/*
 * This file is part of the Code::Blocks IDE and licensed under the GNU General Public License, version 3
 * http://www.gnu.org/licenses/gpl-3.0.html
 *
 * $Revision$
 * $Id$
 * $HeadURL$
 */

#include "sdk.h"
#ifndef CB_PRECOMP
    #include <wx/checkbox.h>
    #include <wx/choice.h>
    #include <wx/filefn.h>
    #include <wx/filename.h>
    #include <wx/textctrl.h>
    #include <wx/timer.h>
    #include <wx/xrc/xmlres.h>
    #include "cbeditor.h"
    #include "cbproject.h"
    #include "cbworkspace.h"
    #include "configmanager.h"
    #include "editormanager.h"
    #include "globals.h"
    #include "pluginmanager.h"
    #include "projectmanager.h"
    #include "manager.h"
    #include "sdk_events.h"
    #include "workspaceloader.h"
#endif

#include <algorithm>
#include "cbstyledtextctrl.h"

#include "projectloader.h"
#include "autosave.h"

// this auto-registers the plugin
namespace
{
    PluginRegistrant<Autosave> reg(_T("Autosave"));
}

BEGIN_EVENT_TABLE(Autosave, cbPlugin)
EVT_TIMER(-1, Autosave::OnTimer)
END_EVENT_TABLE()

Autosave::Autosave()
{
    //ctor
}

Autosave::~Autosave()
{
}

void Autosave::OnAttach()
{
    if(!Manager::LoadResource(_T("autosave.zip")))
    {
        NotifyMissingFile(_T("autosave.zip"));
    }

    timer1 = new wxTimer(this, 10000);
    timer2 = new wxTimer(this, 20000);

    Start();
}

void Autosave::Start()
{
    ConfigManager *cfg = Manager::Get()->GetConfigManager(_T("autosave"));
    if(cfg->ReadBool(_T("do_project")))
        timer1->Start(60 * 1000 * cfg->ReadInt(_T("project_mins")));
    else
        timer1->Stop();

    if(cfg->ReadBool(_T("do_sources")))
        timer2->Start(60 * 1000 * cfg->ReadInt(_T("source_mins")));
    else
        timer2->Stop();
}

void Autosave::OnRelease(cb_unused bool appShutDown)
{
    delete timer1;
    delete timer2;
    timer1 = 0;
    timer2 = 0;
}

void Autosave::SaveProject(cbProject *p, int method)
{
    PluginManager *plm = Manager::Get()->GetPluginManager();
    switch(method)
    {
        case 0:
        {
            if(p->GetModified())
            {
                if(::wxRenameFile(p->GetFilename(), p->GetFilename() + _T(".bak")))
                    if(p->Save())
                    {
                        CodeBlocksEvent e(cbEVT_PROJECT_SAVE, 0, p);
                        plm->NotifyPlugins(e);
                    }
            }
            wxFileName file = p->GetFilename();
            file.SetExt(_T("layout"));
            wxString filename = file.GetFullPath();
            if(::wxRenameFile(filename, filename + _T(".bak")))
                p->SaveLayout();
            break;
        }
        case 1:
        {
            if(p->GetModified() && p->Save())
            {
                CodeBlocksEvent e(cbEVT_PROJECT_SAVE, 0, p);
                plm->NotifyPlugins(e);
            }
            p->SaveLayout();
            break;
        }
        case 2:
        case 3: // doesn't really make sense to keep so many versions of a project file
        {
            if (p->IsLoaded() == false)
                return;
            if(p->GetModified())
            {
                ProjectLoader loader(p);
                if(loader.Save(p->GetFilename() + _T(".save")))
                {
                    CodeBlocksEvent e(cbEVT_PROJECT_SAVE, 0, p);
                    plm->NotifyPlugins(e);
                }
                p->SetModified(); // the actual project file is still not updated!
            }
            wxFileName file = wxFileName(p->GetFilename());
            file.SetExt(_T("layout"));
            wxString filename = file.GetFullPath();
            wxString temp = filename + _T(".temp");
            wxString save = filename + _T(".save");
            if(::wxFileExists(filename) && ::wxCopyFile(filename, temp))
            {
                p->SaveLayout();
                ::wxRenameFile(filename, save);
                ::wxRenameFile(temp, filename);
            }
            break;
        }
        default:
            break;
    }
}

void Autosave::OnTimer(wxTimerEvent& e)
{
    if(e.GetId() == 10000)
    {
        int method = Manager::Get()->GetConfigManager(_T("autosave"))->ReadInt(_T("method"));
        bool allProjects = Manager::Get()->GetConfigManager(_T("autosave"))->ReadBool(_T("all_projects"), true);
        bool doWorkspace = Manager::Get()->GetConfigManager(_T("autosave"))->ReadBool(_T("do_workspace"), true);
        ProjectManager *pm = Manager::Get()->GetProjectManager();
        if(pm)// && pm->GetActiveProject())
        {
            if (allProjects)
            {
                ProjectsArray *projects = pm->GetProjects();
                for (size_t ii = 0; ii < projects->GetCount(); ++ii)
                    SaveProject((*projects)[ii], method);
            }
            else if(cbProject *p = pm->GetActiveProject())
                SaveProject(p, method);

            cbWorkspace *workspace = pm->GetWorkspace();
            if (doWorkspace && workspace && workspace->GetModified())
            {
                switch(method)
                {
                    case 0:
                        if(::wxRenameFile(workspace->GetFilename(), workspace->GetFilename() + _T(".bak")))
                            workspace->Save();
                        break;
                    case 1:
                        workspace->Save();
                        break;
                    case 2:
                    case 3:
                    {
                        WorkspaceLoader loader;
                        loader.Save(workspace->GetTitle(), workspace->GetFilename() + wxT(".save"));
                        workspace->SetModified(true);
                        break;
                    }
                    default:
                        break;
                }
            }
        }
    }
    else if(e.GetId() == 20000)
    {
        int method = Manager::Get()->GetConfigManager(_T("autosave"))->ReadInt(_T("method"));
        EditorManager* em = Manager::Get()->GetEditorManager();

        if(em)
        {
            for(int i = 0; i < em->GetEditorsCount(); ++i)
            {
                cbEditor* ed = em->GetBuiltinEditor(em->GetEditor(i));
                if(ed && ed->GetModified())
                {
                    wxFileName fn(ed->GetFilename());
                    switch(method)
                    {
                        case 0:
                        {
                            if(::wxRenameFile(fn.GetFullPath(), fn.GetFullPath() + _T(".bak")))
                                cbSaveToFile(fn.GetFullPath(), ed->GetControl()->GetText(), ed->GetEncoding(), ed->GetUseBom());
                            break;
                        }
                        case 1:
                        {
                            ed->Save();
                            break;
                        }
                        case 2:
                        {
                            cbSaveToFile(fn.GetFullPath() + _T(".save"), ed->GetControl()->GetText(), ed->GetEncoding(), ed->GetUseBom());
                            ed->SetModified(); // the "real" file has not been saved!
                            break;
                        }
                        case 3:
                        {
                            wxString tmp1;
                            wxString tmp2;

                            for(unsigned int revisions = 8; revisions; --revisions)
                            {
                                tmp1.Printf(_T("%s/%s.%u.%s"), fn.GetPath().c_str(), fn.GetName().c_str(), revisions,   fn.GetExt().c_str());
                                tmp2.Printf(_T("%s/%s.%u.%s"), fn.GetPath().c_str(), fn.GetName().c_str(), revisions+1, fn.GetExt().c_str());

                                if(::wxFileExists(tmp2))
                                    ::wxRemoveFile(tmp2);
                                if(::wxFileExists(tmp1))
                                    ::wxRenameFile(tmp1, tmp2);
                            }

                            tmp1.Printf(_T("%s/%s.1.%s"), fn.GetPath().c_str(), fn.GetName().c_str(), fn.GetExt().c_str());

                            cbSaveToFile(tmp1, ed->GetControl()->GetText(), ed->GetEncoding(), ed->GetUseBom());
                            ed->SetModified(); // the "real" file has not been saved!
                            break;
                        }
                        default:
                            break;
                    }
                }

            }
        }
    }

}

cbConfigurationPanel* Autosave::GetConfigurationPanel(wxWindow* parent)
{
    AutosaveConfigDlg* dlg = new AutosaveConfigDlg(parent, this);
    // deleted by the caller

    return dlg;
}



BEGIN_EVENT_TABLE(AutosaveConfigDlg, cbConfigurationPanel)
    EVT_CHECKBOX(XRCID("do_project"), AutosaveConfigDlg::OnProjectsChecked)
    EVT_CHECKBOX(XRCID("do_sources"), AutosaveConfigDlg::OnSourceChecked)
END_EVENT_TABLE()

AutosaveConfigDlg::AutosaveConfigDlg(wxWindow* parent, Autosave* plug) : plugin(plug)
{
    wxXmlResource::Get()->LoadPanel(this, parent, _T("dlgAutosave"));

    LoadSettings();
}

void AutosaveConfigDlg::LoadSettings()
{
    ConfigManager *cfg = Manager::Get()->GetConfigManager(_T("autosave"));

    bool doProjects = cfg->ReadBool(_T("do_project"));
    bool doSources = cfg->ReadBool(_T("do_sources"));
    XRCCTRL(*this, "do_project", wxCheckBox)->SetValue(doProjects);
    XRCCTRL(*this, "do_sources", wxCheckBox)->SetValue(doSources);
    XRCCTRL(*this, "do_workspace", wxCheckBox)->SetValue(cfg->ReadBool(_T("do_workspace"), true));
    XRCCTRL(*this, "all_projects", wxCheckBox)->SetValue(cfg->ReadBool(_T("all_projects"), true));
    int minsProjects = std::max(cfg->ReadInt(_T("project_mins"), 1), 1);
    int minsSources = std::max(cfg->ReadInt(_T("source_mins"), 1), 1);
    XRCCTRL(*this, "project_mins", wxTextCtrl)->SetValue(wxString::Format(_T("%d"), minsProjects));
    XRCCTRL(*this, "source_mins", wxTextCtrl)->SetValue(wxString::Format(_T("%d"), minsSources));

    XRCCTRL(*this, "do_workspace", wxCheckBox)->Enable(doProjects);
    XRCCTRL(*this, "all_projects", wxCheckBox)->Enable(doProjects);
    XRCCTRL(*this, "project_mins", wxTextCtrl)->Enable(doProjects);

    XRCCTRL(*this, "source_mins", wxTextCtrl)->Enable(doSources);

    XRCCTRL(*this, "method", wxChoice)->SetSelection(cfg->ReadInt(_T("method"), 2));
}

void AutosaveConfigDlg::SaveSettings()
{
    ConfigManager *cfg = Manager::Get()->GetConfigManager(_T("autosave"));

    cfg->Write(_T("do_project"), (bool) XRCCTRL(*this, "do_project", wxCheckBox)->GetValue());
    cfg->Write(_T("do_sources"), (bool) XRCCTRL(*this, "do_sources", wxCheckBox)->GetValue());
    cfg->Write(_T("do_workspace"), (bool) XRCCTRL(*this, "do_workspace", wxCheckBox)->GetValue());
    cfg->Write(_T("all_projects"), (bool) XRCCTRL(*this, "all_projects", wxCheckBox)->GetValue());

    long pm, sm;

    XRCCTRL(*this, "project_mins", wxTextCtrl)->GetValue().ToLong(&pm);
    XRCCTRL(*this, "source_mins", wxTextCtrl)->GetValue().ToLong(&sm);

    if (pm < 1)
        pm = 1;
    if (sm < 1)
        sm = 1;

    cfg->Write(_T("project_mins"), (int) pm);
    cfg->Write(_T("source_mins"), (int) sm);

    cfg->Write(_T("method"), XRCCTRL(*this, "method", wxChoice)->GetSelection());

    plugin->Start();
}

void AutosaveConfigDlg::OnProjectsChecked(wxCommandEvent &event)
{
    XRCCTRL(*this, "project_mins", wxTextCtrl)->Enable(event.IsChecked());
    XRCCTRL(*this, "do_workspace", wxCheckBox)->Enable(event.IsChecked());
    XRCCTRL(*this, "all_projects", wxCheckBox)->Enable(event.IsChecked());
}

void AutosaveConfigDlg::OnSourceChecked(wxCommandEvent &event)
{
    XRCCTRL(*this, "source_mins", wxTextCtrl)->Enable(event.IsChecked());
}

