/**
    File        : AccountManager.cpp
    Description : Manages all user accounts
    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 "AccountManager.h"

CAccountManager::CAccountManager(wxWindow* pParent, IPluginHost* pHost)
: wxNotebook(pParent, idAccountManagerWindow),
  m_pParent(pParent), m_pHost(pHost), m_iterCurrentAccount(m_AccountList.begin() ),
  m_NumAccountsLoaded(0), m_LastTransferNumber(0), m_LastPageNumAssigned(-1)
{
    // set account list filename
#ifndef _DEBUG
    m_AccountListFilename = wxStandardPaths::Get().GetUserConfigDir();
    m_AccountListFilename += _("\\AcePFA\\accounts.adf");
#else
    m_AccountListFilename += _("c:\\Programming\\Ace_PFA_Suite\\AcePFA\\Data\\accounts.adf");
#endif

    SetBackgroundColour(wxColour(225, 225, 200) );

    m_NumAccountsLoaded = LoadAccountsFromDisk();

    switch (m_NumAccountsLoaded)
    {
        case -1:
// ADD (USER#1#): Error, could not load accounts, close app!!
        break;

        case 0:
        AddPage(new CStartPage(this), _("Start") );
        break;

        default:
        std::vector<CAccount*>::iterator iter;
        for (iter = m_AccountList.begin(); iter != m_AccountList.end(); ++iter)
        {
            wxString Label;
            (*iter)->GetAccountLabel(Label);
            AddPage(*iter, Label);
        }
        break;
    }
}

CAccountManager::~CAccountManager()
{
    SaveAccountsToDisk();
}

bool CAccountManager::RecordTransaction(STransaction T)
{
    CAccount* pAccount = GetAccountByName(T.AccountName);
    CNewTransactionDlg* dlg = new CNewTransactionDlg(pAccount, T, false, true);
    if (dlg->ShowModal() == wxID_OK)
    {
        pAccount->SaveTransaction(T, false);
        return true;
    }
    return false;
}

int CAccountManager::LoadAccountsFromDisk()
{
    CInFile iFile(m_AccountListFilename);
    if (iFile.IsOpen() )
    {
        SAccountInfo AI;
        while (iFile.ReadObject(AI) )
        {
            CAccount* pAccount = new CAccount(this, (CAccountManagerBase*)this, m_pHost, AI);
            m_AccountList.push_back(pAccount);
            m_AccountNames.Add(AI.AccountName);
            pAccount->LoadTransactionsFromDisk();
        }
    }

    return m_AccountList.size();
}

bool CAccountManager::SaveAccountsToDisk()
{
    // do not save anything if there are no accounts
    if (m_AccountList.empty() )
        return true;

    COutFile oFile(m_AccountListFilename);
    if (!oFile.IsOpen() )
    {
        aceLOGERROR("Cannot open output file", false);
        return false;
    }

    std::vector<CAccount*>::iterator iter;
    for (iter = m_AccountList.begin(); iter != m_AccountList.end(); ++iter)
    {
        oFile.WriteObject( (*iter)->GetAccountInfo() );
    }
    return true;
}

bool CAccountManager::HasMoreAccounts()
{
    if (m_iterCurrentAccount != m_AccountList.end() && !m_AccountList.empty() )
        return true;
    return false;
}

const CAccount* CAccountManager::GetNextAccount()
{
    if (m_iterCurrentAccount == m_AccountList.end() )
        m_iterCurrentAccount--;
    return *m_iterCurrentAccount++;
}

void CAccountManager::OnNewAccount(wxCommandEvent& event)
{
    wxDateTime Date = wxDateTime::Today();
    SAccountInfo AI =
    {
        _(""),
        _(""),
        _(""),
        _(""),
        _(""),
        0.00f,
        Date.FormatDate(),
        0.00f,
        0.00f,
        _(""),
    };

    CNewAccountWizard wizard( (wxWindow*)this, m_pHost, &AI, true);
    if (wizard.RunWizard(wizard.GetFirstPage() ) )
    {
        // if there are no accounts already set up, delete the start page
        if (m_NumAccountsLoaded == 0)
        {
            DeleteAllPages();
        }

        // add the account to the account list
        CAccount* pAccount = new CAccount(this, (CAccountManagerBase*)this, m_pHost, AI);
        m_AccountList.push_back(pAccount);
        m_AccountNames.Add(AI.AccountName);
//        SaveAccountsToDisk();

        // add a new tab for the new account to the notebook (account manager)
        wxString Label;
        pAccount->GetAccountLabel(Label);
        AddPage(pAccount, Label);
        ChangeSelection(GetPageCount() - 1);
        m_NumAccountsLoaded++;

        // add the starting balance to the transaction list and save it
        STransaction Transaction =
        {
            pAccount->GetNextTransactionNumber(),
            0,
            AI.AccountName,
            _(""),
            NULL,
            AI.DateOpened,
            TT_START,
            0,
            _("Starting Balance"),
            AI.OpeningBalance,
            _("Starting Balance"),
            _(""),
            _(""),
            TS_CLEARED,
            (AI.Balance < 0.00f) ? false : true,
            AI.Balance
        };
        pAccount->SaveTransaction(Transaction);
        pAccount->SaveTransactionsToDisk();
        SaveAccountsToDisk();
    }
}

void CAccountManager::OnDeleteAccount(wxCommandEvent& event)
{
    if (m_AccountList.empty() )
    {
        aceMSGBOX(this, ace::MB_INFO, _("You do not have any accounts to delete!"), NULL);
        return;
    }

    wxString AccountNameToDelete;
    wxString Msg = _("Select an Account to Delete");
    CSelectAccountDlg DeleteAccountDlg(this, Msg, m_AccountNames, &AccountNameToDelete);
    if (DeleteAccountDlg.ShowModal() == wxOK)
    {
        // confirm the action
        wxString msg = _("You are trying to delete the account '");
        msg += AccountNameToDelete;
        msg += _("'\n\nAre you sure you want to do this?\n\nYou will loose all information on this account");
        if (aceMSGBOX(this, ace::MB_QUESTION, msg, NULL) != wxID_YES)
            return;
        // remove the account name from the list of account names and the account list
        m_AccountNames.Remove(AccountNameToDelete);

        // remove the account from the account list and delete it from disk
        std::vector<CAccount*>::iterator i;
        for (i = m_AccountList.begin(); i != m_AccountList.end(); ++i)
        {
            if ( (*i)->GetAccountName() == AccountNameToDelete)
            {
                if (!wxRemoveFile( (*i)->GetFilename() ) )
                {
                    aceMSGBOX(this, ace::MB_ERROR, _("Could not remove account file!"), NULL);
                }
                m_AccountList.erase(i);
                break;
            }
        }
        // remove the account tab from the account manager
        const size_t NumOfPages = GetPageCount();
        for (size_t n = 0; n < NumOfPages; n++)
        {
            if (AccountNameToDelete == GetPageText(n) )
            {
                DeletePage(n);
                m_NumAccountsLoaded--;
                break;
            }
        }
        // save the remaining accounts to disk
        SaveAccountsToDisk();

        // if there are no more accounts loaded, then display the start page
        if (m_NumAccountsLoaded < 1)
        {
            AddPage(new CStartPage(this), _("Start") );
        }
    }
}

void CAccountManager::OnEditAccount(wxCommandEvent& event)
{
    if (m_AccountList.empty() )
    {
        aceMSGBOX(this, ace::MB_INFO, _("You do not have any accounts to Edit!"), NULL);
        return;
    }

    wxString AccountNameToEdit;
    wxString Msg = _("Select an Account to Edit");
    CSelectAccountDlg EditAccountDlg(this, Msg, m_AccountNames, &AccountNameToEdit);
    if (EditAccountDlg.ShowModal() == wxOK)
    {
        // find the selected account
        const size_t NumOfPages = GetPageCount();
        size_t n = 0;
        for (n = 0; n < NumOfPages; n++)
        {
            if (AccountNameToEdit == GetPageText(n) )
            {
                break;
            }
        }
        // get the selected account and save the current balance
        CAccount* pAccount = (CAccount*)GetPage(n);
        SAccountInfo AI = pAccount->GetAccountInfo();
        const double OldBalance = AI.OpeningBalance;

        // run the wizard
        CNewAccountWizard wizard( (wxWindow*)this, m_pHost, &AI, false);
        if (wizard.RunWizard(wizard.GetFirstPage() ) )
        {
            // update the balance if the starting balance changed
            if (AI.OpeningBalance != OldBalance)
            {
                pAccount->SaveAccountInfo(AI);
                STransaction* pTransaction = pAccount->GetTransaction(1);
                pTransaction->Amount = AI.OpeningBalance;
                pAccount->UpdateBalances();
                Refresh();
            }
        }
    }
}

CAccount* CAccountManager::GetAccountByName(wxString AccountNameToGet)
{
    std::vector<CAccount*>::iterator iter;
    for (iter = m_AccountList.begin(); iter != m_AccountList.end(); ++iter)
    {
        if ( (*iter)->GetAccountName() == AccountNameToGet)
        {
            return *iter;
        }
    }
    return NULL;
}

void CAccountManager::TransferFunds(const STransaction FromTransaction)
{
    CAccount* pToAccount = GetAccountByName(FromTransaction.TransferAccount);
    CAccount* pFromAccount = GetAccountByName(FromTransaction.AccountName);
    STransaction ToTransaction =
    {
        pToAccount->GetNextTransactionNumber(),
        FromTransaction.TransferNumber,
        pToAccount->GetAccountName(),
        FromTransaction.AccountName,
        pFromAccount,
        FromTransaction.Date,
        TT_TRANSFER,
        0,
        _("Transfer from ") + FromTransaction.AccountName,
        FromTransaction.Amount,
        _("Account Transfer"),
        _(""),
        FromTransaction.Comment,
        TS_OPEN,
        true
    };
    pToAccount->SaveTransaction(ToTransaction, true, false);
}

ULONG CAccountManager::SelectAccount(wxString* pToAccount, wxString Msg)
{
    CSelectAccountDlg SelectAccountDlg(this, Msg, GetAccountNames(), pToAccount);
    return SelectAccountDlg.ShowModal();
}

void CAccountManager::SetPageLabel(size_t& PageNum, wxString& Label)
{
    wxASSERT_MSG(SetPageText(PageNum, Label), _("Bad Page Number") );
}



///** class : CStartPage ****************************************************///



CStartPage::CStartPage(wxWindow* pParent)
: wxPanel(pParent, wxID_ANY)
{
    SetOwnBackgroundColour(wxColor(240, 240, 240) );
    Connect(wxEVT_PAINT, wxPaintEventHandler(CStartPage::OnPaint), NULL, this);
}

void CStartPage::OnPaint(wxPaintEvent& paint_event)
{
    wxPaintDC DC(this);

    DC.SetTextForeground(wxColor(200, 0, 0) );
    DC.SetFont(wxFont(18, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD) );
    DC.DrawText(_("To begin, create a new account from the 'Account' menu"), 0, 0);
}



///** class : CNewAccountDlg ************************************************///



CNewAccountDlg::CNewAccountDlg(CAccountManager* pParent, IPluginHost* pHost, SAccountInfo* pAI)
: wxDialog(pParent, wxID_ANY, _("Add New Account"), wxDefaultPosition, wxSize(300, 400) ),
  m_pParent(pParent), m_pHost(pHost), m_pAI(pAI)
{
    // get available account types
    wxArrayString AccountTypes;
    pHost->GetAvailableAccountTypes(AccountTypes);

    // set starting balance and statement info
    m_StartingBalance = _("0.00");
    pAI->Balance = 0.00f;
    pAI->LastStatementBalance = 0.00f;
    pAI->LastStatementDate = _("01/01/1900");

    // add controls to the dialog
    wxGridSizer* pGSizer = new wxGridSizer(0, 2, 5, 5);
    pGSizer->Add(new wxStaticText(this, wxID_ANY, _("Account Name") ),
                 1, wxALIGN_CENTER | wxALL | wxEXPAND, 3);
    pGSizer->Add(new wxTextCtrl(this,
                                idAccountNameCtrl,
                                _(""),
                                wxDefaultPosition,
                                wxDefaultSize,
                                0,
                                wxTextValidator(wxFILTER_ASCII,
                                                &(pAI->AccountName) ) ),
                 1, wxEXPAND | wxTOP | wxBOTTOM, 3);
    pGSizer->Add(new wxStaticText(this, wxID_ANY, _("Account Number") ),
                 1, wxALIGN_CENTER | wxALL | wxEXPAND, 3);
    pGSizer->Add(new wxTextCtrl(this,
                                idAccountNumberCtrl,
                                _(""),
                                wxDefaultPosition,
                                wxDefaultSize,
                                0,
                                wxTextValidator(wxFILTER_ASCII,
                                                &(pAI->AccountNumber) ) ),
                 1, wxEXPAND | wxTOP | wxBOTTOM, 3);
    pGSizer->Add(new wxStaticText(this, wxID_ANY, _("Account Type") ),
                 1, wxALIGN_CENTER | wxALL | wxEXPAND, 3);
    pGSizer->Add(new wxComboBox(this,
                                idAccountTypeCtrl,
                                _(""),
                                wxDefaultPosition,
                                wxDefaultSize,
                                AccountTypes,
                                wxCB_READONLY,
                                wxGenericValidator(&(pAI->AccountType) ) ),
                 1, wxEXPAND | wxTOP | wxBOTTOM, 3);
    pGSizer->Add(new wxStaticText(this, wxID_ANY, _("Bank Name") ),
                 1, wxALIGN_CENTER | wxALL | wxEXPAND, 3);
    pGSizer->Add(new wxTextCtrl(this,
                                idAccountNumberCtrl,
                                _(""),
                                wxDefaultPosition,
                                wxDefaultSize,
                                0,
                                wxTextValidator(wxFILTER_ASCII,
                                                &(pAI->BankName) ) ),
                 1, wxEXPAND | wxTOP | wxBOTTOM, 3);
    pGSizer->Add(new wxStaticText(this, wxID_ANY, _("Bank Phone Number") ),
                 1, wxALIGN_CENTER | wxALL | wxEXPAND, 3);
    pGSizer->Add(new wxTextCtrl(this,
                                idAccountNumberCtrl,
                                _(""),
                                wxDefaultPosition,
                                wxDefaultSize,
                                0,
                                wxTextValidator(wxFILTER_ASCII,
                                                &(pAI->BankPhoneNumber) ) ),
                 1, wxEXPAND | wxTOP | wxBOTTOM, 3);
    pGSizer->Add(new wxStaticText(this, wxID_ANY, _("Date Account Opened") ),
                 1, wxALIGN_CENTER | wxALL | wxEXPAND, 3);
    pGSizer->Add(m_pDateOpenedCtrl = new wxDatePickerCtrl(this,
                                                          idDateOpenedCtrl,
                                                          wxDateTime::Today(),
                                                          wxDefaultPosition,
                                                          wxDefaultSize,
                                                          wxDP_DROPDOWN,
                                                          wxDefaultValidator),
                 1, wxEXPAND | wxTOP | wxBOTTOM, 3);
    pGSizer->Add(new wxStaticText(this, wxID_ANY, _("Opening Balance") ),
                 1, wxALIGN_CENTER | wxALL | wxEXPAND, 3);
    pGSizer->Add(new wxTextCtrl(this,
                                idAccountNumberCtrl,
                                _("0.00"),
                                wxDefaultPosition,
                                wxDefaultSize,
                                0,
                                wxTextValidator(wxFILTER_ASCII,
                                                &m_StartingBalance) ),
                 1, wxEXPAND | wxTOP | wxBOTTOM, 3);

    // buttons
    wxStdDialogButtonSizer* pBtnSizer = new wxStdDialogButtonSizer();
    pBtnSizer->AddButton(new wxButton(this, wxID_SAVE, _("Save") ) );
    pBtnSizer->AddButton(new wxButton(this, wxID_CANCEL, _("Cancel") ) );
    pBtnSizer->Realize();

    // put everything together
    wxBoxSizer* pVSizer = new wxBoxSizer(wxVERTICAL);
    pVSizer->Add(pGSizer, 5, wxALIGN_CENTER | wxALL | wxEXPAND, 10);
    pVSizer->Add(new wxStaticLine(this), 0, wxALIGN_CENTER | wxALL | wxEXPAND, 5);
    pVSizer->Add(pBtnSizer, 1, wxALIGN_CENTER | wxALL | wxEXPAND, 10);
    SetSizer(pVSizer);

    Connect(wxID_SAVE, wxEVT_COMMAND_BUTTON_CLICKED,
            wxCommandEventHandler(CNewAccountDlg::OnSave) );
    Connect(wxID_CANCEL, wxEVT_COMMAND_BUTTON_CLICKED,
            wxCommandEventHandler(CNewAccountDlg::OnCancel) );

    Centre();
}

void CNewAccountDlg::OnSave(wxCommandEvent& event)
{
    // if there is already an account with this name then decline it
    const wxArrayString Names = m_pParent->GetAccountNames();
    if (Names.Index(m_pAI->AccountName) != wxNOT_FOUND)
    {
        aceMSGBOX(this, ace::MB_INFO, _("You already have an account with that name!"), NULL);
        return;
    }

    // get the data from the controls
    if (Validate() && TransferDataFromWindow() )
    {
        // make sure there is an account name entered
        if (m_pAI->AccountName.IsEmpty() )
        {
            aceMSGBOX(this, ace::MB_INFO, _("You forgot to enter an account name!"), NULL);
            return;
        }
        // make sure there is an account type entered
        if (m_pAI->AccountType.IsEmpty() )
        {
            aceMSGBOX(this, ace::MB_INFO, _("You forgot to enter an account type!"), NULL);
            return;
        }
        // make sure dollar amount is an accepted value (x.xx)
//        CDataBase* pData = m_pHost->GetGlobalDataObject();
        if (!(ace::g_pData->VerifyCorrectAmountFormat(m_StartingBalance) ) )
        {
            aceMSGBOX(this, ace::MB_INFO, _("You did not enter a correct dollar amount.\nFormat is x.xx"), NULL);
            return;
        }
        // get the balance and the date
        m_StartingBalance.ToDouble(&(m_pAI->Balance) );
        m_pAI->DateOpened = (m_pDateOpenedCtrl->GetValue() ).Format(_("%m/%d/%Y") );
    }

    EndModal(wxOK);
}

void CNewAccountDlg::OnCancel(wxCommandEvent& event)
{
    EndModal(wxCANCEL);
}



///** class : CSelectAccountDlg *********************************************///



CSelectAccountDlg::CSelectAccountDlg(CAccountManager* pParent, wxString Msg, wxArrayString AccountNames, wxString* pAccountName)
: wxDialog(pParent, wxID_ANY, _("Select an Account"), wxDefaultPosition, wxSize(300, 300) ),
  m_pParent(pParent), m_pAccountName(pAccountName), m_pAccountListCtrl(NULL)
{
    *m_pAccountName = _("");
    // add controls to the dialog
    wxBoxSizer* pVSizer = new wxBoxSizer(wxVERTICAL);
    pVSizer->Add(new wxStaticText(this, wxID_ANY, Msg),
                 1, wxALIGN_CENTER | wxALL, 10);
    pVSizer->Add(m_pAccountListCtrl = new wxListBox(this,
                                                    idAccountListCtrl,
                                                    wxDefaultPosition,
                                                    wxDefaultSize,
                                                    AccountNames,
                                                    wxLB_SINGLE),
                 2, wxALIGN_CENTER | wxALL, 10);
    // buttons
    wxStdDialogButtonSizer* pBtnSizer = new wxStdDialogButtonSizer();
    pBtnSizer->AddButton(new wxButton(this, wxID_OK, _("OK") ) );
    pBtnSizer->AddButton(new wxButton(this, wxID_CANCEL, _("Cancel") ) );
    pBtnSizer->Realize();

    // put everything together
    pVSizer->Add(new wxStaticLine(this), 0, wxALIGN_CENTER | wxALL | wxEXPAND, 5);
    pVSizer->Add(pBtnSizer, 1, wxALIGN_CENTER | wxALL | wxEXPAND, 10);
    SetSizer(pVSizer);

    Connect(wxID_OK, wxEVT_COMMAND_BUTTON_CLICKED,
            wxCommandEventHandler(CSelectAccountDlg::OnOK) );
    Connect(wxID_CANCEL, wxEVT_COMMAND_BUTTON_CLICKED,
            wxCommandEventHandler(CSelectAccountDlg::OnCancel) );

    Centre();
}

void CSelectAccountDlg::OnOK(wxCommandEvent& event)
{
    // get the account selected
    wxString SelectedAccount = m_pAccountListCtrl->GetStringSelection();
    if (SelectedAccount.IsEmpty() )
    {
        aceMSGBOX(this, ace::MB_INFO, _("You must select an account\nor click 'Cancel'"), NULL);
        return;
    }

    *m_pAccountName = SelectedAccount;
    EndModal(wxOK);
}

void CSelectAccountDlg::OnCancel(wxCommandEvent& event)
{
    EndModal(wxCANCEL);
}
