// Copyright (c) 2005, Rodrigo Braz Monteiro
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Aegisub Group nor the names of its contributors
//     may be used to endorse or promote products derived from this software
//     without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Aegisub Project http://www.aegisub.org/
//
// $Id$

/// @file dialog_style_manager.cpp
/// @brief Style Manager dialogue box and partial logic
/// @ingroup style_editor
///


////////////
// Includes
#include "config.h"

#include <wx/filename.h>
#include <wx/filedlg.h>
#include <wx/choicdlg.h>
#include <wx/intl.h>
#include <wx/clipbrd.h>
#include <wx/tokenzr.h>
#include "dialog_style_manager.h"
#include "dialog_style_editor.h"
#include "ass_style.h"
#include "ass_file.h"
#include "ass_dialogue.h"
#include "standard_paths.h"
#include "options.h"
#include "subs_grid.h"
#include "utils.h"
#include "help_button.h"
#include "libresrc/libresrc.h"




/// @brief Constructor 
/// @param parent 
/// @param _grid  
///
DialogStyleManager::DialogStyleManager (wxWindow *parent,SubtitlesGrid *_grid)
: wxDialog (parent,-1,_("Styles Manager"),wxDefaultPosition,wxDefaultSize,wxDEFAULT_DIALOG_STYLE,_T("DialogStylesManager"))
{
	// Set icon
	SetIcon(BitmapToIcon(GETIMAGE(style_toolbutton_24)));

	// Vars
	grid = _grid;

	// Catalog
	wxSizer *CatalogBox = new wxStaticBoxSizer(wxHORIZONTAL,this,_("Catalog of available storages"));
	CatalogList = new wxComboBox(this,LIST_CATALOG, _T(""), wxDefaultPosition, wxSize(-1,-1), 0, NULL, wxCB_READONLY | wxCB_READONLY, wxDefaultValidator, _T("Catalog List"));
	wxButton *CatalogNew = new wxButton(this, BUTTON_CATALOG_NEW, _("New"), wxDefaultPosition, wxSize(-1,-1));
	wxButton *CatalogDelete = new wxButton(this, BUTTON_CATALOG_DELETE, _("Delete"), wxDefaultPosition, wxSize(-1,-1));
	CatalogBox->Add(CatalogList,1,wxEXPAND | wxRIGHT | wxALIGN_RIGHT,5);
	CatalogBox->Add(CatalogNew,0,wxRIGHT,5);
	CatalogBox->Add(CatalogDelete,0,0,0);

	// Storage styles list
	StorageList = new wxListBox(this, LIST_STORAGE, wxDefaultPosition, wxSize(240,250), 0, NULL, wxLB_EXTENDED);
	wxSizer *StorageBox = new wxStaticBoxSizer(wxVERTICAL, this, _("Storage"));
	wxSizer *StorageButtons = new wxBoxSizer(wxHORIZONTAL);
	wxSizer *StorageButtonsLow = new wxBoxSizer(wxVERTICAL);
	wxSizer *StorageListSizer = new wxBoxSizer(wxHORIZONTAL);
	MoveToLocal = new wxButton(this, BUTTON_STORAGE_COPYTO, _("Copy to current script ->"), wxDefaultPosition, wxSize(205,-1));
	StorageNew = new wxButton(this, BUTTON_STORAGE_NEW, _("New"), wxDefaultPosition, wxSize(40,-1));
	StorageEdit = new wxButton(this, BUTTON_STORAGE_EDIT, _("Edit"), wxDefaultPosition, wxSize(40,-1));
	StorageCopy = new wxButton(this, BUTTON_STORAGE_COPY, _("Copy"), wxDefaultPosition, wxSize(40,-1));
	StorageDelete = new wxButton(this, BUTTON_STORAGE_DELETE, _("Delete"), wxDefaultPosition, wxSize(40,-1));
	StorageButtons->Add(StorageNew,1,wxEXPAND | wxRIGHT,5);
	StorageButtons->Add(StorageEdit,1,wxEXPAND | wxRIGHT,5);
	StorageButtons->Add(StorageCopy,1,wxEXPAND | wxRIGHT,5);
	StorageButtons->Add(StorageDelete,1,wxEXPAND | wxALL,0);
	StorageMoveUp = new wxBitmapButton(this, BUTTON_STORAGE_UP, GETIMAGE(arrow_up_24));
	StorageMoveDown = new wxBitmapButton(this, BUTTON_STORAGE_DOWN, GETIMAGE(arrow_down_24));
	StorageMoveTop = new wxBitmapButton(this, BUTTON_STORAGE_TOP, GETIMAGE(arrow_up_stop_24));
	StorageMoveBottom = new wxBitmapButton(this, BUTTON_STORAGE_BOTTOM, GETIMAGE(arrow_down_stop_24));
	StorageSort = new wxBitmapButton(this, BUTTON_STORAGE_SORT, GETIMAGE(arrow_sort_24));
	StorageMoveUp->SetToolTip(_("Move style up."));
	StorageMoveDown->SetToolTip(_("Move style down."));
	StorageMoveTop->SetToolTip(_("Move style to top."));
	StorageMoveBottom->SetToolTip(_("Move style to bottom."));
	StorageSort->SetToolTip(_("Sort styles alphabetically."));
	StorageButtonsLow->AddStretchSpacer(1);
	StorageButtonsLow->Add(StorageMoveTop,0,wxEXPAND | wxALL,0);
	StorageButtonsLow->Add(StorageMoveUp,0,wxEXPAND | wxALL,0);
	StorageButtonsLow->Add(StorageMoveDown,0,wxEXPAND | wxALL,0);
	StorageButtonsLow->Add(StorageMoveBottom,0,wxEXPAND | wxALL,0);
	StorageButtonsLow->Add(StorageSort,0,wxEXPAND | wxALL,0);
	StorageButtonsLow->AddStretchSpacer(1);
	StorageListSizer->Add(StorageList,1,wxEXPAND | wxRIGHT,0);
	StorageListSizer->Add(StorageButtonsLow,0,wxEXPAND | wxALL,0);
	StorageBox->Add(StorageListSizer,1,wxEXPAND | wxBOTTOM,5);
	StorageBox->Add(MoveToLocal,0,wxEXPAND | wxBOTTOM,5);
	StorageBox->Add(StorageButtons,0,wxEXPAND | wxBOTTOM,0);
	MoveToLocal->Disable();
	StorageEdit->Disable();
	StorageCopy->Disable();
	StorageDelete->Disable();

	// Local styles list
	CurrentList = new wxListBox(this, LIST_CURRENT, wxDefaultPosition, wxSize(240,250), 0, NULL, wxLB_EXTENDED);
	wxSizer *CurrentBox = new wxStaticBoxSizer(wxVERTICAL, this, _("Current script"));
	wxSizer *CurrentButtons = new wxBoxSizer(wxHORIZONTAL);
	wxSizer *CurrentButtonsLow = new wxBoxSizer(wxVERTICAL);
	wxSizer *CurrentListSizer = new wxBoxSizer(wxHORIZONTAL);
	wxSizer *MoveImportSizer = new wxBoxSizer(wxHORIZONTAL);
	MoveToStorage = new wxButton(this, BUTTON_CURRENT_COPYTO, _("<- Copy to storage"), wxDefaultPosition, wxSize(-1,25));
	MoveImportSizer->Add(MoveToStorage,1,wxEXPAND | wxRIGHT,5);
	MoveImportSizer->Add(new wxButton(this, BUTTON_CURRENT_IMPORT, _("Import from script...")),1,wxEXPAND,0);
	CurrentNew = new wxButton(this, BUTTON_CURRENT_NEW, _("New"), wxDefaultPosition, wxSize(40,-1));
	CurrentEdit = new wxButton(this, BUTTON_CURRENT_EDIT, _("Edit"), wxDefaultPosition, wxSize(40,-1));
	CurrentCopy = new wxButton(this, BUTTON_CURRENT_COPY, _("Copy"), wxDefaultPosition, wxSize(40,-1));
	CurrentDelete = new wxButton(this, BUTTON_CURRENT_DELETE, _("Delete"), wxDefaultPosition, wxSize(40,-1));
	CurrentButtons->Add(CurrentNew,1,wxEXPAND | wxRIGHT,5);
	CurrentButtons->Add(CurrentEdit,1,wxEXPAND | wxRIGHT,5);
	CurrentButtons->Add(CurrentCopy,1,wxEXPAND | wxRIGHT,5);
	CurrentButtons->Add(CurrentDelete,1,wxEXPAND | wxALL,0);
	CurrentMoveUp = new wxBitmapButton(this, BUTTON_CURRENT_UP, GETIMAGE(arrow_up_24));
	CurrentMoveDown = new wxBitmapButton(this, BUTTON_CURRENT_DOWN, GETIMAGE(arrow_down_24));
	CurrentMoveTop = new wxBitmapButton(this, BUTTON_CURRENT_TOP, GETIMAGE(arrow_up_stop_24));
	CurrentMoveBottom = new wxBitmapButton(this, BUTTON_CURRENT_BOTTOM, GETIMAGE(arrow_down_stop_24));
	CurrentSort = new wxBitmapButton(this, BUTTON_CURRENT_SORT, GETIMAGE(arrow_sort_24));
	CurrentMoveUp->SetToolTip(_("Move style up."));
	CurrentMoveDown->SetToolTip(_("Move style down."));
	CurrentMoveTop->SetToolTip(_("Move style to top."));
	CurrentMoveBottom->SetToolTip(_("Move style to bottom."));
	CurrentSort->SetToolTip(_("Sort styles alphabetically."));
	CurrentButtonsLow->AddStretchSpacer(1);
	CurrentButtonsLow->Add(CurrentMoveTop,0,wxEXPAND | wxALL,0);
	CurrentButtonsLow->Add(CurrentMoveUp,0,wxEXPAND | wxALL,0);
	CurrentButtonsLow->Add(CurrentMoveDown,0,wxEXPAND | wxALL,0);
	CurrentButtonsLow->Add(CurrentMoveBottom,0,wxEXPAND | wxALL,0);
	CurrentButtonsLow->Add(CurrentSort,0,wxEXPAND | wxALL,0);
	CurrentButtonsLow->AddStretchSpacer(1);
	CurrentListSizer->Add(CurrentList,1,wxEXPAND | wxRIGHT,0);
	CurrentListSizer->Add(CurrentButtonsLow,0,wxEXPAND | wxALL,0);
	CurrentBox->Add(CurrentListSizer,1,wxEXPAND | wxBOTTOM,5);
	CurrentBox->Add(MoveImportSizer,0,wxEXPAND | wxBOTTOM,5);
	CurrentBox->Add(CurrentButtons,0,wxEXPAND | wxBOTTOM,0);
	MoveToStorage->Disable();
	CurrentEdit->Disable();
	CurrentCopy->Disable();
	CurrentDelete->Disable();

	// Buttons
	wxStdDialogButtonSizer *buttonSizer = new wxStdDialogButtonSizer();
	buttonSizer->SetCancelButton(new wxButton(this, wxID_CLOSE));
	buttonSizer->AddButton(new HelpButton(this,_T("Styles Manager")));
	buttonSizer->Realize();

	// General layout
	wxSizer *StylesSizer = new wxBoxSizer(wxHORIZONTAL);
	StylesSizer->Add(StorageBox,0,wxRIGHT | wxEXPAND,5);
	StylesSizer->Add(CurrentBox,0,wxLEFT | wxEXPAND,0);
	MainSizer = new wxBoxSizer(wxVERTICAL);
	MainSizer->Add(CatalogBox,0,wxEXPAND | wxLEFT | wxRIGHT | wxTOP,5);
	MainSizer->Add(StylesSizer,1,wxEXPAND | wxALL,5);
	MainSizer->Add(buttonSizer,0,wxBOTTOM | wxEXPAND,5);

	// Set sizer
	SetSizer(MainSizer);
	MainSizer->SetSizeHints(this);

	// Position window
	if (lastx == -1 && lasty == -1) {
		CenterOnParent();
	} else {
		Move(lastx, lasty);
	}

	// Populate lists
	LoadCatalog();
	LoadCurrentStyles(AssFile::top);

	//Set key handlers for lists
	CatalogList->PushEventHandler(new DialogStyleManagerEvent(this));
	StorageList->PushEventHandler(new DialogStyleManagerEvent(this));
	CurrentList->PushEventHandler(new DialogStyleManagerEvent(this));

	// Select default item
	wxString selected_style;
	if (_grid) {
		AssDialogue *dia = _grid->GetDialogue(_grid->GetFirstSelRow());
		if(dia)	selected_style = dia->Style;
	}

	if (StorageList->GetCount() && StorageList->SetStringSelection(selected_style)) {
		StorageEdit->Enable();
		StorageCopy->Enable();
		StorageDelete->Enable();
		MoveToLocal->Enable();
	}
	if (CurrentList->GetCount() && CurrentList->SetStringSelection(selected_style)) {
		CurrentEdit->Enable();
		CurrentCopy->Enable();
		CurrentDelete->Enable();
		MoveToStorage->Enable();
	}
	UpdateMoveButtons();
}



/// @brief Destructor 
///
DialogStyleManager::~DialogStyleManager() {
	int sel = CatalogList->GetSelection();
	if (sel != wxNOT_FOUND) {
		AssFile::top->SetScriptInfo(_T("Last Style Storage"),CatalogList->GetString(sel));
	}
	CatalogList->PopEventHandler(true);
	StorageList->PopEventHandler(true);
	CurrentList->PopEventHandler(true);
	Store.Clear();
}



/// @brief Loads the catalog 
///
void DialogStyleManager::LoadCatalog () {
	// Clear
	CatalogList->Clear();

	// Create catalog if it doesn't exist
	wxString dirname = StandardPaths::DecodePath(_T("?user/catalog/"));
	if (!wxDirExists(dirname)) {
		if (!wxMkdir(dirname)) {
			throw _T("Failed creating directory for style catalogues");
		}
		else {
			// Create default style
			Store.Clear();
			AssStyle *defstyle = new AssStyle;
			Store.style.push_back(defstyle);
			Store.Save(_T("Default"));
		}
	}

	// Get dir
	dirname = StandardPaths::DecodePath(_T("?user/catalog/*.sty"));

	// Populate
	wxString curfile = wxFindFirstFile(dirname,wxFILE);
	wxString path,name,ext;
	while (!curfile.empty()) {
		wxFileName::SplitPath(curfile,&path,&name,&ext);
		CatalogList->Append(name);
		curfile = wxFindNextFile();
	}

	// Set to default if available
	StorageActions(false);
	wxString pickStyle = AssFile::top->GetScriptInfo(_T("Last Style Storage"));
	if (pickStyle.IsEmpty()) pickStyle = _T("Default");
	int opt = CatalogList->FindString(pickStyle, false);
	if (opt != wxNOT_FOUND) {
		CatalogList->SetSelection(opt);
		wxCommandEvent dummy;
		OnChangeCatalog(dummy);
	}
}



/// @brief Loads style list 
/// @param subs 
///
void DialogStyleManager::LoadCurrentStyles (AssFile *subs) {
	using std::list;
	AssStyle *style;

	// Reset
	CurrentList->Clear();
	styleMap.clear();

	// Add styles
	for (list<AssEntry*>::iterator cur=subs->Line.begin();cur!=subs->Line.end();cur++) {
		style = AssEntry::GetAsStyle(*cur);
		if (style) {
			if (style->Valid) {
				CurrentList->Append(style->name);
				styleMap.push_back(style);
			}
		}
	}
	UpdateMoveButtons();
}


/// @brief DOCME
///
void DialogStyleManager::LoadStorageStyles () {
	using std::list;
	AssStyle *style;

	// Reset
	StorageList->Clear();
	styleStorageMap.clear();

	// Add styles
	for (list<AssStyle*>::iterator cur=Store.style.begin();cur!=Store.style.end();cur++) {
		style = *cur;
		if (style) {
			if (style->Valid) {
				StorageList->Append(style->name);
				styleStorageMap.push_back(style);
			}
		}
	}

	// Flag change
	wxCommandEvent dummy;
	OnStorageChange(dummy);
	UpdateMoveButtons();
}



/// @brief Enables or disables storage actions 
/// @param state 
///
void DialogStyleManager::StorageActions (bool state) {
	StorageList->Enable(state);
	MoveToLocal->Enable(state);
	StorageNew->Enable(state);
	StorageCopy->Enable(state);
	StorageDelete->Enable(state);

	wxCommandEvent dummy;
	OnStorageChange(dummy);
	
	wxArrayInt selections;
	if (CurrentList->GetSelections(selections) != 0) MoveToStorage->Enable(state);
	UpdateMoveButtons();
}


///////////////
// Event table
BEGIN_EVENT_TABLE(DialogStyleManager, wxDialog)
	EVT_BUTTON(wxID_CLOSE, DialogStyleManager::OnClose)
	EVT_BUTTON(BUTTON_CATALOG_NEW, DialogStyleManager::OnCatalogNew)
	EVT_BUTTON(BUTTON_CATALOG_DELETE, DialogStyleManager::OnCatalogDelete)
	EVT_COMBOBOX(LIST_CATALOG, DialogStyleManager::OnChangeCatalog)
	EVT_LISTBOX(LIST_CURRENT, DialogStyleManager::OnCurrentChange)
	EVT_LISTBOX(LIST_STORAGE, DialogStyleManager::OnStorageChange)
	EVT_LISTBOX_DCLICK(LIST_STORAGE, DialogStyleManager::OnStorageEdit)
	EVT_LISTBOX_DCLICK(LIST_CURRENT, DialogStyleManager::OnCurrentEdit)
	EVT_BUTTON(BUTTON_CURRENT_COPYTO, DialogStyleManager::OnCopyToStorage)
	EVT_BUTTON(BUTTON_STORAGE_COPYTO, DialogStyleManager::OnCopyToCurrent)
	EVT_BUTTON(BUTTON_CURRENT_EDIT, DialogStyleManager::OnCurrentEdit)
	EVT_BUTTON(BUTTON_STORAGE_EDIT, DialogStyleManager::OnStorageEdit)
	EVT_BUTTON(BUTTON_CURRENT_COPY, DialogStyleManager::OnCurrentCopy)
	EVT_BUTTON(BUTTON_STORAGE_COPY, DialogStyleManager::OnStorageCopy)
	EVT_BUTTON(BUTTON_CURRENT_NEW, DialogStyleManager::OnCurrentNew)
	EVT_BUTTON(BUTTON_STORAGE_NEW, DialogStyleManager::OnStorageNew)
	EVT_BUTTON(BUTTON_CURRENT_DELETE, DialogStyleManager::OnCurrentDelete)
	EVT_BUTTON(BUTTON_STORAGE_DELETE, DialogStyleManager::OnStorageDelete)
	EVT_BUTTON(BUTTON_CURRENT_IMPORT, DialogStyleManager::OnCurrentImport)
	EVT_BUTTON(BUTTON_CURRENT_UP, DialogStyleManager::OnCurrentMoveUp)
	EVT_BUTTON(BUTTON_CURRENT_TOP, DialogStyleManager::OnCurrentMoveTop)
	EVT_BUTTON(BUTTON_CURRENT_DOWN, DialogStyleManager::OnCurrentMoveDown)
	EVT_BUTTON(BUTTON_CURRENT_BOTTOM, DialogStyleManager::OnCurrentMoveBottom)
	EVT_BUTTON(BUTTON_CURRENT_SORT, DialogStyleManager::OnCurrentSort)
	EVT_BUTTON(BUTTON_STORAGE_UP, DialogStyleManager::OnStorageMoveUp)
	EVT_BUTTON(BUTTON_STORAGE_TOP, DialogStyleManager::OnStorageMoveTop)
	EVT_BUTTON(BUTTON_STORAGE_DOWN, DialogStyleManager::OnStorageMoveDown)
	EVT_BUTTON(BUTTON_STORAGE_BOTTOM, DialogStyleManager::OnStorageMoveBottom)
	EVT_BUTTON(BUTTON_STORAGE_SORT, DialogStyleManager::OnStorageSort)
END_EVENT_TABLE()


//////////
// Events


/// @brief Close 
/// @param event 
///
void DialogStyleManager::OnClose (wxCommandEvent &event) {
	GetPosition(&lastx, &lasty);
	Close();
}



/// @brief Change catalog entry 
/// @param event 
///
void DialogStyleManager::OnChangeCatalog (wxCommandEvent &event) {
	int sel = CatalogList->GetSelection();
	if (sel != wxNOT_FOUND) {
		StorageActions(true);
		Store.Load(CatalogList->GetString(sel));
		LoadStorageStyles();
	}
	else {
		StorageActions(false);
		Store.Clear();
		LoadStorageStyles();
	}
	UpdateMoveButtons();
}



/// @brief New catalog entry 
/// @param event 
/// @return 
///
void DialogStyleManager::OnCatalogNew (wxCommandEvent &event) {
	wxString name = wxGetTextFromUser(_("New storage name:"), _("New catalog entry"), _T(""), this);
	if (!name.empty()) {
		// Remove bad characters from the name
		wxString badchars = wxFileName::GetForbiddenChars();
		int badchars_removed = 0;
		for (size_t i = 0; i < name.Length(); ++i) {
			for (size_t j = 0; j < badchars.Length(); ++j) {
				if (name[i] == badchars[j]) {
					name[i] = _T('_');
					++badchars_removed;
				}
			}
		}

		// Make sure that there is no storage with the same name (case insensitive search since Windows filenames are case insensitive)
		if (CatalogList->FindString(name, false) != wxNOT_FOUND) {
			wxMessageBox(_("A catalog with that name already exists."),_("Catalog name conflict"),wxICON_ERROR|wxOK);
			return;
		}

		// Warn about bad characters
		if (badchars_removed > 0) {
			wxMessageBox(wxString::Format(_("The specified catalog name contains one or more illegal characters. They have been replaced with underscores instead.\nThe catalog has been renamed to \"%s\"."), name.c_str()),_("Invalid characters"));
		}

		// Add to list of storages
		Store.Clear();
		StorageList->Clear();
		CatalogList->Append(name);
		CatalogList->SetStringSelection(name);
		StorageActions(true);

		// Save
		wxString dirname = StandardPaths::DecodePath(_T("?user/catalog/"));
		if (!wxDirExists(dirname)) {
			if (!wxMkdir(dirname)) {
				throw _T("Failed creating directory for style catalogues");
			}
		}
		Store.Save(name);
	}
	UpdateMoveButtons();
}



/// @brief Catalog delete 
/// @param event 
///
void DialogStyleManager::OnCatalogDelete (wxCommandEvent &event) {
	int sel = CatalogList->GetSelection();
	if (sel != wxNOT_FOUND) {
		wxString name = CatalogList->GetString(sel);
		wxString message = wxString::Format(_("Are you sure you want to delete the storage \"%s\" from the catalog?"), name.c_str());
		int option = wxMessageBox(message, _("Confirm delete"), wxYES_NO | wxICON_EXCLAMATION , this);
		if (option == wxYES) {
			wxRemoveFile(StandardPaths::DecodePath(_T("?user/catalog/") + name + _T(".sty")));
			CatalogList->Delete(sel);
			StorageList->Clear();
			StorageActions(false);
		}
	}
	UpdateMoveButtons();
}



/// @brief Edit style on storage 
/// @param event 
///
void DialogStyleManager::OnStorageEdit (wxCommandEvent &event) {
	wxArrayInt selections;
	int n = StorageList->GetSelections(selections);
	AssStyle *temp;
	if (n == 1) {
		temp = styleStorageMap.at(selections[0]);
		DialogStyleEditor editor(this,temp,grid,false,&Store);
		int modified = editor.ShowModal();
		if (modified) {
			//LoadStorageStyles();
			StorageList->SetString(selections[0],temp->name);
			Store.Save(CatalogList->GetString(CatalogList->GetSelection()));
		}
	}
	else if (n > 1) {
	}
	UpdateMoveButtons();
}



/// @brief Edit style on current script 
/// @param event 
///
void DialogStyleManager::OnCurrentEdit (wxCommandEvent &event) {
	wxArrayInt selections;
	int n = CurrentList->GetSelections(selections);
	AssStyle *temp;
	if (n == 1) {
		temp = styleMap.at(selections[0]);
		DialogStyleEditor editor(this,temp,grid,true,&Store);
		int modified = editor.ShowModal();
		if (modified) {
			CurrentList->SetString(selections[0],temp->name);
		}
	}
	else if (n > 1) {
	}
	UpdateMoveButtons();
}



/// @brief Selection on current script changed 
/// @param event 
///
void DialogStyleManager::OnCurrentChange (wxCommandEvent &event) {
	wxArrayInt selections;
	int n = CurrentList->GetSelections(selections);

	CurrentEdit->Enable(n == 1);
	CurrentCopy->Enable(n == 1);
	CurrentDelete->Enable(n > 0);
	MoveToStorage->Enable(n > 0);
	UpdateMoveButtons();
}



/// @brief Selection on storage changed 
/// @param event 
///
void DialogStyleManager::OnStorageChange (wxCommandEvent &event) {
	wxArrayInt selections;
	int n = StorageList->GetSelections(selections);

	StorageEdit->Enable(n == 1);
	StorageCopy->Enable(n == 1);
	StorageDelete->Enable(n > 0);
	MoveToLocal->Enable(n > 0);
	UpdateMoveButtons();
}



/// @brief Copy to Storage 
/// @param event 
/// @return 
///
void DialogStyleManager::OnCopyToStorage (wxCommandEvent &event) {
	// Check if there is actually a storage
	if (!StorageNew->IsEnabled()) {
		return;
	}

	wxArrayInt selections;
	int n = CurrentList->GetSelections(selections);
	for (int i = 0; i < n; i++) {
		wxString styleName = CurrentList->GetString(selections[i]);
		bool addStyle = true;
		
		for (std::list<AssStyle *>::iterator style = Store.style.begin(); style != Store.style.end(); ++style) {
			if ((*style)->name.CmpNoCase(styleName) == 0) {
				addStyle = false;
				if (wxYES == wxMessageBox(wxString::Format(_T("There is already a style with the name \"%s\" on the current storage. Proceed and overwrite anyway?"),styleName), _T("Style name collision."), wxYES_NO)) {
					**style = *styleMap.at(selections[i]);
				}
				break;
			}
		}
		if (addStyle) {
			AssStyle *temp = new AssStyle(*styleMap.at(selections[i]));
			Store.style.push_back(temp);
		}
	}
	Store.Save(CatalogList->GetString(CatalogList->GetSelection()));
	LoadStorageStyles();
	UpdateMoveButtons();
}



/// @brief Copy to Current 
/// @param event 
///
void DialogStyleManager::OnCopyToCurrent (wxCommandEvent &event) {
	wxArrayInt selections;
	int n = StorageList->GetSelections(selections);
	for (int i = 0; i < n; i++) {
		wxString styleName = StorageList->GetString(selections[i]);
		bool addStyle = true;
		
		for (std::vector<AssStyle *>::iterator style = styleMap.begin(); style != styleMap.end(); ++style) {
			if ((*style)->name.CmpNoCase(styleName) == 0) {
				addStyle = false;
				if (wxYES == wxMessageBox(wxString::Format(_T("There is already a style with the name \"%s\" on the current script. Proceed and overwrite anyway?"),styleName), _T("Style name collision."), wxYES_NO)) {
					**style = *styleStorageMap.at(selections[i]);
				}
				break;
			}
		}
		if (addStyle) {
			AssStyle *temp = new AssStyle(*styleStorageMap.at(selections[i]));
			AssFile::top->InsertStyle(temp);
		}
	}
	LoadCurrentStyles(AssFile::top);
	grid->ass->FlagAsModified(_("style copy"));
	grid->CommitChanges();
	UpdateMoveButtons();
}



/// @brief Storage make copy 
/// @param event 
///
void DialogStyleManager::OnStorageCopy (wxCommandEvent &event) {
	wxArrayInt selections;
	StorageList->GetSelections(selections);
	AssStyle *temp = new AssStyle(*(styleStorageMap.at(selections[0])));

	wxString newName = _("Copy of ");
	newName += temp->name;
	temp->name = newName;

	DialogStyleEditor editor(this,temp,grid,false,&Store);
	int modified = editor.ShowModal();
	if (modified) {
		Store.style.push_back(temp);
		Store.Save(CatalogList->GetString(CatalogList->GetSelection()));
		LoadStorageStyles();
		StorageList->SetStringSelection(temp->name); // the copy/delete/copy-to-local buttons stay disabled after this?
	}
	else delete temp;
	UpdateMoveButtons();
}



/// @brief Current make copy 
/// @param event 
///
void DialogStyleManager::OnCurrentCopy (wxCommandEvent &event) {
	wxArrayInt selections;
	CurrentList->GetSelections(selections);

	AssStyle *temp = new AssStyle(styleMap.at(selections[0])->GetEntryData());
	wxString newName = _("Copy of ");
	newName += temp->name;
	temp->name = newName;

	DialogStyleEditor editor(this,temp,grid,true,&Store);
	int modified = editor.ShowModal();
	if (modified) {
		AssFile::top->InsertStyle(temp);
		LoadCurrentStyles(AssFile::top);
		CurrentList->SetStringSelection(temp->name); // but even without this, the copy/delete/copy-to-storage buttons stay enabled?
	}
	else delete temp;

	grid->ass->FlagAsModified(_("style copy"));
	grid->CommitChanges();
	UpdateMoveButtons();
}



/// @brief Copy to clipboard 
/// @param list 
/// @param v    
///
void DialogStyleManager::CopyToClipboard (wxListBox *list, std::vector<AssStyle*> v) {
	wxString data = _T("");
	AssStyle *s;
	wxArrayInt selections;
	list->GetSelections(selections);

	for(int unsigned i=0;i<selections.size();i++) {
		if (i!=0) data += _T("\r\n");
		s = v.at(selections[i]);
		s->UpdateData();
		data += s->GetEntryData();
	}

	if (wxTheClipboard->Open()) {
		wxTheClipboard->SetData(new wxTextDataObject(data));
		wxTheClipboard->Close();
	}
}

/// @brief Paste from clipboard 
///
void DialogStyleManager::PasteToCurrent() {
	wxString data = _T("");

	if (wxTheClipboard->Open()) {
		if (wxTheClipboard->IsSupported(wxDF_TEXT)) {
			wxTextDataObject rawdata;
			wxTheClipboard->GetData(rawdata);
			data = rawdata.GetText();
		}
		wxTheClipboard->Close();
	}

	wxStringTokenizer st(data,_T('\n'));
	while (st.HasMoreTokens()) {
		try {
			AssStyle *s = new AssStyle(st.GetNextToken().Trim(true));
			if (s->Valid) {
				while (AssFile::top->GetStyle(s->name) != NULL)
					s->name = _T("Copy of ") + s->name;

				s->UpdateData();
				AssFile::top->InsertStyle(s);
				LoadCurrentStyles(AssFile::top);

				grid->ass->FlagAsModified(_("style paste"));
				grid->CommitChanges();
			}
			else
				wxMessageBox(_("Could not parse style"), _("Could not parse style"), wxOK | wxICON_EXCLAMATION , this);
		}
		catch (...) {
			wxMessageBox(_("Could not parse style"), _("Could not parse style"), wxOK | wxICON_EXCLAMATION , this);
		}

	}
}

/// @brief DOCME
///
void DialogStyleManager::PasteToStorage() {
	wxString data = _T("");

	if (wxTheClipboard->Open()) {
		if (wxTheClipboard->IsSupported(wxDF_TEXT)) {
			wxTextDataObject rawdata;
			wxTheClipboard->GetData(rawdata);
			data = rawdata.GetText();
		}
		wxTheClipboard->Close();
	}

	wxStringTokenizer st(data,_T('\n'));
	while (st.HasMoreTokens()) {
		try {
			AssStyle *s = new AssStyle(st.GetNextToken().Trim(true));
			if (s->Valid) {
				while (Store.GetStyle(s->name) != NULL)
					s->name = _T("Copy of ") + s->name;

				s->UpdateData();
				Store.style.push_back(s);
				Store.Save(CatalogList->GetString(CatalogList->GetSelection()));

				LoadStorageStyles();
				StorageList->SetStringSelection(s->name);
			}
			else
				wxMessageBox(_("Could not parse style"), _("Could not parse style"), wxOK | wxICON_EXCLAMATION , this);
		}
		catch(...) {
			wxMessageBox(_("Could not parse style"), _("Could not parse style"), wxOK | wxICON_EXCLAMATION , this);
		}

	}
}

/// @brief Storage new 
/// @param event 
///
void DialogStyleManager::OnStorageNew (wxCommandEvent &event) {
	AssStyle *temp = new AssStyle;

	DialogStyleEditor editor(this,temp,grid,false,&Store);
	int modified = editor.ShowModal();
	if (modified) {
		Store.style.push_back(temp);
		Store.Save(CatalogList->GetString(CatalogList->GetSelection()));
		LoadStorageStyles();
	}
	else delete temp;
	UpdateMoveButtons();
}



/// @brief Current new 
/// @param event 
///
void DialogStyleManager::OnCurrentNew (wxCommandEvent &event) {
	AssStyle *temp = new AssStyle;

	DialogStyleEditor editor(this,temp,grid,true,&Store);
	int modified = editor.ShowModal();
	if (modified) {
		AssFile::top->InsertStyle(temp);
		LoadCurrentStyles(AssFile::top);
	}
	else delete temp;
	UpdateMoveButtons();
}



/// @brief Storage delete 
/// @param event 
///
void DialogStyleManager::OnStorageDelete (wxCommandEvent &event) {
	wxArrayInt selections;
	int n = StorageList->GetSelections(selections);

	wxString message;
	if (n!=1) {
		message = _("Are you sure you want to delete these ");
		message += wxString::Format(_T("%i"),n);
		message += _(" styles?");
	}
	else message = _("Are you sure you want to delete this style?");
	int option = wxMessageBox(message, _("Confirm delete from storage"), wxYES_NO | wxICON_EXCLAMATION , this);

	if (option == wxYES) {
		AssStyle *temp;
		for (int i=0;i<n;i++) {
			temp = styleStorageMap.at(selections[i]);
			Store.style.remove(temp);
			delete temp;
		}
		Store.Save(CatalogList->GetString(CatalogList->GetSelection()));
		LoadStorageStyles();
		
		// Set buttons
		MoveToLocal->Enable(false);
		StorageCopy->Enable(false);
		StorageDelete->Enable(false);
	}
	UpdateMoveButtons();
}



/// @brief Current delete 
/// @param event 
///
void DialogStyleManager::OnCurrentDelete (wxCommandEvent &event) {
	wxArrayInt selections;
	int n = CurrentList->GetSelections(selections);

	wxString message;
	if (n!=1) {
		message = _("Are you sure you want to delete these ");
		message += wxString::Format(_T("%i"),n);
		message += _(" styles?");
	}
	else message = _("Are you sure you want to delete this style?");
	int option = wxMessageBox(message, _("Confirm delete from current"), wxYES_NO | wxICON_EXCLAMATION , this);

	if (option == wxYES) {
		AssStyle *temp;
		for (int i=0;i<n;i++) {
			temp = styleMap.at(selections[i]);
			grid->ass->Line.remove(temp);
			delete temp;
		}
		LoadCurrentStyles(grid->ass);

		// Set buttons
		MoveToStorage->Enable(false);
		CurrentCopy->Enable(false);
		CurrentDelete->Enable(false);

		grid->ass->FlagAsModified(_("style delete"));
		grid->CommitChanges();
	}
	UpdateMoveButtons();
}



/// @brief Import styles from another script 
/// @param event 
/// @return 
///
void DialogStyleManager::OnCurrentImport(wxCommandEvent &event) {
	// Get file name
	wxString path = Options.AsText(_T("Last open subtitles path"));	
	wxString filename = wxFileSelector(_("Open subtitles file"),path,_T(""),_T(""),AssFile::GetWildcardList(0),wxFD_OPEN | wxFD_FILE_MUST_EXIST);

	if (!filename.IsEmpty()) {
		// Save path
		wxFileName filepath(filename);
		Options.SetText(_T("Last open subtitles path"), filepath.GetPath());
		Options.Save();

		try {
			// Load file
			AssFile temp;
			temp.Load(filename,_T(""),false);

			// Get styles
			wxArrayString styles = temp.GetStyles();
			if (styles.Count() == 0 || (styles.Count() == 1 && styles[0] == _T("Default"))) {
				wxMessageBox(_("The selected file has no available styles."),_("Error Importing Styles"),wxOK);
				return;
			}

			// Get selection
			wxArrayInt selections;
			int res = wxGetSelectedChoices(selections,_("Choose styles to import:"),_("Import Styles"),styles);
			if (res == -1 || selections.Count() == 0) return;
			bool modified = false;

			// Loop through selection
			for (unsigned int i=0;i<selections.Count();i++) {
				// Check if there is already a style with that name
				int test = CurrentList->FindString(styles[selections[i]], false);
				if (test != wxNOT_FOUND) {
					int answer = wxMessageBox(wxString::Format(_T("There is already a style with the name \"%s\" on the current script. Overwrite?"),styles[selections[i]].c_str()),_T("Style name collision."),wxYES_NO);
					if (answer == wxYES) {
						// Overwrite
						modified = true;
						// The GetString->FindString mess is a silly workaround for the fact that to vsfilter
						// (and the duplicate check a few lines above), style names aren't case sensitive, but to the
						// rest of Aegisub they are.
						*(AssFile::top->GetStyle(CurrentList->GetString(CurrentList->FindString(styles[selections[i]], false)))) = *temp.GetStyle(styles[selections[i]]);
					}
					continue;
				}

				// Copy
				modified = true;
				AssStyle *tempStyle = new AssStyle;
				*tempStyle = *temp.GetStyle(styles[selections[i]]);
				AssFile::top->InsertStyle(tempStyle);
			}

			// Update
			if (modified) {
				LoadCurrentStyles(grid->ass);
				grid->ass->FlagAsModified(_("style import"));
				grid->CommitChanges();
			}
		}
		catch (...) {
		}
	}
}



/// @brief Update move buttons 
///
void DialogStyleManager::UpdateMoveButtons() {
	// Get storage selection
	wxArrayInt sels;
	int n = StorageList->GetSelections(sels);
	int firstStor = -1;
	int lastStor = -1;
	if (n) {
		firstStor = sels[0];
		lastStor = sels[n-1];
	}
	int itemsStor = StorageList->GetCount();

	// Check if selection is continuous
	bool contStor = true;
	if (n) {
		int last = sels[0];
		for (int i=1;i<n;i++) {
			if (sels[i] != last+1) {
				contStor = false;
				break;
			}
			last = sels[i];
		}
	}

	// Get current selection
	n = CurrentList->GetSelections(sels);
	int firstCurr = -1;
	int lastCurr = -1;
	if (n) {
		firstCurr = sels[0];
		lastCurr = sels[n-1];
	}
	int itemsCurr = CurrentList->GetCount();

	// Check if selection is continuous
	bool contCurr = true;
	if (n) {
		int last = sels[0];
		for (int i=1;i<n;i++) {
			if (sels[i] != last+1) {
				contCurr = false;
				break;
			}
			last = sels[i];
		}
	}

	// Set values
	StorageMoveUp->Enable(contStor && firstStor > 0);
	StorageMoveTop->Enable(contStor && firstStor > 0);
	StorageMoveDown->Enable(contStor && lastStor != -1 && lastStor < itemsStor-1);
	StorageMoveBottom->Enable(contStor && lastStor != -1 && lastStor < itemsStor-1);
	StorageSort->Enable(itemsStor > 1);
	CurrentMoveUp->Enable(contCurr && firstCurr > 0);
	CurrentMoveTop->Enable(contCurr && firstCurr > 0);
	CurrentMoveDown->Enable(contCurr && lastCurr != -1 && lastCurr < itemsCurr-1);
	CurrentMoveBottom->Enable(contCurr && lastCurr != -1 && lastCurr < itemsCurr-1);
	CurrentSort->Enable(itemsCurr > 1);
}



/// @brief Move events 
/// @param event 
///
void DialogStyleManager::OnStorageMoveUp (wxCommandEvent &event) { MoveStyles(true,0); }

/// @brief DOCME
/// @param event 
///
void DialogStyleManager::OnStorageMoveTop (wxCommandEvent &event) { MoveStyles(true,1); }

/// @brief DOCME
/// @param event 
///
void DialogStyleManager::OnStorageMoveDown (wxCommandEvent &event) { MoveStyles(true,2); }

/// @brief DOCME
/// @param event 
///
void DialogStyleManager::OnStorageMoveBottom (wxCommandEvent &event) { MoveStyles(true,3); }

/// @brief DOCME
/// @param event 
///
void DialogStyleManager::OnStorageSort (wxCommandEvent &event) { MoveStyles(true,4); }

/// @brief DOCME
/// @param event 
///
void DialogStyleManager::OnCurrentMoveUp (wxCommandEvent &event) { MoveStyles(false,0); }

/// @brief DOCME
/// @param event 
///
void DialogStyleManager::OnCurrentMoveTop (wxCommandEvent &event) { MoveStyles(false,1); }

/// @brief DOCME
/// @param event 
///
void DialogStyleManager::OnCurrentMoveDown (wxCommandEvent &event) { MoveStyles(false,2); }

/// @brief DOCME
/// @param event 
///
void DialogStyleManager::OnCurrentMoveBottom (wxCommandEvent &event) { MoveStyles(false,3); }

/// @brief DOCME
/// @param event 
///
void DialogStyleManager::OnCurrentSort (wxCommandEvent &event) { MoveStyles(false,4); }


/// @brief Move function 
/// @param storage 
/// @param type    
/// @return 
///
void DialogStyleManager::MoveStyles(bool storage, int type) {
	// Variables
	AssFile *subs = AssFile::top;
	wxListBox *list;
	if (storage) list = StorageList;
	else list = CurrentList;

	// Get selection
	wxArrayInt sels;
	int n = list->GetSelections(sels);
	int first = -1;;
	int last = -1;
	if (n) {
		first = sels[0];
		last = sels[n-1];
	}

	// Get total style count
	int nStyles = list->GetCount();

	// Get styles
	std::vector<AssStyle*> styls;
	std::vector<AssStyle*> *srcStyls;
	if (storage) srcStyls = &styleStorageMap;
	else srcStyls = &styleMap;

	// Move up
	if (type == 0) {
		for (int i=0;i<first-1;i++) styls.push_back(srcStyls->at(i));
		for (int i=first;i<=last;i++) styls.push_back(srcStyls->at(i));
		styls.push_back(srcStyls->at(first-1));
		for (int i=last+1;i<nStyles;i++) styls.push_back(srcStyls->at(i));
		first--;
		last--;
	}

	// Move to top
	if (type == 1) {
		for (int i=first;i<=last;i++) styls.push_back(srcStyls->at(i));
		for (int i=0;i<first;i++) styls.push_back(srcStyls->at(i));
		for (int i=last+1;i<nStyles;i++) styls.push_back(srcStyls->at(i));
		last = last-first;
		first = 0;
	}

	// Move down
	if (type == 2) {
		for (int i=0;i<first;i++) styls.push_back(srcStyls->at(i));
		styls.push_back(srcStyls->at(last+1));
		for (int i=first;i<=last;i++) styls.push_back(srcStyls->at(i));
		for (int i=last+2;i<nStyles;i++) styls.push_back(srcStyls->at(i));
		first++;
		last++;
	}

	// Move to bottom
	if (type == 3) {
		for (int i=0;i<first;i++) styls.push_back(srcStyls->at(i));
		for (int i=last+1;i<nStyles;i++) styls.push_back(srcStyls->at(i));
		for (int i=first;i<=last;i++) styls.push_back(srcStyls->at(i));
		first = nStyles-(last-first+1);
		last = nStyles-1;
	}

	// Sort
	if (type == 4) {
		// Get confirmation
		if (storage) {
			int res = wxMessageBox(_("Are you sure? This cannot be undone!"),_("Sort styles"),wxYES_NO);
			if (res == wxNO) return;
		}

		// Get sorted list
		wxArrayString styleNames;
		for (int i=0; i < nStyles; i++) styleNames.Add(srcStyls->at(i)->name);
		styleNames.Sort();

		std::list<AssStyle *> styles(srcStyls->begin(), srcStyls->end());
		for (int i = 0; i < nStyles; i++) {
			for (std::list<AssStyle *>::iterator style = styles.begin(); style != styles.end(); style++) {
				if ((*style)->name == styleNames[i]) {
					styls.push_back(*style);
					styles.erase(style);
					break;
				}
			}
		}

		// Zero selection
		first = 0;
		last = 0;
	}

	// Storage
	if (storage) {
		// Rewrite storage
		Store.style.clear();
		for (unsigned int i=0;i<styls.size();i++) Store.style.push_back(styls[i]);
		
		// Save storage
		Store.Save(CatalogList->GetString(CatalogList->GetSelection()));
	}

	// Current
	else {
		// Replace styles
		entryIter next;
		int curn = 0;
		for (entryIter cur=subs->Line.begin();cur!=subs->Line.end();cur = next) {
			next = cur;
			next++;
			AssStyle *style = AssEntry::GetAsStyle(*cur);
			if (style) {
				subs->Line.insert(cur,styls[curn]);
				subs->Line.erase(cur);
				curn++;
			}
		}

		// Flag as modified
		grid->ass->FlagAsModified(_("style move"));
		grid->CommitChanges();
	}

	// Update
	for (int i=0;i<nStyles;i++) {
		list->SetString(i,styls[i]->name);
		if (i < first || i > last) list->Deselect(i);
		else list->Select(i);
	}

	// Set map
	*srcStyls = styls;

	// Update buttons
	UpdateMoveButtons();
}



/// @brief Keydown event 
/// @param event 
///
void DialogStyleManager::OnKeyDown(wxKeyEvent &event) {
	wxCommandEvent evt;
	switch(event.GetKeyCode()) {
		case WXK_ESCAPE :
			OnClose(evt);
			break;

		case WXK_DELETE :
			if (wxWindow::FindFocus()==StorageList) {
				OnStorageDelete(evt);
			}
			else if (wxWindow::FindFocus()==CurrentList) {
				OnCurrentDelete(evt);
			}
			break;

		case 'C' :
		case 'c' :
#ifdef __APPLE__
			if (event.CmdDown()) {
#else
			if (event.ControlDown()) {
#endif
				if (wxWindow::FindFocus()==CurrentList) {
					CopyToClipboard(CurrentList,styleMap);
				}
				else if (wxWindow::FindFocus()==StorageList) {
					CopyToClipboard(StorageList,styleStorageMap);
				}
			}
			break;

		case 'V' :
		case 'v' :
#ifdef __APPLE__
			if (event.CmdDown()) {
#else
			if (event.ControlDown()) {
#endif
				if (wxWindow::FindFocus()==CurrentList) {
					PasteToCurrent();
				}
				else if (wxWindow::FindFocus()==StorageList) {
					PasteToStorage();
				}
			}

			break;

	}
}

/// DOCME
int DialogStyleManager::lastx = -1;

/// DOCME
int DialogStyleManager::lasty = -1;



/// @brief DialogStyleManagerEvent stuff 
/// @param ctrl 
///
DialogStyleManagerEvent::DialogStyleManagerEvent(DialogStyleManager *ctrl) {
	control = ctrl;
}
BEGIN_EVENT_TABLE(DialogStyleManagerEvent, wxEvtHandler)
	EVT_KEY_DOWN(DialogStyleManagerEvent::OnKeyDown)
END_EVENT_TABLE()

/// @brief DOCME
/// @param event 
///
void DialogStyleManagerEvent::OnKeyDown(wxKeyEvent &event) {
	control->OnKeyDown(event); //we need to access controls, so rather than make the controls public...
}





