/////////////////////////////////////////////////////////////////////////////
// Name:        main_window.cpp
// Purpose:
// Author:      César Mauri Loba
// Modified by:
// Created:     04/04/2011 18:26:27
// RCS-ID:
// Copyright:   (C) 2011 Cesar Mauri Loba - CREA Software Systems
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

////@begin includes
#include "about.h"
////@end includes

#include "sitplus_main_window.h"
#include "widgets_base/dialogcontainer.h"
#include "activitymanager.h"
#include "activityloader.h"

#include "spcore/component.h"
#include "spcore/coreruntime.h"
#include "spcore/paths.h"
#include "spcore/basictypes.h"

#include "sphost/componenthelper.h"
#include "sphost/langutils.h"
#include "sphost/configutils.h"

////@begin XPM images
#include "bitmaps/sitplus_logo_16x16.xpm"
////@end XPM images
#include <wx/filedlg.h>
#include <wx/utils.h>

using namespace spcore;
using namespace sphost;
using namespace std;

// Flag to control whether more than one activity can be opened simultaneously.
#define ALLOW_MULTIPLE_ACTIVITIES	0

static 
void LoadComponentSettings(const char* cname, IConfiguration& cfg)
{
	ICoreRuntime* cr= getSpCoreRuntime();

	SmartPtr<IComponent> c=	cr->CreateComponent(cname, "", NULL, NULL);
	assert (c.get());
	if (!c.get()) return;

	cfg.SetPath(cname);

	c->LoadSettings(cfg);

	cfg.SetPath("..");
}

static
void SaveComponentSettings(const char* cname, IConfiguration& cfg)
{
	ICoreRuntime* cr= getSpCoreRuntime();

	SmartPtr<IComponent> c=	cr->CreateComponent(cname, "", NULL, NULL);
	assert (c.get());
	if (!c.get()) return;

	cfg.SetPath(cname);

	c->SaveSettings(cfg);

	cfg.SetPath("..");
}

static
void LoadGlobalSettings()
{
	SmartPtr<IConfiguration> cfg= sphost::LoadConfiguration("global.cfg");
	if (!cfg.get()) return;

	LoadComponentSettings("midi_config", *cfg);
	LoadComponentSettings("camera_config", *cfg);
}



static
void SaveGlobalSettings()
{
	ICoreRuntime* cr= getSpCoreRuntime();

	SmartPtr<IConfiguration> cfg= cr->GetConfiguration();
	assert (cfg.get());
	
	SaveComponentSettings("midi_config", *cfg);
	SaveComponentSettings("camera_config", *cfg);

	sphost::SaveConfiguration(*cfg, "global.cfg");
}


/*
 * SitplusMainWindow type definition
 */

IMPLEMENT_CLASS( SitplusMainWindow, wxFrame )

// New event to comunicate log updates to GUI
DECLARE_LOCAL_EVENT_TYPE(wxEVT_LOCAL_LOG_MESSAGE, -1)
DEFINE_LOCAL_EVENT_TYPE(wxEVT_LOCAL_LOG_MESSAGE)

/*
 * SitplusMainWindow event table definition
 */

BEGIN_EVENT_TABLE( SitplusMainWindow, wxFrame )

////@begin SitplusMainWindow event table entries
    EVT_CLOSE( SitplusMainWindow::OnCloseWindow )

    EVT_MENU( ID_MENUITEM_EXIT, SitplusMainWindow::OnMenuitemExitClick )

    EVT_MENU( ID_MENUITEM_ACTIVITY_START, SitplusMainWindow::OnMenuitemActivityStartClick )

    EVT_MENU( ID_MENUITEM_ACTIVITY_LOAD_FILE, SitplusMainWindow::OnMenuitemActivityLoadFileClick )

    EVT_MENU( ID_MENUITEM_CONFIG_CAMERA, SitplusMainWindow::OnMenuitemConfigCameraClick )

    EVT_MENU( ID_MENUITEM_CONFIG_WIIMOTES, SitplusMainWindow::OnMenuitemConfigWiimotesClick )

    EVT_MENU( ID_MENUITEM_CONFIG_MIDI, SitplusMainWindow::OnMenuitemConfigMidiClick )

    EVT_MENU( ID_MENUITEM_CONFIG_PD, SitplusMainWindow::OnMenuitemConfigPdClick )

    EVT_MENU( ID_MENUITEM_LANGUAGE, SitplusMainWindow::OnMenuitemLanguageClick )

    EVT_MENU( ID_MENUITEM_HELPHELP, SitplusMainWindow::OnMenuitemHelphelpClick )

    EVT_MENU( ID_MENUITEM_ABOUT, SitplusMainWindow::OnMenuitemAboutClick )

////@end SitplusMainWindow event table entries

	EVT_COMMAND  (wxID_ANY, wxEVT_LOCAL_LOG_MESSAGE, SitplusMainWindow::OnLogMessage)

END_EVENT_TABLE()



/*
 * SitplusMainWindow constructors
 */

SitplusMainWindow::SitplusMainWindow()
{
    Init();
}

SitplusMainWindow::SitplusMainWindow( wxWindow* parent, wxWindowID id, const wxString& caption, const wxPoint& pos, const wxSize& size, long style )
{
    Init();
    Create( parent, id, caption, pos, size, style );
}


/*
 * main_window creator
 */

bool SitplusMainWindow::Create( wxWindow* parent, wxWindowID id, const wxString& caption, const wxPoint& pos, const wxSize& size, long style )
{
////@begin SitplusMainWindow creation
    wxFrame::Create( parent, id, caption, pos, size, style );

    CreateControls();
    SetIcon(GetIconResource(wxT("bitmaps/sitplus_logo_16x16.xpm")));
    Centre();
////@end SitplusMainWindow creation
	
	getSpCoreRuntime()->RegisterLogTarget(this);



    return true;
}


/*
 * SitplusMainWindow destructor
 */

SitplusMainWindow::~SitplusMainWindow()
{
	getSpCoreRuntime()->UnregisterLogTarget(this);
////@begin SitplusMainWindow destruction
////@end SitplusMainWindow destruction
}


/*
 * Member initialisation
 */

void SitplusMainWindow::Init()
{
	m_hasVetoed= false;
////@begin SitplusMainWindow member initialisation
    m_txtLog = NULL;
////@end SitplusMainWindow member initialisation
	LoadGlobalSettings();
}


/*
 * Control creation for main_window
 */

void SitplusMainWindow::CreateControls()
{
////@begin SitplusMainWindow content construction
    SitplusMainWindow* itemFrame1 = this;

    wxMenuBar* menuBar = new wxMenuBar;
    wxMenu* itemMenu3 = new wxMenu;
    itemMenu3->Append(ID_MENUITEM_EXIT, _("&Exit"), wxEmptyString, wxITEM_NORMAL);
    menuBar->Append(itemMenu3, _("&File"));
    wxMenu* itemMenu5 = new wxMenu;
    itemMenu5->Append(ID_MENUITEM_ACTIVITY_START, _("&Start..."), wxEmptyString, wxITEM_NORMAL);
    itemMenu5->AppendSeparator();
    itemMenu5->Append(ID_MENUITEM_ACTIVITY_LOAD_FILE, _("&Load file..."), wxEmptyString, wxITEM_NORMAL);
    menuBar->Append(itemMenu5, _("&Activity"));
    wxMenu* itemMenu9 = new wxMenu;
    itemMenu9->Append(ID_MENUITEM_CONFIG_CAMERA, _("&Camera..."), wxEmptyString, wxITEM_NORMAL);
    itemMenu9->Append(ID_MENUITEM_CONFIG_WIIMOTES, _("&Wii remotes..."), wxEmptyString, wxITEM_NORMAL);
    itemMenu9->Append(ID_MENUITEM_CONFIG_MIDI, _("&MIDI Out..."), wxEmptyString, wxITEM_NORMAL);
    itemMenu9->Append(ID_MENUITEM_CONFIG_PD, _("&Pure Data..."), wxEmptyString, wxITEM_NORMAL);
    itemMenu9->AppendSeparator();
    itemMenu9->Append(ID_MENUITEM_LANGUAGE, _("&Language..."), wxEmptyString, wxITEM_NORMAL);
    menuBar->Append(itemMenu9, _("&Configuration"));
    wxMenu* itemMenu16 = new wxMenu;
    itemMenu16->Append(ID_MENUITEM_HELPHELP, _("&Help"), wxEmptyString, wxITEM_NORMAL);
    itemMenu16->AppendSeparator();
    itemMenu16->Append(ID_MENUITEM_ABOUT, _("&About"), wxEmptyString, wxITEM_NORMAL);
    menuBar->Append(itemMenu16, _("&Help"));
    itemFrame1->SetMenuBar(menuBar);

    m_txtLog = new wxTextCtrl( itemFrame1, ID_TEXTCTRL_LOG, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY );

////@end SitplusMainWindow content construction
}


void SitplusMainWindow::EnableActivityOptions(bool enable)
{
#if !ALLOW_MULTIPLE_ACTIVITIES
	GetMenuBar()->FindItem(ID_MENUITEM_ACTIVITY_START)->Enable(enable);
	GetMenuBar()->FindItem(ID_MENUITEM_ACTIVITY_LOAD_FILE)->Enable(enable);	
#else
	((void)enable);
#endif
}


/*
 * Should we show tooltips?
 */

bool SitplusMainWindow::ShowToolTips()
{
    return true;
}

/*
 * Get bitmap resources
 */

wxBitmap SitplusMainWindow::GetBitmapResource( const wxString& name )
{
    // Bitmap retrieval
////@begin SitplusMainWindow bitmap retrieval
    wxUnusedVar(name);
    return wxNullBitmap;
////@end SitplusMainWindow bitmap retrieval
}

/*
 * Get icon resources
 */

wxIcon SitplusMainWindow::GetIconResource( const wxString& name )
{
    // Icon retrieval
////@begin SitplusMainWindow icon retrieval
    wxUnusedVar(name);
    if (name == _T("bitmaps/sitplus_logo_16x16.xpm"))
    {
        wxIcon icon(sitplus_logo_16x16);
        return icon;
    }
    return wxNullIcon;
////@end SitplusMainWindow icon retrieval
}

/*
 * wxEVT_COMMAND_MENU_SELECTED event handler for ID_MENUITEM_EXIT
 */

void SitplusMainWindow::OnMenuitemExitClick( wxCommandEvent& event )
{
	Close();
    event.Skip(false);
}


void SitplusMainWindow::OpenConfigDialog(const char* component, const std::vector<std::string>* args)
{
	widgets_base::DialogContainer dlg(this);

	unsigned int argc= 0;
	const char** argv= NULL;

	if (args) {
		argc= args->size();
		if (argc) {
			argv= new const char* [argc];
			for (unsigned int i= 0; i< argc; ++i)
				argv[i]= (*args)[i].c_str();
		}
	}

	// Create config component
	SmartPtr<IComponent> compo=
		getSpCoreRuntime()->CreateComponent(component, "config_dlg", argc, argv);
	delete[] argv;

	if (compo.get()== NULL) {
		wxMessageDialog dlgErr(this, _("Unexpected error opening config dialogue. See console for details"), _("ERROR"), wxICON_ERROR );
		dlgErr.ShowModal();
		return;
	}

	wxWindow* panel= compo->GetGUI(&dlg);
	assert (panel);
	dlg.AddSitplusPanel(panel);

	if (compo->Initialize()!= 0) {
		wxMessageDialog msg(this, 
			_("Configuration of ") + wxString(component, wxConvUTF8) + _(" failed.\nSee console for details"));
		msg.ShowModal();
		return;
	}

	dlg.ShowModal();

	compo->Finish();

	SaveGlobalSettings();
}


void SitplusMainWindow::OnCloseChildWindow( wxCloseEvent& event )
{
	if (event.CanVeto()) {
		wxMessageDialog dlg(this, _("Are you sure you want to close this activity?"), 
			_("Close confirmation"), wxYES_NO | wxNO_DEFAULT);
		if (dlg.ShowModal()!= wxID_YES) {
			m_hasVetoed= true;
			return;
		}
	}

	m_hasVetoed= false;
	static_cast<wxWindow *>(event.GetEventObject())->Destroy();

	// Enable activity menu entries
	EnableActivityOptions(true);
	
	event.Skip(false);
}

void SitplusMainWindow::LoadActivity (const char* fileName)
{
	// Try to load activity
	try {
		SmartPtr<spcore::IComponent> root= 
			sphost::ComponentHelper::SimpleParserFile (fileName);

		ActivityManager* actManager= new ActivityManager(this);
		actManager->Show();
		if (!actManager->SetActivity(root)) {
			actManager->Destroy();
			wxMessageDialog dlg(this, _("Initialization failed.\nSee console for details."), 
				_("Error loading activity"), wxOK);
			dlg.ShowModal();
		}

		// Connect child window close event
		actManager->Connect (wxEVT_CLOSE_WINDOW, wxCloseEventHandler(SitplusMainWindow::OnCloseChildWindow), NULL, this);

		// Disable activity menu entries
		EnableActivityOptions(false);
	}
	catch (std::exception& e) {
		wxString message= _("Creation of activity failed.\n") 
			+ wxString::FromUTF8(e.what());
		wxMessageDialog dlg(this, message, _("Error loading activity"), wxOK);
		dlg.ShowModal();
	}
}

void SitplusMainWindow::AppendLogMessage(ICoreRuntime::LogSeverityLevel severity, const wxString& msg)
{
	switch (severity) {
		case ICoreRuntime::LOG_FATAL: 
			m_txtLog->AppendText(_T("FATAL ERROR. "));
			break;
		case ICoreRuntime::LOG_ERROR:
			m_txtLog->AppendText(_T("ERROR. "));
			break;
		case ICoreRuntime::LOG_WARNING:
			m_txtLog->AppendText(_T("WARNING. "));
			break;
		case ICoreRuntime::LOG_INFO:
			m_txtLog->AppendText(_T("INFORMATION. "));
			break;
		case ICoreRuntime::LOG_DEBUG:
			m_txtLog->AppendText(_T("DEBUG. "));
			break;
		default: 
			assert (false);
	}
	m_txtLog->AppendText(msg);
}

void SitplusMainWindow::OnLogMessage( wxCommandEvent& event )
{
	AppendLogMessage((ICoreRuntime::LogSeverityLevel) event.GetInt(), event.GetString());
}

void SitplusMainWindow::LogMessage (ICoreRuntime::LogSeverityLevel severity, const char* message)
{
	wxString msg(message, wxConvUTF8);
	// Make sure there is a trailing new line
	if (msg[msg.size()-1]!= _T('\n')) msg+= _T('\n');

	if (wxIsMainThread()) AppendLogMessage(severity, msg);
	else {
		wxCommandEvent evt(wxEVT_LOCAL_LOG_MESSAGE);

		evt.SetString(msg);
		evt.SetInt((int) severity);
		wxPostEvent (this, evt);		
	}
}

/*
 * wxEVT_COMMAND_MENU_SELECTED event handler for ID_MENUITEM_CONFIG_CAMERA
 */

void SitplusMainWindow::OnMenuitemConfigCameraClick( wxCommandEvent& event )
{
	OpenConfigDialog("camera_config");
    event.Skip(false);
}


/*
 * wxEVT_COMMAND_MENU_SELECTED event handler for ID_MENUITEM_CONFIG_WIIMOTES
 */

void SitplusMainWindow::OnMenuitemConfigWiimotesClick( wxCommandEvent& event )
{
	OpenConfigDialog("wiimotes_config_gui");
    event.Skip(false);
}


/*
 * wxEVT_COMMAND_MENU_SELECTED event handler for ID_MENUITEM_CONFIG_MIDI
 */

void SitplusMainWindow::OnMenuitemConfigMidiClick( wxCommandEvent& event )
{
	OpenConfigDialog("midi_config_gui");
    event.Skip(false);
}


/*
 * wxEVT_COMMAND_MENU_SELECTED event handler for ID_MENUITEM_CONFIG_PD
 */

void SitplusMainWindow::OnMenuitemConfigPdClick( wxCommandEvent& event )
{
	vector<string> args;
	args.push_back("--data-dir");
	args.push_back(string(getSpCoreRuntime()->GetPaths().GetDataDir()) + "/pd");

	OpenConfigDialog("puredata_config", &args);
    event.Skip(false);
}

/*
 * wxEVT_COMMAND_MENU_SELECTED event handler for ID_MENUITEM_ACTIVITY_LOAD_FILE
 */

void SitplusMainWindow::OnMenuitemActivityLoadFileClick( wxCommandEvent& event )
{
	wxFileDialog filePick(this, _("Load activity script"), wxEmptyString, wxEmptyString, 
		_T("SITPLUS files (*.sp)|*.sp|All files|*.*"), wxFD_OPEN);

	if (filePick.ShowModal()== wxID_OK) {
		wxString fileName= filePick.GetPath();

		LoadActivity (fileName.utf8_str());
	}

    event.Skip(false);
}

/*
 * wxEVT_COMMAND_MENU_SELECTED event handler for ID_MENUITEM_ACTIVITY_START
 */

void SitplusMainWindow::OnMenuitemActivityStartClick( wxCommandEvent& event )
{
	ActivityLoader dlg(this);

	if (dlg.ShowModal()!= wxID_OK) return;

	std::string fileName(getSpCoreRuntime()->GetPaths().GetDataDir());
	fileName+= "/sp/";

	switch (dlg.GetSelection()) {
		case ActivityLoader::INPUT_CAM:
			fileName+= "cam_midi_collage.sp";
			break;
		case ActivityLoader::INPUT_VOICE:
			fileName+= "voice_collage.sp";
			break;
		case ActivityLoader::INPUT_WII:
			if (dlg.GetUseMotionPlus())
				fileName+= "wiimp_midi_collage.sp";
			else
				fileName+= "wiiacc_midi_collage.sp";
			break;
		case ActivityLoader::INPUT_CAM | ActivityLoader::INPUT_VOICE:
			fileName+= "cam_voice_midi_collage.sp";
			break;
		case ActivityLoader::INPUT_CAM | ActivityLoader::INPUT_WII:
			// TODO: script non available
			if (dlg.GetUseMotionPlus())
				fileName+= "cam_wiimp_voice_midi_collage.sp";
			else
				fileName+= "cam_wiiacc_voice_midi_collage.sp";
			break;
		case ActivityLoader::INPUT_WII | ActivityLoader::INPUT_VOICE:
			if (dlg.GetUseMotionPlus())
				fileName+= "wiimp_voice_midi_collage.sp";
			else
				fileName+= "wiiacc_voice_midi_collage.sp";
			break;
		case ActivityLoader::INPUT_CAM | ActivityLoader::INPUT_VOICE | ActivityLoader::INPUT_WII:
			if (dlg.GetUseMotionPlus())
				fileName+= "cam_wiimp_voice_midi_collage.sp";
			else
				fileName+= "cam_wiiacc_voice_midi_collage.sp";
			break;
		default:
			assert (false);
	}

	LoadActivity (fileName.c_str());


    event.Skip(false);
}


/*
 * wxEVT_COMMAND_MENU_SELECTED event handler for ID_MENUITEM_LANGUAGE
 */

void SitplusMainWindow::OnMenuitemLanguageClick( wxCommandEvent& event )
{
	string lang;

	if (LanguageSelectionDialog(lang)) {
		SaveLanguageToConfiguration(lang);

		wxMessageDialog dlg(this, _("You must restart SITPLUS to apply this change"), _T("SITPLUS"), wxICON_INFORMATION | wxOK);
		dlg.ShowModal();
	}
	
	event.Skip(false);
}


/*
 * wxEVT_COMMAND_MENU_SELECTED event handler for ID_MENUITEM_ABOUT
 */

void SitplusMainWindow::OnMenuitemAboutClick( wxCommandEvent&  )
{
////@begin wxEVT_COMMAND_MENU_SELECTED event handler for ID_MENUITEM_ABOUT in SitplusMainWindow.
    // Before editing this code, remove the block markers.
    About* window = new About(this);
    int returnValue = window->ShowModal();
    window->Destroy();
////@end wxEVT_COMMAND_MENU_SELECTED event handler for ID_MENUITEM_ABOUT in SitplusMainWindow. 
}


/*
 * wxEVT_COMMAND_MENU_SELECTED event handler for ID_MENUITEM_HELPHELP
 */

void SitplusMainWindow::OnMenuitemHelphelpClick( wxCommandEvent& event )
{
	wxLaunchDefaultBrowser(wxString(getSpCoreRuntime()->GetPaths().GetDataDir(), wxConvUTF8) + _T("/doc/manual.html"));

    event.Skip(false);
}


/*
 * wxEVT_CLOSE_WINDOW event handler for ID_MAIN_WINDOW
 */

void SitplusMainWindow::OnCloseWindow( wxCloseEvent& event )
{
	if (event.CanVeto()) {
		// Close children (if any)
		wxWindowList& wl= GetChildren();

		m_hasVetoed= false;
		
		wxWindowList::iterator it= wl.begin();
		for (; it!= wl.end() && !m_hasVetoed; ++it) {
			if ((*it)->IsKindOf(wxClassInfo::FindClass(_T("ActivityManager"))))
				(*it)->Close();
		}

		if (m_hasVetoed) return;
	}

	Destroy();
    event.Skip(false);
}

