// This file is a part of the Dynamic Preferences Library.
//
// Copyright (c) 2004 Theodore R. Smith (hopeseekr@xmule.ws / http://www.xmule.ws/)
// DSA-1024 Fingerprint: 10A0 6372 9092 85A2 BB7F 907B CB8B 654B E33B F1ED
//
// This file is dually licensed under the terms of the following licenses:
// * Primary License: OSSAL - Open Source Software Alliance License
//   * See the included License.OSSAL for complete details.
//   * Key points:
//       5.Redistributions of source code in any non-textual form (i.e.
//          binary or object form, etc.) must not be linked to software that is
//          released with a license that requires disclosure of source code
//          (ex: the GPL).
//       6.Redistributions of source code must be licensed under more than one
//          license and must not have the terms of the OSSAL removed.
//
// * Secondary License: Creative Commons Attribution-NoDerivs License v1.0
//   * See the included License.CCANDL for complete details.
//   * Key Points:
//       * You are free:
//           * to copy, distribute, display, and perform the work
//           * to make non-commercial use of the work in its original form
//       * Under the following conditions:
//           * Attribution.You must give the original author credit.
//           * No Derivative Works.You may not alter, transform, or build upon
//             this work.
//
// * Special exceptions:
//   I, Theodore R.Smith, hereby grant, as the sole author of this library,
//   exclusive permission to the xMule Project to distribute and link to this
//   library, specifically voiding clause 5 of the OSSAL for the xMule Project.
//   As a further exclusive permission, when linked to the xMule Project,
//   the terms of the GPL concerning binary distribution must be observed.
//
// For more information on legality, see README.txt.

#include "DynPrefsCtrl.h"                   // Needed for Module's Prototype(s)
#include "DynPrefs.h"                       // Needed for DynamicPreferences

#include <iostream>                         // Needed for std::cout std::endl
#include <list>                             // Needed for std::list
#include <map>                              // Needed for std::map
#include <utility>                          // Needed for std::pair
#include <vector>                           // Needed for std::vector

#include <wx/log.h>                         // Needed for wxLogDebug

#if wxCHECK_VERSION(2, 5, 1)
    #include <wx/xml/xml.h>                 // Needed for wxXmlNode
#else
    #include <wx/xrc/xml.h>                 // Needed for wxXmlNode
#endif

#include <wx/button.h>                      // Needed for wxButton
#include <wx/checkbox.h>                    // Needed for wxCheckBox
#include <wx/combobox.h>                    // Needed for wxComboBox
#include <wx/dirdlg.h>                      // Needed for wxDirDialog
#include <wx/filesys.h>                     // Needed for wxFileSystem
#include <wx/intl.h>                        // Needed for _
#include <wx/msgdlg.h>                      // Needed for wxMessageBox
#include <wx/sizer.h>                       // Needed for wxBoxSizer
#include <wx/slider.h>                      // Needed for wxSlider
#include <wx/statline.h>                    // Needed for wxStaticLine
#include <wx/stattext.h>                    // Needed for wxStaticText
#include <wx/textctrl.h>                    // Needed for EVT_TEXT
#include <wx/valtext.h>                     // Needed for wxFILTER_NUMERIC

using std::cout;
using std::endl;
using std::list;

static std::list< std::pair<const wxString, const short int > > controls;

const wxEventType wxEVT_PREFS_CHANGED = wxNewEventType();
short int E_BROWSE1 = 6660;
short int E_BROWSE2 = 7770;

// Begin private functions
namespace
{
    void RegisterControl(const wxString& preference, const short int type);
    inline wxString ControlName(const wxString& in);
    wxString GetNodeText(wxXmlNode *node);
    wxSize GetNodeSize(wxXmlNode* node);
    void parse_xml(wxTreeMultiCtrl* parent, wxXmlNode* node, wxTreeMultiItem& current_item);
};

wxWindow* add_item(wxTreeMultiCtrl* parent, wxXmlNode* node, wxPanel* panel=NULL);
// End private functions

BEGIN_EVENT_TABLE(DynamicPreferencesCtrl, wxTreeMultiCtrl)
    EVT_PREFS_CHANGED(DynamicPreferencesCtrl::OnChangedPrefs)
    EVT_CHECKBOX(-1, DynamicPreferencesCtrl::OnCheckBox)
    EVT_TEXT(-1, DynamicPreferencesCtrl::OnTextChange)
    EVT_ROOT_EXPANDED(-1, DynamicPreferencesCtrl::OnExpandNode)
    EVT_COMBOBOX(-1, DynamicPreferencesCtrl::OnCombo)
END_EVENT_TABLE()
  
class SmartSlider: public wxSlider
{
public:
    SmartSlider(wxWindow* parent, wxWindowID id, int value, int minValue, int maxValue, 
        const wxPoint& point=wxDefaultPosition, const wxSize& size=wxDefaultSize, 
        long style=wxSL_HORIZONTAL, const wxValidator& validator=wxDefaultValidator,
        const wxString& name=wxT("slider")):
            wxSlider(parent, id, value, minValue, maxValue, point, size, style, validator, name)
    {
    }

    virtual ~SmartSlider()
    {
    }

    wxString m_string;
};

void DynamicPreferencesCtrl::OnExpandNode(const wxTreeMultiEvent& event)
{
    wxTreeMultiItem current_item(event.GetItem());

    if (GetChildrenCount(current_item) > 0)
    {
        return;
    }
    
    wxXmlNode* node = doc->GetRoot();
    node = node->GetChildren();

    do
    {
        if (node->GetName() == wxT("section"))
        {
            if (node->GetProperties()->GetValue() == event.GetLabel())
            {
                parse_xml(this, node->GetChildren(), current_item);
                Expand(current_item, true);
            }
        }

    }
    while ((node = node->GetNext()) != NULL);

    OnChangedPrefs(event);
}

void DynamicPreferencesCtrl::OnCheckBox(wxCommandEvent& event)
{
    dynprefs->Add(ControlName(this->FindWindowById(event.GetId())->GetName()), event.IsChecked());
}

void DynamicPreferencesCtrl::OnTextChange(wxCommandEvent& event)
{
    wxChar first_char = this->FindWindowById(event.GetId())->GetName().GetChar(0);

    if (first_char == wxT('l'))
    {
        long number;
        event.GetString().ToLong(&number);
        dynprefs->Add(ControlName(this->FindWindowById(event.GetId())->GetName()), number);
    }
    else if (first_char == wxT('f'))
    {
        double number;
        event.GetString().ToDouble(&number);
        dynprefs->Add(ControlName(this->FindWindowById(event.GetId())->GetName()), number);
    }
    else
    {
        dynprefs->Add(ControlName(this->FindWindowById(event.GetId())->GetName()), event.GetString());
    }
}

DynamicPreferencesCtrl::DynamicPreferencesCtrl(const wxString& filename, wxWindow* parent, DynamicPreferences* dp, wxWindowID id, const wxPoint& pos, const wxSize& size):
    wxTreeMultiCtrl(parent, id, pos, size)
{
    if (dp == NULL)
    {
        dynprefs = new DynamicPreferences();
    }
    else
    {
        dynprefs = dp;
    }

    this->SetSpacingY(0);
    wxFileSystem fsys;
    wxFSFile* f = fsys.OpenFile(filename);
    
    doc = new wxXmlDocument(*f->GetStream());
    wxXmlNode* node;
    node = doc->GetRoot();

    if ((node->GetProperties()->GetName() == wxT("version")) && (node->GetProperties()->GetValue() != wxT("2.0")))
    {
        wxMessageBox(wxT("This version of dynamic preferences is not supported."), wxT("Warning"), wxICON_WARNING);
    }

    node = node->GetChildren();

    /* Get Sections */
    wxTreeMultiItem current_item;

    do
    {
        if (node->GetName() == wxT("section"))
        {
            wxString section_name(node->GetProperties()->GetValue());
            
            current_item = AddRoot(section_name);
//            if (a == 0)
//            {
//                parse_xml(this, node->GetChildren(), current_item);
//                ++a;
//            }
//            else
//            {

/* This method should *really* be done better. */
            if (section_name == wxT("Advanced Settings"))
            {
                parse_xml(this, node->GetChildren(), current_item);
            }

            Collapse(current_item, false);
//            }
        }

    }
    while ((node = node->GetNext()) != NULL);

    delete node;    
}

namespace
{
wxString GetNodeText(wxXmlNode* node)
{
    wxXmlNode* tmp = node;

    if (tmp == NULL)
    {
        return wxEmptyString;
    }
    tmp = tmp->GetChildren();

    do
    {
        if (tmp->GetType() == wxXML_CDATA_SECTION_NODE || tmp->GetType() == wxXML_TEXT_NODE)
        {
            return tmp->GetContent();
        }
    } while ((tmp = tmp->GetNext()) != NULL);

    return wxEmptyString;
}

void parse_xml(wxTreeMultiCtrl* parent, wxXmlNode* node, wxTreeMultiItem& current_item)
{
    do
    {
        if (node->GetName() == wxT("item"))
        {
            parent->AppendWindow(current_item, add_item(parent, node));
        }
        else if (node->GetName() == wxT("multi"))
        {
            int orient = 0;
            wxPanel* panel = new wxPanel(parent, -1);
            wxBoxSizer* sizer;
            wxBoxSizer* sizer_v1 = new wxBoxSizer(wxVERTICAL);
            wxBoxSizer* sizer_v2 = new wxBoxSizer(wxVERTICAL);
            wxBoxSizer* sizer_v3 = new wxBoxSizer(wxHORIZONTAL);

            wxXmlNode* parent_node2 = node;
            node = node->GetChildren();

            if (node != NULL && node->GetName() == wxT("orient"))
            {
                if (GetNodeText(node) == wxT("1"))
                {
                    orient = 1;
                    sizer = new wxBoxSizer(wxVERTICAL);
                }
                else
                {
                    sizer = new wxBoxSizer(wxHORIZONTAL);
                }

                node = node->GetNext();
            }
            else
            {
                sizer = new wxBoxSizer(wxHORIZONTAL);
            }

            if (node != NULL && node->GetName() == wxT("item"))
            {
                
                sizer_v1->Add(add_item(parent, node, panel), 0, wxALIGN_BOTTOM | wxLEFT, 5);
            }

            while (node = node->GetNext())
            {
                if (node->GetName() == wxT("item"))
                {
                    if (GetNodeSize(node) == wxDefaultSize)
                    {
                        sizer_v3->Add(add_item(parent, node, panel), 1, wxGROW | wxALIGN_RIGHT | wxLEFT, 5);
                    }
                    else
                    {
                        sizer_v3->Add(add_item(parent, node, panel), 0, wxALIGN_RIGHT | wxLEFT, 5);
                    }
                }
            }

            if (orient == 1)
            {
                sizer->Add(sizer_v1, 0);
                sizer->Add(sizer_v3, 1, wxGROW | wxALIGN_RIGHT);
            }
            else
            {
                sizer_v2->Add(sizer_v3, 1, wxALIGN_RIGHT);
                sizer->Add(sizer_v1, 1);
                sizer->Add(sizer_v2, 1, wxGROW | wxALIGN_RIGHT);
            }

            node = parent_node2;
            panel->SetSizer(sizer);
            panel->SetAutoLayout(true);
            parent->AppendWindow(current_item, panel, 340);
        }
        else if (node->GetName() == wxT("group"))
        {
            current_item = parent->AppendNode(current_item, node->GetPropVal(wxT("name"), wxT("")));
            parse_xml(parent, node->GetChildren(), current_item);
            current_item = parent->GetItemParent(current_item);
        }
    } while (node = node->GetNext());
}

wxSize GetNodeSize(wxXmlNode* node)
{
    long width, height;
    wxString size = node->GetPropVal(wxT("size"), wxEmptyString);

    if (size.IsEmpty() == true)
    {
        return wxSize(-1, -1);
    }

    if ((size.BeforeFirst(wxT(',')).ToLong(&width) == false) || (size.AfterLast(wxT(',')).ToLong(&height) == false))
    {
        wxLogError(wxT("Invalid size coordinates:") + size);
        return wxSize(-1, -1);
    }

    return wxSize(width, height);
}

void RegisterControl(const wxString& preference, const short int type)
{
    controls.push_front(std::make_pair(preference, type));
}

inline wxString ControlName(const wxString& in)
{
    wxString name(in);
    name.Remove(0, 1);
    return name;
}
};

wxWindow* add_item(wxTreeMultiCtrl* parent, wxXmlNode* node, wxPanel* panel)
{
    wxString type(node->GetPropVal(wxT("type"), wxT("")));

    if (panel == NULL)
    {
        panel = parent;
    }

    wxString name(node->GetPropVal(wxT("name"), wxT("")));

    if (type == wxT("label"))
    {
        return new wxStaticText(panel, -1, GetNodeText(node), wxDefaultPosition, GetNodeSize(node));
    }
    else if (type == wxT("text"))
    {        
        RegisterControl(name, DynamicPreferences::T_TEXT);
        return new wxTextCtrl(panel, -1, wxEmptyString, wxDefaultPosition, GetNodeSize(node), 0, wxDefaultValidator, wxT("s") + name);
    }
    else if (type == wxT("select"))
    {
        wxXmlNode* tmp_node = node->GetChildren();
        wxComboBox* combobox = new wxComboBox(panel, -1, wxEmptyString, wxDefaultPosition, GetNodeSize(node), 0, NULL, wxCB_READONLY);
        combobox->SetName(wxT("c") + name);

        while (tmp_node != NULL)
        {
            if (tmp_node->GetName() == wxT("option"))
            {
                combobox->Append(GetNodeText(tmp_node));
            }

            tmp_node = tmp_node->GetNext();
        }

        combobox->SetAutoLayout(true);
        RegisterControl(name, DynamicPreferences::T_SELECT);
        return combobox;
    }
    else if (type == wxT("bool"))
    {
        long value;
        node->GetPropVal(wxT("value"), wxT("0")).ToLong(&value);
        RegisterControl(name, DynamicPreferences::T_CHECK);

        wxCheckBox* checkbox = new wxCheckBox(panel, -1, GetNodeText(node), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, wxT("b") + name);

        if (value == 1)
        {
            cout << "Setting value of " << name << " to true." << endl;
            checkbox->SetValue(true);
            static_cast<DynamicPreferencesCtrl *>(parent)->dynprefs->Add(name, true);
        }

        return checkbox;
    }
    else if (type == wxT("float"))
    {
        RegisterControl(name, DynamicPreferences::T_DTEXT);
        return new wxTextCtrl(panel, -1, "", wxDefaultPosition, GetNodeSize(node), 0, wxTextValidator(wxFILTER_NUMERIC, new wxString), wxT("f") + name);
    }
    else if (type == wxT("long"))
    {
        RegisterControl(name, DynamicPreferences::T_LTEXT);
        return new wxTextCtrl(panel, -1, "", wxDefaultPosition, GetNodeSize(node), 0, wxTextValidator(wxFILTER_NUMERIC, new wxString), wxT("l") + name);
    }
    else if (type == wxT("separator"))
    {
        return new wxStaticLine(panel, -1, wxDefaultPosition, wxSize(400, -1));
    }
    else if (type == wxT("browse1"))
    {
        RegisterControl(name, DynamicPreferences::T_DIR);
        wxPanel* panel2 = new wxPanel(panel, -1, wxDefaultPosition, wxSize(450, 20));
        wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);

        sizer->Add(new wxTextCtrl(panel2, -1, wxEmptyString, wxDefaultPosition, GetNodeSize(node), 0, wxDefaultValidator, wxT("d") + name), 1, wxEXPAND | wxALIGN_RIGHT | wxRIGHT, 5);
        sizer->Add(new wxButton(panel2, E_BROWSE1, _("Browse"), wxDefaultPosition, wxSize(45, 20), 0, wxDefaultValidator, wxT("b") + name), 0, wxALIGN_RIGHT);
        parent->Connect(E_BROWSE1, wxEVT_COMMAND_BUTTON_CLICKED, (wxObjectEventFunction) &DynamicPreferencesCtrl::OnBrowse1);
        ++E_BROWSE1;
        panel2->SetSizer(sizer);
        return panel2;
    }
    else if (type == wxT("slider"))
    {
        RegisterControl(name, DynamicPreferences::T_SLIDER);
        wxPanel* panel2 = new wxPanel(panel, -1, wxDefaultPosition, wxSize(450, 30));
        wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);

        long min, max;
        node->GetPropVal(wxT("min"), wxT("0")).ToLong(&min);
        node->GetPropVal(wxT("max"), wxT("0")).ToLong(&max);
#warning The "z" needs a rename.
        SmartSlider* slider = new SmartSlider(panel2, E_BROWSE1, 0, min, max, wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL, wxDefaultValidator, wxT("z") + name);
        slider->m_string = GetNodeText(node);

        sizer->Add(new wxStaticText(panel2, -1, wxString::Format(slider->m_string, 0), wxDefaultPosition, GetNodeSize(node), wxADJUST_MINSIZE, wxT("z") + name + wxT("_2")), 0);
        sizer->Add(slider, 1, wxEXPAND | wxALIGN_RIGHT);
        parent->Connect(E_BROWSE1, wxEVT_SCROLL_THUMBTRACK, (wxObjectEventFunction)(wxScrollEventFunction) &DynamicPreferencesCtrl::OnSlider);
        ++E_BROWSE1;
        panel2->SetSizer(sizer);
        return panel2;
    }
    else if (type == wxT("colorlist"))
    {
        RegisterControl(name, DynamicPreferences::T_LLONG);
        wxPanel* panel2 = new wxPanel(panel, -1, wxDefaultPosition, wxSize(450, 20));
        wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);

        wxComboBox* combobox = new wxComboBox(panel2, E_BROWSE1, wxEmptyString, wxDefaultPosition, GetNodeSize(node), 0, NULL, wxCB_READONLY, wxDefaultValidator, wxT("1") + name);
        sizer->Add(combobox, 1, wxEXPAND);
        sizer->Add(new wxButton(panel2, E_BROWSE2, wxT("Color"), wxDefaultPosition, wxSize(-1, 20), 0, wxDefaultValidator, wxT("2") + name), 0);
        
        parent->Connect(E_BROWSE1, wxEVT_COMMAND_COMBOBOX_SELECTED, (wxObjectEventFunction) &DynamicPreferencesCtrl::OnCombo);
        ++E_BROWSE1;
        ++E_BROWSE2;

        wxXmlNode* tmp_node = node->GetChildren();

        while (tmp_node != NULL)
        {
            if (tmp_node->GetName() == wxT("option"))
            {
                combobox->Append(GetNodeText(tmp_node));
            }
            tmp_node = tmp_node->GetNext();
        }

        delete tmp_node;

        panel2->SetSizer(sizer);
#warning This entire colorlist thing needs to be re-implemented :-/
        return panel2;
    }
    else
    {
        return new wxWindow(panel, -1);
    }
}


void DynamicPreferencesCtrl::OnChangedPrefs(const wxEvent& event)
{
    list< std::pair<const wxString, const short int > >::iterator it;
    
    for (it = controls.begin(); it != controls.end(); ++it)
    {
        wxString name((*it).first);
        const short int id((*it).second);

        if (id == DynamicPreferences::T_CHECK)
        {
            dynamic_cast<wxCheckBox*>(FindWindow(wxT("b") + name))->SetValue(dynprefs->Get<bool>(name));
        }
        else if (id == DynamicPreferences::T_TEXT)
        {
            dynamic_cast<wxTextCtrl*>(FindWindow(wxT("s") + name))->SetValue(dynprefs->Get<wxString>(name));
        }
        else if (id == DynamicPreferences::T_DTEXT)
        {
            dynamic_cast<wxTextCtrl*>(FindWindow(wxT("f") + name))->SetValue(wxString::Format("%f", dynprefs->Get<double>(name)));
        }
        else if (id == DynamicPreferences::T_LTEXT)
        {
            dynamic_cast<wxTextCtrl*>(FindWindow(wxT("l") + name))->SetValue(wxString::Format("%ld", dynprefs->Get<long>(name)));
        }
        else if (id == DynamicPreferences::T_SELECT)
        {
            dynamic_cast<wxComboBox*>(FindWindow(wxT("c") + name))->SetSelection(dynprefs->Get<long>(name));
        }
        else if (id == DynamicPreferences::T_DIR)
        {
            dynamic_cast<wxTextCtrl*>(FindWindow(wxT("d") + name))->SetValue(dynprefs->Get<wxString>(name));
        }
        else if (id == DynamicPreferences::T_SLIDER)
        {
            SmartSlider* tmp = (SmartSlider *)FindWindow(wxT("z") + name);
            dynamic_cast<wxStaticText *>(FindWindow(wxT("z") + name + wxT("_2")))->SetLabel(wxString::Format(tmp->m_string, dynprefs->Get<long>(name)));
            tmp->SetValue(dynprefs->Get<long>(name));
        }
    }
}

void DynamicPreferencesCtrl::OnBrowse1(wxCommandEvent& event)
{
    cout << "2. Name: " << this->FindWindowById(event.GetId())->GetName() << endl;
    wxString name(this->FindWindowById(event.GetId())->GetName());

    wxDirDialog dirbox(this, "Choose a Folder that you wan't to share.", "~");

    if (dirbox.ShowModal() == wxID_OK)
    {
        dynamic_cast<wxTextCtrl*>(FindWindow(wxT("d") + ControlName(name)))->SetValue(dirbox.GetPath() + wxT("/"));
    }    
}

void DynamicPreferencesCtrl::OnCombo(wxCommandEvent& event)
{
    static int old_id, old_value;
    int id, value;

    id = event.GetId();
    value = event.GetInt();
    
    if (id != old_id || value != old_value)
    {
        old_id = id;
        old_value = value;
        dynprefs->Add(ControlName(this->FindWindowById(event.GetId())->GetName()), static_cast<long>(value));
    }
}

void DynamicPreferencesCtrl::OnSlider(wxScrollEvent& event)
{
    long pos(static_cast<long>(event.GetPosition()));
    wxString name(this->FindWindowById(event.GetId())->GetName());
    SmartSlider* tmp = dynamic_cast<SmartSlider *>(event.GetEventObject());

    if (pos > 0)
    {
        dynamic_cast<wxStaticText *>(FindWindow(name + wxT("_2")))->SetLabel(wxString::Format(tmp->m_string, pos));
    }
    else
    {
        wxString sTemp;
        sTemp = tmp->m_string.Left(tmp->m_string.Find(wxT(":")) + 2) + _("disabled");
        dynamic_cast<wxStaticText *>(FindWindow(name + wxT("_2")))->SetLabel(sTemp);
    }

    dynprefs->Add(ControlName(name), pos);
}

