// 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
//
// Website: http://aegisub.cellosoft.com
// Contact: mailto:zeratul@cellosoft.com
//


///////////
// Headers
#include <wx/recguard.h>
#include "dialog_styling_assistant.h"
#include "subs_grid.h"
#include "ass_file.h"
#include "ass_style.h"
#include "ass_dialogue.h"
#include "video_display.h"
#include "vfr.h"
#include "frame_main.h"
#include "audio_display.h"
#include "audio_box.h"
#include "hotkeys.h"
#include "utils.h"
#include "help_button.h"


///////////////
// Constructor
DialogStyling::DialogStyling (wxWindow *parent,SubtitlesGrid *_grid) :
wxDialog (parent, -1, _("Styling assistant"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxRESIZE_BORDER)
{
	// Set icon
	SetIcon(BitmapToIcon(wxBITMAP(styling_toolbutton)));

	// Variables
	grid = _grid;
	audio = VideoContext::Get()->audio->box->audioDisplay;
	needCommit = false;
	linen = -1;

	// Top sizer
	wxSizer *TopSizer = new wxStaticBoxSizer(wxHORIZONTAL,this,_("Current line"));
	CurLine = new wxTextCtrl(this,-1,_("Current line"),wxDefaultPosition,wxSize(300,60),wxTE_MULTILINE | wxTE_READONLY);
	TopSizer->Add(CurLine,1,wxEXPAND,0);

	// Left sizer
	Styles = new wxListBox(this,STYLE_LIST,wxDefaultPosition,wxSize(150,180),grid->ass->GetStyles());
	wxSizer *LeftSizer = new wxStaticBoxSizer(wxVERTICAL,this,_("Styles available"));
	LeftSizer->Add(Styles,1,wxEXPAND,0);

	// Right sizer
	wxSizer *RightSizer = new wxBoxSizer(wxVERTICAL);
	wxSizer *RightTop = new wxStaticBoxSizer(wxHORIZONTAL,this,_("Set style"));
	wxSizer *RightBottom = new wxStaticBoxSizer(wxVERTICAL,this,_("Keys"));
	TypeBox = new StyleEditBox(this);
	RightTop->Add(TypeBox,1,wxEXPAND);

	// Shortcuts
	//wxStaticText *Keys = new wxStaticText(this,-1,_("Enter:\t\tAccept changes\nPage up:\tPrevious line\nPage down:\tNext line\nEnd:\t\tPlay sound\nClick on list:\tSet style\nCtrl+enter:\tAccept without going to next"));
	wxSizer *KeysInnerSizer = new wxGridSizer(2,0,5);
	KeysInnerSizer->Add(new wxStaticText(this,-1,Hotkeys.GetText(_T("Styling Assistant Accept")) + _T(": ")));
	KeysInnerSizer->Add(new wxStaticText(this,-1,_("Accept changes")));
	KeysInnerSizer->Add(new wxStaticText(this,-1,Hotkeys.GetText(_T("Styling Assistant Preview")) + _T(": ")));
	KeysInnerSizer->Add(new wxStaticText(this,-1,_("Preview changes")));
	KeysInnerSizer->Add(new wxStaticText(this,-1,Hotkeys.GetText(_T("Styling Assistant Prev")) + _T(": ")));
	KeysInnerSizer->Add(new wxStaticText(this,-1,_("Previous line")));
	KeysInnerSizer->Add(new wxStaticText(this,-1,Hotkeys.GetText(_T("Styling Assistant Next")) + _T(": ")));
	KeysInnerSizer->Add(new wxStaticText(this,-1,_("Next line")));
	KeysInnerSizer->Add(new wxStaticText(this,-1,Hotkeys.GetText(_T("Styling Assistant Play")) + _T(": ")));
	KeysInnerSizer->Add(new wxStaticText(this,-1,_("Play Audio")));
	KeysInnerSizer->Add(new wxStaticText(this,-1,_("Click on list:")));
	KeysInnerSizer->Add(new wxStaticText(this,-1,_("Select style")));

	// Rest of right sizer
	PreviewCheck = new wxCheckBox(this,-1,_("Enable preview (slow)"));
	PreviewCheck->SetValue(true);
	RightBottom->Add(KeysInnerSizer,0,wxEXPAND | wxBOTTOM,5);
	RightBottom->Add(PreviewCheck,0,0,0);
	RightBottom->AddStretchSpacer(1);
	RightSizer->Add(RightTop,0,wxEXPAND | wxBOTTOM,5);
	RightSizer->Add(RightBottom,1,wxEXPAND | wxBOTTOM,0);

	// Bottom sizer
	wxSizer *BottomSizer = new wxBoxSizer(wxHORIZONTAL);
	BottomSizer->Add(LeftSizer,1,wxEXPAND | wxRIGHT,5);
	BottomSizer->Add(RightSizer,1,wxEXPAND,0);

	// Button sizer
	wxStdDialogButtonSizer *ButtonSizer = new wxStdDialogButtonSizer();
	wxButton *PlayButton = new wxButton(this,BUTTON_PLAY,_("Play Audio"));
	PlayButton->Enable(audio->loaded);
	ButtonSizer->AddButton(PlayButton);
	ButtonSizer->AddButton(new wxButton(this,wxID_CANCEL));
	ButtonSizer->AddButton(new HelpButton(this,_T("Styling Assistant")));
	ButtonSizer->SetAffirmativeButton(PlayButton);
	ButtonSizer->Realize();

	// Main sizer
	wxSizer *MainSizer = new wxBoxSizer(wxVERTICAL);
	MainSizer->Add(TopSizer,0,wxEXPAND | wxALL,5);
	MainSizer->Add(BottomSizer,1,wxEXPAND | wxLEFT | wxBOTTOM | wxRIGHT,5);
	MainSizer->Add(ButtonSizer,0,wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT,5);
	MainSizer->SetSizeHints(this);
	SetSizer(MainSizer);

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

	// h4x
	origColour = TypeBox->GetBackgroundColour();

	// Set selection
	wxArrayInt sel = grid->GetSelection();
	if (sel.Count() > 0) JumpToLine(sel[0]);
	else JumpToLine(0);
}


//////////////
// Destructor
DialogStyling::~DialogStyling () {
	GetPosition(&lastx, &lasty);
	if (needCommit) {
		grid->ass->FlagAsModified(_("style changes"));
		grid->CommitChanges();
	}
}


////////////////
// Jump to line
void DialogStyling::JumpToLine(int n) {
	// Check stuff
	if (linen == n) return;
	if (n == -1) return;

	// Get line
	AssDialogue *nextLine = grid->GetDialogue(n);
	if (!nextLine) return;
	line = nextLine;

	// Set number
	linen = n;

	// Set text
	CurLine->SetValue(line->Text);

	// Set focus
	TypeBox->SetFocus();
	if (TypeBox->GetValue().IsEmpty()) TypeBox->SetValue(Styles->GetString(0));
	TypeBox->SetSelection(0,TypeBox->GetValue().Length());

	// Update grid
	grid->SelectRow(linen,false);
	grid->MakeCellVisible(linen,0);

	// Update display
	if (PreviewCheck->IsChecked()) VideoContext::Get()->JumpToFrame(VFR_Output.GetFrameAtTime(line->Start.GetMS(),true));
}


/////////////////////////////
// Set style of current line
void DialogStyling::SetStyle (wxString curName, bool jump) {
	// Get line
	AssDialogue *line = grid->GetDialogue(linen);

	// Update line
	line->Style = curName;
	line->UpdateData();

	// Update grid/subs
	grid->Refresh(false);
	if (PreviewCheck->IsChecked()) {
		grid->ass->FlagAsModified(_("styling assistant"));
		grid->CommitChanges();
	}
	else needCommit = true;

	// Next line
	if (jump) JumpToLine(linen+1);
}


///////////////
// Event table
BEGIN_EVENT_TABLE(DialogStyling,wxDialog)
	EVT_BUTTON(BUTTON_PLAY, DialogStyling::OnPlayButton)
	//EVT_TEXT_ENTER(ENTER_STYLE_BOX, DialogStyling::OnStyleBoxEnter)
	EVT_TEXT(ENTER_STYLE_BOX, DialogStyling::OnStyleBoxModified)
	EVT_LISTBOX(STYLE_LIST, DialogStyling::OnListClicked)
	EVT_KEY_DOWN(DialogStyling::OnKeyDown)
END_EVENT_TABLE()


///////////////
// Key pressed
void DialogStyling::OnKeyDown(wxKeyEvent &event) {
	int keycode = event.GetKeyCode();

	// Previous line
	if (keycode == WXK_PAGEUP) {
		JumpToLine(linen-1);
	}

	// Next line
	if (keycode == WXK_PAGEDOWN) {
		JumpToLine(linen+1);
	}

	event.Skip();
}


////////////////////
// Edit box changed
void DialogStyling::OnStyleBoxModified (wxCommandEvent &event) {
	// Recursion guard
	static wxRecursionGuardFlag s_flag;
	wxRecursionGuard guard(s_flag);
	if (guard.IsInside()) return;

	// Find partial style name
	long from,to;
	TypeBox->GetSelection(&from,&to);
	wxString partial = TypeBox->GetValue().Left(from).Lower();
	int len = partial.Length();

	// Find first style that matches partial name
	bool match = false;
	int n = Styles->GetCount();
	wxString curname;
	for (int i=0;i<n;i++) {
		curname = Styles->GetString(i);
		if (curname.Left(len).Lower() == partial) {
			match = true;
			break;
		}
	}

	// Found
	if (match) {
		TypeBox->SetValue(curname);
		TypeBox->SetSelection(from,curname.Length());
		TypeBox->SetBackgroundColour(origColour);
		TypeBox->Refresh();
	}

	// Not found
	else {
		TypeBox->SetBackgroundColour(wxColour(255,108,108));
		TypeBox->Refresh();
	}
}


/////////////////
// Enter pressed
void DialogStyling::OnStyleBoxEnter (wxCommandEvent &event) {

}


//////////////////////
// Style list clicked
void DialogStyling::OnListClicked(wxCommandEvent &event) {
	int n = event.GetInt();
	SetStyle(Styles->GetString(n));
	Styles->SetSelection(wxNOT_FOUND);
	TypeBox->SetFocus();
}


///////////////
// Play button
void DialogStyling::OnPlayButton(wxCommandEvent &event) {
	audio->Play(line->Start.GetMS(),line->End.GetMS());
	TypeBox->SetFocus();
}


//////////////////////////////
// Style edit box constructor
StyleEditBox::StyleEditBox(DialogStyling *parent)
: wxTextCtrl(parent,ENTER_STYLE_BOX,_T(""),wxDefaultPosition,wxSize(180,-1),wxTE_PROCESS_ENTER)
{
	diag = parent;
}


////////////////////////////
// Event table for edit box
BEGIN_EVENT_TABLE(StyleEditBox,wxTextCtrl)
	EVT_KEY_DOWN(StyleEditBox::OnKeyDown)
END_EVENT_TABLE()


///////////////
// Key pressed
void StyleEditBox::OnKeyDown(wxKeyEvent &event) {
	//int keycode = event.GetKeyCode();
#ifdef __APPLE__
	Hotkeys.SetPressed(event.GetKeyCode(),event.m_metaDown,event.m_altDown,event.m_shiftDown);
#else
	Hotkeys.SetPressed(event.GetKeyCode(),event.m_controlDown,event.m_altDown,event.m_shiftDown);
#endif

	// Backspace
#ifdef __APPLE__
	if (event.GetKeyCode() == WXK_BACK && !event.m_metaDown && !event.m_altDown && !event.m_shiftDown) {
#else
	if (event.GetKeyCode() == WXK_BACK && !event.m_controlDown && !event.m_altDown && !event.m_shiftDown) {
#endif
		long from,to;
		GetSelection(&from,&to);
		if (from > 0) SetSelection(from-1,to);
	}

	// Previous line
	if (Hotkeys.IsPressed(_T("Styling Assistant Prev"))) {
		diag->JumpToLine(diag->linen-1);
		return;
	}

	// Next line
	if (Hotkeys.IsPressed(_T("Styling Assistant Next"))) {
		diag->JumpToLine(diag->linen+1);
		return;
	}

	// Play audio
	if (Hotkeys.IsPressed(_T("Styling Assistant Play"))) {
		if (diag->audio->loaded) {
			diag->audio->Play(diag->line->Start.GetMS(),diag->line->End.GetMS());
		}
		return;
	}

	// Enter key
	if (Hotkeys.IsPressed(_T("Styling Assistant Accept")) || Hotkeys.IsPressed(_T("Styling Assistant Preview"))) {
		// Make sure that entered style is valid
		int n = diag->Styles->GetCount();
		wxString curName;
		bool match = false;
		for (int i=0;i<n;i++) {
			curName = diag->Styles->GetString(i);
			if (curName == GetValue()) {
				match = true;
				break;
			}
		}

		// Set style name
		if (match) {
			diag->SetStyle(curName,Hotkeys.IsPressed(_T("Styling Assistant Accept")));
		}
		return;
	}

	event.Skip();
}


int DialogStyling::lastx = -1;
int DialogStyling::lasty = -1;