//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
// copyright            : (C) 2008 by Eran Ifrah
// file name            : templateclassdlg.cpp
//
// -------------------------------------------------------------------------
// A
//              _____           _      _     _ _
//             /  __ \         | |    | |   (_) |
//             | /  \/ ___   __| | ___| |    _| |_ ___
//             | |    / _ \ / _  |/ _ \ |   | | __/ _ )
//             | \__/\ (_) | (_| |  __/ |___| | ||  __/
//              \____/\___/ \__,_|\___\_____/_|\__\___|
//
//                                                  F i l e
//
//    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 2 of the License, or
//    (at your option) any later version.
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////

#include <wx/textbuf.h>
#include <wx/dirdlg.h>
#include <wx/textfile.h>
#include "virtualdirectoryselector.h"
#include <wx/filefn.h>
#include "project.h"
#include "templateclassdlg.h"
#include <wx/msgdlg.h>
#include "swStringDb.h"
#include "snipwiz.h"
#include "swGlobals.h"
#include "imanager.h"

static const wxString swHeader = wxT( "header" );
static const wxString swSource = wxT( "source" );
static const wxString swPhClass = wxT( "%CLASS%" );

TemplateClassDlg::TemplateClassDlg( wxWindow* parent, SnipWiz *plugin, IManager *manager )
		: TemplateClassBaseDlg( parent )
		, m_plugin(plugin)
		, m_pManager(manager)
{
	Initialize();
}

void TemplateClassDlg::Initialize()
{
	// set tab sizes
	wxTextAttr attribs = m_textCtrlHeader->GetDefaultStyle();
	wxArrayInt	tabs = attribs.GetTabs();
	int tab = 70;
	for ( int i = 1; i < 20; i++ )
		tabs.Add( tab * i );
	attribs.SetTabs( tabs );
	m_textCtrlHeader->SetDefaultStyle( attribs );
	m_textCtrlImpl->SetDefaultStyle( attribs );

	GetStringDb()->Load( m_pluginPath + defaultTmplFile );

	wxArrayString templates;
	GetStringDb()->GetAllSets( templates );
	for ( wxUint32 i = 0; i < templates.GetCount(); i++ ) {
		m_comboxTemplates->AppendString( templates[i] );
		m_comboxCurrentTemplate->AppendString( templates[i] );
	}
	if ( templates.GetCount() ) {
		m_comboxTemplates->Select( 0 );
		wxString set = m_comboxTemplates->GetValue();
		m_textCtrlHeader->SetValue( GetStringDb()->GetString( set, swHeader ) );
		m_textCtrlImpl->SetValue( GetStringDb()->GetString( set, swSource ) );
		m_comboxCurrentTemplate->Select( 0 );
	}
	TreeItemInfo item = m_pManager->GetSelectedTreeItemInfo( TreeFileView );
	if ( item.m_item.IsOk() && item.m_itemType == ProjectItem::TypeVirtualDirectory ) {
		m_virtualFolder = VirtualDirectorySelector::DoGetPath( m_pManager->GetTree( TreeFileView ), item.m_item, false );
		m_projectPath =  item.m_fileName.GetPath( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR );
	}
	m_textCtrlVD->SetValue( m_virtualFolder );
	if(m_virtualFolder.IsEmpty() == false){
		m_staticProjectTreeFolder->SetForegroundColour(wxColour(0,128,0));
	}
	m_textCtrlFilePath->SetValue( m_projectPath );
	m_textCtrlClassName->SetFocus();
}

void TemplateClassDlg::OnClassNameEntered( wxCommandEvent& event )
{
	wxUnusedVar( event );
	wxString buffer = m_textCtrlClassName->GetValue();
	if ( buffer.IsEmpty() ) {
		m_textCtrlHeaderFile->SetValue(wxT(""));
		m_textCtrlCppFile->SetValue(wxT(""));
		return;
	} else {
		m_textCtrlHeaderFile->SetValue( buffer.Lower() + wxT( ".h" ) );
		m_textCtrlCppFile->SetValue( buffer.Lower() + wxT( ".cpp" ) );
	}
}

void TemplateClassDlg::OnBrowseVD( wxCommandEvent& event )
{
	wxUnusedVar( event );
	VirtualDirectorySelector dlg( this, m_pManager->GetWorkspace(), m_textCtrlVD->GetValue() );
	if ( dlg.ShowModal() == wxID_OK ) {
		m_textCtrlVD->SetValue( dlg.GetVirtualDirectoryPath() );
		m_staticProjectTreeFolder->SetForegroundColour(wxColour(0,128,0));
		m_staticProjectTreeFolder->Refresh();
	}
}

void TemplateClassDlg::OnBrowseFilePath( wxCommandEvent& event )
{
	wxUnusedVar( event );
	wxString dir = wxT("");
	if ( wxFileName::DirExists( m_projectPath ) ) {
		dir = m_projectPath;
	}

	dir = wxDirSelector( _("Select output folder"), dir, wxDD_DEFAULT_STYLE, wxDefaultPosition, this);
	if ( !dir.IsEmpty() ) {
		m_projectPath = dir;
		m_textCtrlFilePath->SetValue(m_projectPath);
	}
}

void TemplateClassDlg::OnGenerate( wxCommandEvent& event )
{
	wxUnusedVar(event);
	wxArrayString files;
	wxString newClassName = m_textCtrlClassName->GetValue();
	wxString baseClass = m_comboxCurrentTemplate->GetValue();

	if (!wxEndsWithPathSeparator(m_projectPath))
		m_projectPath += wxFILE_SEP_PATH;

	wxString buffer = GetStringDb()->GetString( baseClass, swHeader );
	buffer.Replace( swPhClass, newClassName );
	buffer.Replace(wxT("\v"), eol[m_curEol].c_str());

	files.Add( m_projectPath + m_textCtrlHeaderFile->GetValue() );
	SaveBufferToFile( files.Item(0), buffer );

	buffer = wxString::Format( wxT( "#include \"%s\"%s%s" ), m_textCtrlHeaderFile->GetValue().c_str(), eol[m_curEol].c_str(), eol[m_curEol].c_str() );
	buffer += GetStringDb()->GetString( baseClass, swSource );
	buffer.Replace( swPhClass, newClassName );
	buffer.Replace(wxT("\v"), eol[m_curEol].c_str());

	files.Add( m_projectPath + m_textCtrlCppFile->GetValue() );
	SaveBufferToFile( files.Item(1), buffer );

	if ( !m_textCtrlVD->GetValue().IsEmpty() ) {
		// Create the Success message first, as 'files' may be altered during creation
		wxString msg;
		msg << wxString::Format( wxT( "%s%s" ), files.Item(0).c_str(), eol[m_curEol].c_str())
		<< wxString::Format( wxT( "%s%s%s" ), files.Item(1).c_str(), eol[m_curEol].c_str(), eol[m_curEol].c_str())
		<< _( "Files successfully created." );
		// We have a .cpp and an .h file, and there may well be a :src and an :include folder available
		// So try to place the files appropriately. If that fails, dump both in the selected folder
		if ( m_pManager->AddFilesToVirtualFolderIntelligently( m_textCtrlVD->GetValue(), files )
				|| m_pManager->AddFilesToVirtualFolder( m_textCtrlVD->GetValue(), files ) ) {
			wxMessageBox(msg, _("Add template class"), wxOK|wxCENTER|wxICON_INFORMATION, this);
			EndModal(wxID_OK);
			return;
		}
	}

	wxMessageBox(_("Adding the template class failed"), _("Oops"), wxOK|wxCENTER|wxICON_ERROR, this);
	EndModal(wxID_CANCEL);	// The return value isn't actually used at present, but send Cancel on failure for future-proofing
}

void TemplateClassDlg::OnGenerateUI( wxUpdateUIEvent& event )
{
	wxUnusedVar( event );
	if ( m_comboxCurrentTemplate->GetSelection() == wxNOT_FOUND ||
	        m_textCtrlClassName->GetValue().IsEmpty() ||
	        m_textCtrlHeaderFile->GetValue().IsEmpty() ||
	        m_textCtrlCppFile->GetValue().IsEmpty() )
		event.Enable( false );
	else
		event.Enable( true );
}

void TemplateClassDlg::OnQuit( wxCommandEvent& event )
{
	wxUnusedVar(event);
	GetStringDb()->Save( m_pluginPath + defaultTmplFile );
	EndModal(wxID_CANCEL);
}

void TemplateClassDlg::OnTemplateClassSelected( wxCommandEvent& event )
{
	wxUnusedVar( event );
	wxString set = m_comboxTemplates->GetValue();
	if ( GetStringDb()->IsSet( set ) ) {
		m_textCtrlHeader->SetValue( GetStringDb()->GetString( set, swHeader ) );
		m_textCtrlImpl->SetValue( GetStringDb()->GetString( set, swSource) );
	}
}

void TemplateClassDlg::OnButtonAdd( wxCommandEvent& event )
{
	wxUnusedVar( event );
	wxString set = m_comboxTemplates->GetValue();
	bool isSet = GetStringDb()->IsSet( set );
	if ( isSet ) {
		int ret = wxMessageBox( _("Class exists!\nOverwrite?"), _("Add class"), wxYES_NO | wxICON_QUESTION );
		if ( ret == wxNO )
			return;
	}
	GetStringDb()->SetString( set, swHeader, m_textCtrlHeader->GetValue() );
	GetStringDb()->SetString( set, swSource, m_textCtrlImpl->GetValue() );
	if ( !isSet ) {
		m_comboxTemplates->AppendString( set );
	}

	RefreshTemplateList();
	m_modified = true;
}

void TemplateClassDlg::OnButtonAddUI( wxUpdateUIEvent& event )
{
	if ( m_comboxTemplates->GetValue().IsEmpty() || m_textCtrlHeader->GetValue().IsEmpty() || m_textCtrlImpl->GetValue().IsEmpty() )
		event.Enable( false );
	else
		event.Enable( true );
}

void TemplateClassDlg::OnButtonChange( wxCommandEvent& event )
{
	wxUnusedVar( event );
	wxString set = m_comboxTemplates->GetValue();
	bool isSet = GetStringDb()->IsSet( set );
	if ( !isSet ) {
		int ret = ::wxMessageBox( _("That class doesn't exist!\nTry again?"), _("Change class"), wxYES_NO | wxICON_QUESTION );
		if ( ret == wxNO )
			return;
	}
	GetStringDb()->SetString( set, swHeader, m_textCtrlHeader->GetValue() );
	GetStringDb()->SetString( set, swSource, m_textCtrlImpl->GetValue() );
	if ( !isSet )
		m_comboxTemplates->AppendString( set );
	RefreshTemplateList();
	m_modified = true;
}

void TemplateClassDlg::OnButtonChangeUI( wxUpdateUIEvent& event )
{
	if ( m_comboxTemplates->GetSelection() == wxNOT_FOUND )
		event.Enable( false );
	else
		event.Enable( true );
	if ( m_textCtrlHeader->IsModified() == false && m_textCtrlImpl->IsModified() == false )
		event.Enable( false );
}

void TemplateClassDlg::OnButtonRemove( wxCommandEvent& event )
{
	wxUnusedVar( event );
	wxString set = m_comboxTemplates->GetValue();
	bool isSet = GetStringDb()->IsSet( set );
	if ( !isSet ) {
		::wxMessageBox( wxT( "Class not found!\nNothing to remove." ), wxT( "Remove class" ) );
		return;
	}
	GetStringDb()->DeleteKey( set, swHeader );
	GetStringDb()->DeleteKey( set, swSource );
	long index = m_comboxTemplates->FindString( set );
	m_comboxTemplates->Delete( index );
	RefreshTemplateList();
	m_modified = true;
}

void TemplateClassDlg::OnButtonRemoveUI( wxUpdateUIEvent& event )
{
	if ( m_comboxTemplates->GetSelection() == wxNOT_FOUND )
		event.Enable( false );
	else
		event.Enable( true );
}

void TemplateClassDlg::OnButtonClear(wxCommandEvent& e)
{
	m_comboxTemplates->SetValue( wxT( "" ) );
	m_textCtrlImpl->SetValue( wxT( "" ) );
	m_textCtrlHeader->SetValue( wxT( "" ) );
}

void TemplateClassDlg::OnButtonClearUI(wxUpdateUIEvent& e)
{
	e.Enable(true);
}

void TemplateClassDlg::OnInsertClassKeyword( wxCommandEvent& event )
{
	wxUnusedVar( event );
	long from, to;

	if ( m_notebookFiles->GetSelection() == 0 ) {
		m_textCtrlHeader->GetSelection( &from, &to );
		m_textCtrlHeader->Replace( from, to, swPhClass );
		m_textCtrlHeader->SetFocus();
	} else {
		m_textCtrlImpl->GetSelection( &from, &to );
		m_textCtrlImpl->Replace( from, to, swPhClass );
		m_textCtrlImpl->SetFocus();
	}
}

void TemplateClassDlg::OnInsertClassKeywordUI( wxUpdateUIEvent& event )
{
	event.Enable(true);
}

void TemplateClassDlg::OnHeaderFileContentChnaged( wxCommandEvent& event )
{
	event.Skip();
}

void TemplateClassDlg::OnImplFileContentChnaged( wxCommandEvent& event )
{
	event.Skip();
}

swStringDb* TemplateClassDlg::GetStringDb()
{
	return m_plugin->GetStringDb();
}

void TemplateClassDlg::RefreshTemplateList()
{
	wxArrayString templates;
	GetStringDb()->GetAllSets( templates );
	m_comboxCurrentTemplate->Clear();

	for ( wxUint32 i = 0; i < templates.GetCount(); i++ ) {
		m_comboxCurrentTemplate->AppendString( templates[i] );
	}

	if ( templates.GetCount() ) {
		m_comboxCurrentTemplate->Select( 0 );
	}
}

bool TemplateClassDlg::SaveBufferToFile( const wxString filename, const wxString buffer, int eolType)
{
	wxTextFile file( filename );
	wxTextFileType tft = wxTextFileType_Dos;
	if ( file.Exists() ) {
		int ret = wxMessageBox( _( "File already exists!\n\n Overwrite?" ), _( "Generate class files" ), wxYES_NO | wxYES_DEFAULT | wxICON_EXCLAMATION );
		if ( ret == wxID_NO )
			return false;
	}
	switch ( m_curEol ) {
	case 0:
		tft = wxTextFileType_Dos;
		break;
	case 1:
		tft = wxTextFileType_Mac;
		break;
	case 2:
		tft = wxTextFileType_Unix;
		break;
	}
	file.Create();
	file.AddLine( buffer, tft );
	file.Write( tft );
	file.Close();
	return true;
}
