/**
    File        : Main.cpp
    Description : Initializes the app
    Author      : Raymond Marsh
    Created     : 08/01/2009
    Copyright 2009, 2010 Raymond Marsh

    This program is distributed under the terms of the GNU General Public License.
    To view a copy of the license see the file 'copying.txt'

    This file is part of the Ace Personal Finance Assistant Suite.

    Ace Personal Finance Assistant 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.

    Ace Personal Finance Assistant 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 Ace Personal Finance Assistant.
    If not, see <http://www.gnu.org/licenses/>.
**/

#include "Main.h"

//(*AppHeaders
#include <wx/xrc/xmlres.h>
#include <wx/image.h>
//*)

IMPLEMENT_APP(CApp);

/** \brief Sorts plugins by their listed weight
 *
 * \param A
 * \param B
 * \return bool
 *
 */
bool SortPluginsByWeight(const SPluginInfo& A, const SPluginInfo& B)
{
    return A.pPlugin->GetPluginWeight() < B.pPlugin->GetPluginWeight();
}

/** \brief Initializes the application
 *
 * \return true on success, false on failure
 *
 */
bool CApp::OnInit()
{
	//(*AppInitialize
	bool wxsOK = true;
	wxInitAllImageHandlers();
	wxXmlResource::Get()->InitAllHandlers();
	wxsOK = wxsOK && wxXmlResource::Get()->Load(_T("LanguageDlg.xrc"));
	wxsOK = wxsOK && wxXmlResource::Get()->Load(_T("SettingsDlg.xrc"));
	wxsOK = wxsOK && wxXmlResource::Get()->Load(_T("Dialogs.xrc"));
	//*)

    if (wxsOK)
    {
        // construct main window
        m_pMainFrame = new CMainFrame(g_AppName);
    }
    else
        aceMSGBOX(NULL, ace::MB_FAIL, _(".xrc Resources failed to load!"), NULL);
    return wxsOK;
}

void CMainFrame::OnTimerEnd(wxTimerEvent& event)
{
    if (m_IsOkToRun && m_pSplash->IsFinished() )
    {
        m_pSplash->End();
        this->Show(true);
        FinishStartup();
    }

}

/** \brief exits the application
 *
 * \return exit code
 *
 */
int CApp::OnExit()
{
//    ace::g_pOptions->SaveOptions();
    delete wxConfigBase::Set( (wxConfigBase*)NULL);

    return 0;
}




/** \brief Constructor
 *
 * \param title Title to be displayed on the window bar
 *
 * Constructs the main wxFrame
 *
 */
CMainFrame::CMainFrame(const wxString& title)
: wxFrame(NULL, 1, title, wxDefaultPosition, wxDefaultSize,
          wxDEFAULT_FRAME_STYLE | wxMAXIMIZE, "CMainFrame"),
  m_pAccountManager(NULL), m_pData(NULL), m_IsOkToRun(false)
{
    Maximize(true);

#ifndef _DEBUG
    // display splash screen
    wxString Version = wxString::Format(_("Version %i.%i.%i"), AutoVersion::MAJOR, AutoVersion::MINOR, AutoVersion::BUILD);
    wxString Revision = wxString::Format(_("Revision %i"), AutoVersion::REVISION);
    m_pSplash = new CSplashScreen(this, 5000, idTimer, Version, Revision);
    m_pSplash->Show(true);
#endif

#ifndef _DEBUG
    m_pSplash->SetStatusMsg(_("Loading Resources...") );
#endif

#ifndef _DEBUG
    // make sure proper directory structure is present
    wxString path = wxStandardPaths::Get().GetUserConfigDir() + "\\AcePFA";
    wxDir::Make(path, wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL);
#endif

    // set the application icon
    wxIcon icon1(_("Media/Images/logo16x16.ico"), wxBITMAP_TYPE_ICO);
    m_Icon.AddIcon(icon1);
    if (!m_Icon.IsEmpty() )
        SetIcons(m_Icon);
    else
        aceMSGBOX(this, ace::MB_ERROR, _("Could not load bmp"), NULL);

    // create menu bar
    wxMenuBar *pMenuBar = new wxMenuBar();

    // menu->file
    wxMenu *pMenuFile     = new wxMenu;
    pMenuFile->Append(idFileQuit, _("E&xit\tAlt-X"), _("Quit this program") );

    // menu->accounts
    wxMenu *pMenuAccounts = new wxMenu;
    pMenuAccounts->Append(idAccountNew, _("&New Account"), _("Add a new Account") );
    pMenuAccounts->Append(idAccountEdit, _("&Edit Account"), _("Edit Account Details") );
    pMenuAccounts->Append(idAccountDelete, _("&Delete Account"), _("Delete an Account") );
    pMenuAccounts->AppendSeparator();

    // menu->bills
    wxMenu *pMenuBills    = new wxMenu;
    pMenuBills->Append(idBillsView, _("&View Transactions"), _("View Reoccuring Transactions") );

    // menu->reports
    wxMenu *pMenuReports = new wxMenu;

    // menu->plugins
    wxMenu *pMenuPlugins = new wxMenu;
    pMenuPlugins->Append(idPluginManager, _("&Manage Plugins"), _("Enable or Disable Plugins") );

    // menu->settings
    wxMenu *pMenuSettings = new wxMenu;
    pMenuSettings->Append(idSettingsEnviroment, _("&Enviroment"), _("Change enviroment settings") );
    pMenuSettings->Append(idSettingsLanguage, _("&Languages"), _("Change language settings") );
    pMenuSettings->AppendSeparator();
    pMenuSettings->Append(idSettingsRestore, _("&Restore Defaults"), _("Restore all settings to default values") );

    // menu->help
    wxMenu *pMenuHelp = new wxMenu;

    wxMenuItem* pItemMyHelp = new wxMenuItem(NULL, idHelpUsing, _("&Help\tF1"), _("Get help using this program") );
    pItemMyHelp->SetBitmaps(wxBitmap(_("Media/Images/help16x16.bmp"), wxBITMAP_TYPE_BMP) );
    pMenuHelp->Append(pItemMyHelp);

    wxMenuItem* pItemMyAbout = new wxMenuItem(NULL, idHelpAbout, _("&About...\tF12"), _("About this program") );
    pItemMyAbout->SetBitmaps(wxBitmap(_("Media/Images/smalllogo16x16.bmp"), wxBITMAP_TYPE_BMP) );
    pMenuHelp->Append(pItemMyAbout);

    wxMenuItem* pItemWidgets = new wxMenuItem(NULL, idHelpWidgets, _("&wxWidgets...\tF11"), _("About wxWidgets") );
    pItemWidgets->SetBitmaps(wxBitmap(_("Media/Images/wxwidgets16x16.bmp"), wxBITMAP_TYPE_BMP) );
    pMenuHelp->Append(pItemWidgets);

    // add items to menu bar
    pMenuBar->Append(pMenuFile, _("&File") );
    pMenuBar->Append(pMenuAccounts, _("&Accounts") );
    pMenuBar->Append(pMenuBills, _("&Reoccuring Transactions") );
    pMenuBar->Append(pMenuReports, _("&Reports") );
    pMenuBar->Append(pMenuPlugins, _("&Plugins") );
    pMenuBar->Append(pMenuSettings, _("&Settings") );
    pMenuBar->Append(pMenuHelp, _("&Help") );
    SetMenuBar(pMenuBar);

    // create the status bar
    CreateStatusBar(2);
    SetStatusText(_(""), 0);
    SetStatusText(_("Welcome."), 1);

// TODO (USER#1#): load the bitmaps for the toolbar
//    wxBitmap BM_Back(_("data/images/arrow_back.bmp"), wxBITMAP_TYPE_BMP);

    // create the tool bar
    wxToolBar *pToolbar = CreateToolBar();
//    pToolbar->AddTool(IDT_BACK, _("back"), BM_Back, _("back") );
//    pToolbar->AddSeparator();
    pToolbar->Realize();

#ifndef _DEBUG
    m_pSplash->SetStatusMsg(_("Initializing Settings...") );
#endif

    // initialize options and configuration
    ace::g_pOptions = new ace::COptions();
    ace::g_pOptions->Initialize();
    ace::g_pOptions->LoadOptions();

    // initialize the data manager
    ace::g_pData = new ace::CData();
    m_pData = ace::g_pData;
    m_pData->Initialize();

#ifndef _DEBUG
    m_pSplash->SetStatusMsg(_("Loading Plugins...") );
#endif

    // find, load and initialize plugins
    if (EnumPlugins() <= 0)
    {
        aceMSGBOX(this, ace::MB_ERROR, _("No available account plugins!"), NULL);
        return;
    }

#ifndef _DEBUG
    m_pSplash->SetStatusMsg(_("Loading Accounts...") );
#endif

    // initialize account manager
    m_pAccountManager = new CAccountManager(this, (IPluginHost*)this);

    wxBoxSizer *pHSizer = new wxBoxSizer(wxHORIZONTAL);
    pHSizer->Add(m_pAccountManager, 1, wxALIGN_CENTER | wxALL | wxEXPAND, 20);
    SetSizer(pHSizer);

    // initialize reoccuring transactions
    m_pReoccuringTransactionDlg = new CReoccuringTransactionListDlg(this, m_pAccountManager);

    // set background color
    SetOwnBackgroundColour(ace::g_pOptions->COLORS.MainBGColor);

#ifndef _DEBUG
    // dismiss splash screen and show main window
    m_IsOkToRun = true;
    if (m_IsOkToRun && m_pSplash->IsFinished() )
    {
        m_pSplash->End();
        this->Show(true);
        FinishStartup();
    }
#else
    this->Show(true);
    FinishStartup();
#endif

    // attach handlers
    // timer
    Connect(idTimer, wxEVT_TIMER,
            wxTimerEventHandler(CMainFrame::OnTimerEnd) );
    // menu commands
    Connect(idFileQuit, wxEVT_COMMAND_MENU_SELECTED,
            wxCommandEventHandler(CMainFrame::OnQuit) );
    Connect(idAccountNew, wxEVT_COMMAND_MENU_SELECTED,
            wxCommandEventHandler(CAccountManager::OnNewAccount), NULL, m_pAccountManager);
    Connect(idAccountEdit, wxEVT_COMMAND_MENU_SELECTED,
            wxCommandEventHandler(CAccountManager::OnEditAccount), NULL, m_pAccountManager);
    Connect(idAccountDelete, wxEVT_COMMAND_MENU_SELECTED,
            wxCommandEventHandler(CAccountManager::OnDeleteAccount), NULL, m_pAccountManager);
    Connect(idBillsView, wxEVT_COMMAND_MENU_SELECTED,
            wxCommandEventHandler(CMainFrame::OnViewBills) );
    Connect(idPluginManager, wxEVT_COMMAND_MENU_SELECTED,
            wxCommandEventHandler(CMainFrame::OnManagePlugins) );
    Connect(idSettingsEnviroment, wxEVT_COMMAND_MENU_SELECTED,
            wxCommandEventHandler(CMainFrame::OnChangeEnviromentSettings) );
    Connect(idSettingsLanguage, wxEVT_COMMAND_MENU_SELECTED,
            wxCommandEventHandler(CMainFrame::OnChangeLanguageSettings) );
    Connect(idSettingsRestore, wxEVT_COMMAND_MENU_SELECTED,
            wxCommandEventHandler(CMainFrame::OnRestoreDefaultSettings) );
    Connect(idHelpUsing, wxEVT_COMMAND_MENU_SELECTED,
            wxCommandEventHandler(CMainFrame::OnHelp) );
    Connect(idHelpAbout, wxEVT_COMMAND_MENU_SELECTED,
            wxCommandEventHandler(CMainFrame::OnAbout) );
    Connect(idHelpWidgets, wxEVT_COMMAND_MENU_SELECTED,
            wxCommandEventHandler(CMainFrame::OnWidgets) );
    // event commands
    Connect(wxEVT_CLOSE_WINDOW, wxCommandEventHandler(CMainFrame::OnQuit) );
    // toolbar commands
    //Connect(IDT_FORWARD, wxEVT_COMMAND_TOOL_CLICKED ,
    //        wxCommandEventHandler(CHomeFrame::OnGoForward) );
}

/** \brief Destructor
 */
CMainFrame::~CMainFrame()
{
    UnloadAllPlugins();

    wxASSERT(ace::g_pData);
    delete ace::g_pData;
    ace::g_pData = NULL;
    m_pData = NULL;

    wxASSERT(ace::g_pOptions);
    delete ace::g_pOptions;
    ace::g_pOptions = NULL;
}

void CMainFrame::FinishStartup()
{
    // accept license if not already done so
    if (ace::g_pOptions->GLOBAL.FirstRun)
    {
        CLicenseDlg Dlg(this);
        if (Dlg.ShowModal() == wxID_CANCEL)
        {
            Close(true);
        }
    }

    // If this is the first time running, ask to create 1st account
    if (ace::g_pOptions->GLOBAL.FirstRun)
    {
        wxString msg = _("Welcome to the ACE Personal Finance Assistant.\n\n");
        msg += _("Before you can begin, you must set up an account.\n");
        msg += _("Press 'OK' to set up an account.\n");
        aceMSGBOX(this, ace::MB_INFO, msg, NULL);
        wxCommandEvent event;
        m_pAccountManager->OnNewAccount(event);
        ace::g_pOptions->GLOBAL.FirstRun = false;
        ace::g_pOptions->SaveOptions();
    }
}

/** \brief Searches for all plugins
 *
 * \return Number of plugins found
 *
 * Searches the Plugins directory for all *.dll plugin files
 *
 */
int CMainFrame::EnumPlugins()
{
    // find all .dll plugin files and attempt to load them
    wxArrayString Files;
#ifdef _DEBUG
    size_t num = wxDir::GetAllFiles(wxGetCwd() + _("/Plugins/debug"), &Files, _("*_d.dll"), wxDIR_FILES);
#else
    size_t num = wxDir::GetAllFiles(wxGetCwd() + _("/Plugins"), &Files, _("*.dll"), wxDIR_FILES);
#endif
	for(size_t i = 0; i < num; i++)
	{
        LoadPlugin(Files[i] );
	}

	// if any plugins were loaded then sort them by their weight
	if (!m_PluginList.empty() )
	{
    	sort(m_PluginList.begin(), m_PluginList.end(), SortPluginsByWeight);
	}

    return m_PluginList.size();
}

/** \brief Loads a single plugin from disk
 *
 * \param Filename Filename of the plugin to load
 * \return void
 *
 */
void CMainFrame::LoadPlugin(const wxString &Filename)
{
    SPluginInfo Map;

    // initialize the wxDynamicLibrary object and attempt to load the plugin
    Map.pLibLoader = new wxDynamicLibrary(Filename, wxDL_NOW);
    if(Map.pLibLoader->IsLoaded() )
    {
        // if loaded successfully, check for required symbols in the DLL
        if(Map.pLibLoader->HasSymbol(g_strFuncRegister) && Map.pLibLoader->HasSymbol(g_strFuncUnregister) )
        {
            // if the required symbols are found then retrive the function pointers to those symbols
            Map.pFuncRegisterPlugin = (DLLFunctionPtr)(Map.pLibLoader->GetSymbol(g_strFuncRegister) );
            Map.pFuncUnregisterPlugin = (DLLFunctionPtr)(Map.pLibLoader->GetSymbol(g_strFuncUnregister) );

            // attempt to register the plugin with the app
            // this will return a pointer to the plugin interface if successful
            if (Map.pFuncRegisterPlugin(&Map.pPlugin) )
            {
                // if successful, get the name of the plugin and check that it is not already loaded
                Map.PluginName = Map.pPlugin->GetPluginName();
                Map.PluginType = Map.pPlugin->GetPluginType();
                PLUGIN_LIST_ITER it;
                bool Bad = false;
                for (it = m_PluginList.begin(); it != m_PluginList.end(); ++it)
                {
                    // check if the plugin has already been loaded
                    if (it->PluginName == Map.PluginName)
                    {
                        wxString msg = _("An attempt to load the same plugin was made more than once");
                        aceMSGBOX(this, ace::MB_WARNING, msg, NULL);
                        Bad = true;
                    }

                    // check that the plugin version of wxWidgets is the same as the app's
                    if (Map.pPlugin->GetWxVersionString() != wxVERSION_STRING)
                    {
                        wxString msg;
                        msg = wxString::Format(_("'%s'\nPlugin uses an incompatable version of wxWidgets\nVersion should be %s"),
                                               Map.PluginName, wxVERSION_STRING);
                        aceMSGBOX(this, ace::MB_WARNING, msg, NULL);
                        Bad = true;
                    }

                    // unload the plugin if it was marked as bad
                    if (Bad)
                    {
                        // unregister and delete this instance of the plugin
                        Map.pFuncUnregisterPlugin(&Map.pPlugin);
                        delete Map.pLibLoader;
                        return;
                    }
                }

                // initialize the plugin and add it to the list of loaded plugins
                Map.pPlugin->InitializePlugin( (IPluginHost*)this);
                if (Map.PluginType = PT_ACCOUNTTYPE)
                {
                    m_pData->AddNewAccountType( ( (CAccountTypePluginBase*)Map.pPlugin)->GetPluginName() );
                }
                m_PluginList.push_back(Map);
            }
        }
        else
        {
            aceMSGBOX(this, ace::MB_ERROR, _("Required symbols not found"), NULL);
        }
    }
    else
    {
        aceMSGBOX(this, ace::MB_ERROR, wxString::Format(_("Could not load Plugin\n%s"), Filename), NULL);
        return;
    }
}

/** \brief Unloads all plugins loaded from previous calls to CMainFrame::LoadPlugin
 *
 * \return void
 *
 */
void CMainFrame::UnloadAllPlugins()
{
    PLUGIN_LIST_ITER it;
    for (it = m_PluginList.begin(); it != m_PluginList.end(); ++it)
    {
        if (it->pPlugin && it->pLibLoader)
        {
            it->pPlugin->ShutdownPlugin();
            it->pFuncUnregisterPlugin(&it->pPlugin);
            delete it->pLibLoader;
            it->pLibLoader            = NULL;
            it->pPlugin               = NULL;
            it->pFuncRegisterPlugin   = NULL;
            it->pFuncUnregisterPlugin = NULL;
        }
    }
}

/** \brief Find account viewer from the appropriate plugin
 *
 * \param ViewerName Name of the plugin to request the viewer from
 * \param ppViewer Pointer to store the viewer in
 * \return void
 *
 */
void CMainFrame::GetViewer(const wxString ViewerName, CAccountTypePluginBase** ppViewer)
{
    PLUGIN_LIST_ITER iter = m_PluginList.begin();
    for (iter = m_PluginList.begin(); iter != m_PluginList.end(); ++iter)
    {
        if (iter->PluginType == PT_ACCOUNTTYPE)
        {
            if (iter->PluginName == ViewerName)
            {
                *ppViewer = (CAccountTypePluginBase*)(iter->pPlugin);
            }
        }
    }
}

/** \brief Get a list of all account plugings available
 *
 * \param AccountTypes array to hold account types
 * \return false if no account types are available, true otherwise
 *
 */
bool CMainFrame::GetAvailableAccountTypes(wxArrayString& AccountTypes)
{
    PLUGIN_LIST_ITER iter;
    for (iter = m_PluginList.begin(); iter != m_PluginList.end(); ++iter)
    {
        if (iter->PluginType == PT_ACCOUNTTYPE)
        {
            AccountTypes.Add(iter->PluginName);
        }
    }
    if (AccountTypes.IsEmpty() )
        return false;
    return true;
}

/** \brief Event handler to exit the app
 *
 * \param event
 * \return void
 *
 */
void CMainFrame::OnQuit(wxCommandEvent& event)
{
// TODO (USER#1#): Ask Are you sure, save and backup!\

//    SaveOnExit();
//    m_pReoccuringTransactionDlg->EndModal(wxID_OK);
    Destroy();
}

/** \brief Event handler for bill view menu option
 *
 * \param event
 * \return void
 *
 */
void CMainFrame::OnViewBills(wxCommandEvent& event)
{
    m_pReoccuringTransactionDlg->Show(true);
}

/** \brief Event handler for plugin managment menu option
 *
 * \param event
 * \return void
 *
 */
void CMainFrame::OnManagePlugins(wxCommandEvent& event)
{
// TODO (USER#1#): Implement Plugin Manager
    aceMSGBOX(this, ace::MB_INFO, _("Sorry, this feature has not\nbeen implemented yet"), NULL);
}

/** \brief Event handler for enviroment settings menu option
 *
 * \param event
 * \return void
 *
 */
void CMainFrame::OnChangeEnviromentSettings(wxCommandEvent& event)
{
    CSettings Dlg(this);
    Dlg.ShowModal();
}

/** \brief Event handler for language settings menu option
 *
 * \param event
 * \return void
 *
 */
void CMainFrame::OnChangeLanguageSettings(wxCommandEvent& event)
{
    CLanguageDlg Dlg(this);
    if (Dlg.ShowModal() == wxID_OK)
    {
// TODO (USER#1#): Change language here!
    }
}

/** \brief Event handler restore settings to default values
 *
 * \param event
 * \return void
 *
 */
void CMainFrame::OnRestoreDefaultSettings(wxCommandEvent& event)
{
    if (aceMSGBOX(this, ace::MB_QUESTION, _("Change all settings back to default values?"), NULL) == wxID_YES)
    {
        ace::g_pOptions->RestoreDefaults();
        ace::g_pOptions->SaveOptions();
    }
}

/** \brief Event handler for help menu option
 *
 * \param event
 * \return void
 *
 */

void CMainFrame::OnHelp(wxCommandEvent& event)
{
// TODO (USER#1#): Add help feature
    aceMSGBOX(this, ace::MB_INFO, _("Sorry, this feature has not\nbeen implemented yet"), NULL);
}

/** \brief Event handler for about menu option
 *
 * \param event
 * \return void
 *
 */
void CMainFrame::OnAbout(wxCommandEvent& event)
{
        wxAboutDialogInfo AboutInfo;
        AboutInfo.AddArtist("Raymond Marsh");
        AboutInfo.AddDeveloper("Raymond Marsh");
        AboutInfo.AddDocWriter("Raymond Marsh");
        AboutInfo.SetCopyright(_("(C) 2009, 2010") );
        AboutInfo.SetDescription(_("A personal finance application to track your daily expenses") );
        AboutInfo.SetIcon(m_Icon.GetIcon(wxSize(32,32) ) );
        AboutInfo.SetLicense(_("GNU GPL 3.0") );
        AboutInfo.SetName(g_AppName);
        AboutInfo.SetVersion(g_AppVersion);
        AboutInfo.SetWebSite(_("https://acepfa.sourceforge.net") );

        wxAboutBox(AboutInfo, this);
}

/** \brief Event handler for about wxWidgets menu option
 *
 * \param event
 * \return void
 *
 */
void CMainFrame::OnWidgets(wxCommandEvent& event)
{
    wxInfoMessageBox(this);
}

