/**
    File        : ReoccuringTransactions.cpp
    Description : Lists and manages reoccuring transactions
    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 "ReoccuringTransactions.h"

bool SortTransactionsByDate(const SReoccuringTransaction& A, const SReoccuringTransaction& B)
{
    bool result = false;
    struct SDate { long month; long day; long year; } a, b;

    wxStringTokenizer ST(A.NextOccurance, _("/"), wxTOKEN_RET_EMPTY);
    (ST.GetNextToken() ).ToLong(&a.month);
    (ST.GetNextToken() ).ToLong(&a.day);
    (ST.GetNextToken() ).ToLong(&a.year);
    ST.SetString(B.NextOccurance, _("/"), wxTOKEN_RET_EMPTY);
    (ST.GetNextToken() ).ToLong(&b.month);
    (ST.GetNextToken() ).ToLong(&b.day);
    (ST.GetNextToken() ).ToLong(&b.year);

    if (a.year == b.year)
    {
        if (a.month == b.month)
            result = a.day < b.day;
        else
            result = a.month < b.month;
    }
    else
        result = a.year < b.year;
    return result;
}

bool UpdateNextOccurance(const wxString& strLast, const ULONG Frequency, wxString& strNext)
{
    wxDateTime Date;
    Date.ParseFormat(strLast, _("%m/%d/%Y") );
    switch (Frequency)
    {
        case BO_ONCE:
        return false;

        case BO_DAILY:
        Date += wxDateSpan::Day();
        break;

        case BO_WEEKLY:
        Date += wxDateSpan::Week();
        break;

        case BO_BIWEEKLY:
        Date += wxDateSpan::Weeks(2);
        break;

        case BO_TWICEMONTHLY:
        Date += wxDateSpan::Days(15);
        break;

        case BO_MONTHLY:
        Date += wxDateSpan::Month();
        break;

        case BO_QUARTERLY:
        Date += wxDateSpan::Months(3);
        break;

        case BO_TWICEANNUALLY:
        Date += wxDateSpan::Months(6);
        break;

        case BO_ANNUALLY:
        Date += wxDateSpan::Year();
        break;

        default:
        wxFAIL_MSG(_("Incorrect Reoccurance frequency") );
    }
    strNext = Date.Format(_("%m/%d/%Y") );
    return true;
}


///** class CHeaderPanel ****************************************************///


CHeaderPanel::CHeaderPanel(wxWindow* pParent, wxWindowID id)
: wxPanel(pParent, id), m_pParent(pParent)
{
    SetOwnBackgroundColour(wxColour(240, 240, 240) );
    Connect(wxEVT_PAINT, wxPaintEventHandler(CHeaderPanel::OnPaint), NULL, this);
}

CHeaderPanel::~CHeaderPanel()
{

}

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

    const int offset = 0;

    // draw the column lables
    DC.DrawText(_("Transaction"),   0 + offset, 20);
    DC.DrawText(_("Type"),        300 + offset, 20);
//    DC.DrawText(_("Last Transaction"), 450 + offset,  7);
//    DC.DrawText(_("Amount"),           450 + offset, 20);
    DC.DrawText(_("Days Until"),  575 + offset,  7);
    DC.DrawText(_("Due"),         575 + offset, 20);
    DC.DrawText(_("Next"),        700 + offset,  7);
    DC.DrawText(_("Due Date"),    700 + offset, 20);
    DC.DrawText(_("Next"),        825 + offset,  7);
    DC.DrawText(_("Amount Due"),  825 + offset, 20);
}


///** class : CTransactionListPanel *****************************************///


CTransactionListPanel::CTransactionListPanel(wxWindow* pParent, CReoccuringTransactionListDlg* pParentDlg, wxWindowID id)
: wxScrolledWindow(pParent, id, wxDefaultPosition, wxDefaultSize),
  m_pParent(pParent), m_pParentDlg(pParentDlg), m_ID(id), m_yCount(0), m_pCurrentTransaction(NULL)
{
    SetOwnBackgroundColour(wxColour(255, 255, 255) );

    // set line sizes
    wxClientDC DC(this);

    wxCoord w, h, d, e;
    DC.GetTextExtent(_("Yy"), &w, &h, &d, &e);
    m_LineHeight = (int)(h + d + e + 6);

    wxSize ClientSize = GetClientSize();
    m_LinesPerScreen = ClientSize.GetHeight() / m_LineHeight;

    // set the scroll bar position
    SetScrollPosition();

    // attach handlers
    // event handlers
    Connect(wxEVT_LEFT_DCLICK,
            wxMouseEventHandler(CTransactionListPanel::OnTransactionLeftDoubleClick) );
    Connect(wxEVT_LEFT_UP,
            wxMouseEventHandler(CTransactionListPanel::OnTransactionLeftClick) );
    Connect(wxEVT_RIGHT_UP,
            wxMouseEventHandler(CTransactionListPanel::OnTransactionRightClick) );
    // popup command menu from grid
    Connect(idEditTransaction, wxEVT_COMMAND_MENU_SELECTED,
            wxCommandEventHandler(CTransactionListPanel::OnViewTransaction) );
    Connect(idDeleteTransaction, wxEVT_COMMAND_MENU_SELECTED,
            wxCommandEventHandler(CTransactionListPanel::OnDeleteTransaction) );
    Connect(idRecordTransaction, wxEVT_COMMAND_MENU_SELECTED,
            wxCommandEventHandler(CTransactionListPanel::OnRecordTransaction) );
}

CTransactionListPanel::~CTransactionListPanel()
{

}

void CTransactionListPanel::SetScrollPosition()
{
    wxClientDC DC(this);
    OnDraw(DC);
    int NumLines = (int)m_pParentDlg->GetNumberOfTransactions() + 1;
    SetScrollbars(0, m_LineHeight, 0, NumLines, 0);
}

void CTransactionListPanel::OnDraw(wxDC &DC)
{
    wxCoord xCoord = 0, yCoord = 0;
    int yPos = 0;
    int LineYPos = 0;
    m_yCount = 0;
    m_LineMap.clear();
    wxColor ForegroundColor(0, 0, 0);

    DC.SetTextForeground(ForegroundColor);

    DC.GetSize(&xCoord, &yCoord);

    m_pParentDlg->ResetTransactionCounter();
    while (m_pParentDlg->HasMoreTransactions() )
    {
        const SReoccuringTransaction ThisTransaction = m_pParentDlg->GetNextTransaction();

        // get the line position to draw on
        yPos = (m_yCount * m_LineHeight);

        // determine the foreground color to use
        if (m_pCurrentTransaction)
        {
            if (m_pCurrentTransaction->Number == ThisTransaction.Number)
            {
                ForegroundColor = wxColor(0, 0, 200);
            }
            else
            {
                ForegroundColor = wxColor(0, 0, 0);
            }
        }
        else
        {
            ForegroundColor = wxColor(0, 0, 0);
        }
        DC.SetTextForeground(ForegroundColor);

        // draw the line
        wxString Type = ace::g_pData->GetTransactionTypeString(ThisTransaction.TransactionType);
        wxString EstAmount = wxString::Format(_("%1.2f"), ThisTransaction.EstAmount);
        DC.DrawText(ThisTransaction.Name,            0, yPos);
        DC.DrawText(Type,                          300, yPos);
        DC.DrawText(ThisTransaction.NextOccurance, 700, yPos);
        DC.DrawText(EstAmount,                     825, yPos);

        // draw a seperating line
        LineYPos = (yPos + m_LineHeight) - 5;
        DC.SetPen(wxPen(wxColor(200, 200, 200) ) );
        DC.DrawLine(0, LineYPos, xCoord, LineYPos);

        m_yCount++;

        // associate the line number with the transaction number
        m_LineMap.insert(std::make_pair(m_yCount, ThisTransaction.Number) );
    }
}

bool CTransactionListPanel::HitTest(wxMouseEvent& event, SReoccuringTransaction** ppTransaction)
{
    ULONG RowNumber = 0;
    *ppTransaction = NULL;
    int xStartPos = 0, yStartPos = 0;
    GetViewStart(&xStartPos, &yStartPos);

    const int MaxPosition = m_yCount * m_LineHeight;

    wxPoint PointClickedOn = event.GetPosition();
    if (PointClickedOn.y > MaxPosition)
    {
        return false;
    }
    RowNumber = ( (PointClickedOn.y / m_LineHeight)  + yStartPos) + 1;
    const std::map<ULONG, ULONG>::iterator iter = m_LineMap.find(RowNumber);
    if (iter == m_LineMap.end() )
    {
        wxFAIL_MSG(_("Line Not Found!") );
        return false;
    }

    *ppTransaction = m_pParentDlg->GetTransaction(iter->second);
    if (!(*ppTransaction) )
    {
        wxFAIL_MSG(_("Transaction Not Found!") );
        return false;
    }
    return true;
}

void CTransactionListPanel::OnTransactionLeftClick(wxMouseEvent& event)
{
    SReoccuringTransaction* pTransaction = NULL;
    if (!HitTest(event, &pTransaction) )
    {
        return;
    }
    if (pTransaction == NULL || pTransaction == m_pCurrentTransaction)
    {
        m_pCurrentTransaction = NULL;
    }
    else
    {
        m_pCurrentTransaction = pTransaction;
    }
    Refresh();
}

void CTransactionListPanel::OnTransactionLeftDoubleClick(wxMouseEvent& event)
{
    SReoccuringTransaction* pTransaction = NULL;
    if (!HitTest(event, &pTransaction) )
    {
        return;
    }
    m_pCurrentTransaction = pTransaction;
    CViewReoccuringTransactionDlg* pDlg = new CViewReoccuringTransactionDlg(m_pParentDlg, *pTransaction, m_pParentDlg->GetAccountNames(), false, true);
    pDlg->ShowModal();
    Refresh();
}

void CTransactionListPanel::OnTransactionRightClick(wxMouseEvent& event)
{
    SReoccuringTransaction* pTransaction = NULL;
    if (!HitTest(event, &pTransaction) )
    {
        return;
    }
    m_pCurrentTransaction = pTransaction;
    Refresh();

    // create the command popup window
    wxMenu *pCommandMenu = new wxMenu(wxString::Format(_("Transaction : %s"), pTransaction->Name) );
    pCommandMenu->Append(idRecordTransaction, _("Record Transaction") );
    pCommandMenu->Append(idEditTransaction, _("Edit Transaction Details") );
    pCommandMenu->Append(idDeleteTransaction, _("Delete This Transaction") );

    PopupMenu(pCommandMenu);
}

void CTransactionListPanel::OnViewTransaction(wxCommandEvent& event)
{
    if (m_pCurrentTransaction == NULL)
    {
        aceMSGBOX(this, ace::MB_INFO, _("You must first select a transaction!"), NULL);
        return;
    }
    CViewReoccuringTransactionDlg* pDlg = new CViewReoccuringTransactionDlg(m_pParentDlg, *m_pCurrentTransaction, m_pParentDlg->GetAccountNames(), false, true);
    pDlg->ShowModal();
    Refresh();
}

void CTransactionListPanel::OnDeleteTransaction(wxCommandEvent& event)
{
    if (m_pCurrentTransaction == NULL)
    {
        aceMSGBOX(this, ace::MB_INFO, _("You must first select a transaction!"), NULL);
        return;
    }
    m_pParentDlg->DeleteTransaction(m_pCurrentTransaction->Number);
    Refresh();
}

void CTransactionListPanel::OnRecordTransaction(wxCommandEvent& event)
{
    if (m_pCurrentTransaction == NULL)
    {
        aceMSGBOX(this, ace::MB_INFO, _("You must first select a transaction!"), NULL);
        return;
    }

    // convert reoccuring transaction to a transaction
    wxString FromAccount, ToAccount;
    bool IsDeposit = false;
    switch (m_pCurrentTransaction->Type)
    {
        case RT_TRANSFER:
        ToAccount = m_pCurrentTransaction->DepositToAccountName;

        case RT_DEBIT:
        FromAccount = m_pCurrentTransaction->PayFromAccountName;
        break;

        case RT_DEPOSIT:
        FromAccount = m_pCurrentTransaction->DepositToAccountName;
        IsDeposit = true;
        break;

        default:
        wxFAIL_MSG(_("Incorrect Reoccuring Transaction Type") );
    }

    CAccountBase* pToAccount = m_pParentDlg->GetAccountByName(ToAccount);
    STransaction Transaction =
    {
        0,
        0,
        FromAccount,
        ToAccount,
        (ToAccount.IsEmpty() ) ? NULL : pToAccount,
        m_pCurrentTransaction->NextOccurance,
        m_pCurrentTransaction->TransactionType,
        0,
        m_pCurrentTransaction->Name,
        m_pCurrentTransaction->EstAmount,
        m_pCurrentTransaction->Category,
        m_pCurrentTransaction->SubCategory,
        _(""),
        TS_OPEN,
        IsDeposit,
        0
    };

    // send trasaction to account manager to be recorded
    if (!m_pParentDlg->RecordNewTransaction(Transaction) )
    {
        return;
    }

    // update the reoccuring transaction
    m_pCurrentTransaction->LastOccurance = Transaction.Date;
    wxString Next;
    if (!UpdateNextOccurance(m_pCurrentTransaction->LastOccurance, m_pCurrentTransaction->Frequency, Next) )
    {
        m_pParentDlg->DeleteCurrentTransaction();
        Refresh();
        return;
    }
    m_pCurrentTransaction->NextOccurance = Next;
    m_pCurrentTransaction->LastAmount = Transaction.Amount;

    // update reoccuring transaction list
    m_pParentDlg->SortTransactions();
    Refresh();
}


///** class : CReoccuringTransactionListDlg *********************************///


CReoccuringTransactionListDlg::CReoccuringTransactionListDlg(wxWindow* pParent, CAccountManagerBase* pAM)
: wxDialog(pParent, wxID_ANY, _("Reoccuring Transactions"), wxDefaultPosition, wxSize(1150, 950), wxCAPTION),
  m_pParent(pParent), m_pAM(pAM), m_iCurrentTransaction(m_vTransactionList.end() ), m_LastTransactionNumber(0)
{
    // set reoccuring transactions filename
#ifndef _DEBUG
    m_Filename = wxStandardPaths::Get().GetUserConfigDir();
    m_Filename += _("\\AcePFA\\ReoccuringTransactions.adf");
#else
    m_Filename = _("Data\\ReoccuringTransactions.adf");
#endif

    // get list of account names from the account manager
    m_AccountNames = m_pAM->GetAccountNames();

    // load all reoccuring transactions from disk
    if (!LoadTransactionsFromDisk() )
        aceMSGBOX(this, ace::MB_ERROR, _("Could not load reoccuring transactions from disk!"), NULL);

    // left panel
    wxPanel* pLeftPanel = new wxPanel(this, wxID_ANY);
    pLeftPanel->SetOwnBackgroundColour(wxColour(240, 240, 240) );
    wxBoxSizer *pLVSizer = new wxBoxSizer(wxVERTICAL);
    pLVSizer->Add(new wxButton(pLeftPanel, idAddBtn, _("Add New Transaction") ), 1, wxALL | wxEXPAND, 10);
    pLVSizer->Add(new wxButton(pLeftPanel, idViewBtn, _("View Transaction") ), 1, wxALL | wxEXPAND, 10);
    pLVSizer->Add(new wxButton(pLeftPanel, idDeleteBtn, _("Delete Transaction") ), 1, wxALL | wxEXPAND, 10);
    pLVSizer->Add(new wxStaticLine(pLeftPanel), 0, wxTOP | wxBOTTOM | wxEXPAND, 20);
    pLVSizer->Add(new wxStaticText(pLeftPanel, wxID_ANY, _("Show transactions due within") ), 1, wxALL | wxEXPAND, 10);
    pLVSizer->AddStretchSpacer(10);
    pLVSizer->Add(new wxStaticLine(pLeftPanel), 0, wxTOP | wxBOTTOM | wxEXPAND, 20);
    pLVSizer->Add(new wxButton(pLeftPanel, idExitBtn, _("Exit") ), 1, wxALL | wxEXPAND, 10);
    pLeftPanel->SetSizer(pLVSizer);

    // right panel
    wxPanel* pRightPanel = new wxPanel(this, wxID_ANY);
    wxBoxSizer* pRVSizer = new wxBoxSizer(wxVERTICAL);
    m_pTransactionListPanel = new CTransactionListPanel(pRightPanel, this, idDebitListPanel);
    pRVSizer->Add(new CHeaderPanel(pRightPanel, wxID_ANY), 3, wxBOTTOM | wxEXPAND, 1);
    pRVSizer->Add(m_pTransactionListPanel, 60, wxTOP | wxEXPAND, 1);
    pRightPanel->SetSizer(pRVSizer);

    // put them together
    wxBoxSizer* pMSizer = new wxBoxSizer(wxHORIZONTAL);
    pMSizer->Add(pLeftPanel, 1, wxALL | wxEXPAND, 10);
    pMSizer->Add(pRightPanel, 5, wxALL | wxEXPAND, 10);
    SetSizer(pMSizer);

    // event handlers
    Connect(idAddBtn, wxEVT_COMMAND_BUTTON_CLICKED,
            wxCommandEventHandler(CReoccuringTransactionListDlg::OnNewTransaction) );
    Connect(idExitBtn, wxEVT_COMMAND_BUTTON_CLICKED,
            wxCommandEventHandler(CReoccuringTransactionListDlg::OnExit) );

}

CReoccuringTransactionListDlg::~CReoccuringTransactionListDlg()
{
    if (!SaveTransactionsToDisk() )
        aceMSGBOX(this, ace::MB_ERROR, _("Could not save reoccuring transactions to disk!"), NULL);
}

bool CReoccuringTransactionListDlg::LoadTransactionsFromDisk()
{
    CInFile iFile(m_Filename);
    if (iFile.IsOpen() )
    {
        SReoccuringTransaction transaction;
        while (iFile.ReadObject(transaction) )
        {
            // update last transaction number
            if (transaction.Number > m_LastTransactionNumber)
                m_LastTransactionNumber = transaction.Number;

            // save the transaction to the list
            m_vTransactionList.push_back(transaction);
        }
    }
    SortTransactions();
    return true;
}

bool CReoccuringTransactionListDlg::SaveTransactionsToDisk()
{
    // do not save anything if there are no transactions
    if (m_vTransactionList.empty() )
        return true;

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

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

bool CReoccuringTransactionListDlg::AddNewTransaction(SReoccuringTransaction& Transaction)
{
    // save the transaction to the list
    m_vTransactionList.push_back(Transaction);

    // save the transactions to disk
    SaveTransactionsToDisk();

    return true;
}

SReoccuringTransaction* CReoccuringTransactionListDlg::GetTransaction(const ULONG TransactionNumber)
{
    if (m_vTransactionList.empty() )
    {
        return NULL;
    }

    std::vector<SReoccuringTransaction>::iterator iter;
    for (iter = m_vTransactionList.begin(); iter != m_vTransactionList.end(); ++iter)
    {
        if (TransactionNumber == iter->Number)
        {
            return &(*iter);
        }
    }
    return NULL;
}

void CReoccuringTransactionListDlg::UpdateTransaction(SReoccuringTransaction* pTransaction)
{
    wxDateTime NextDate;
    NextDate.ParseDate(pTransaction->NextOccurance);
    switch(pTransaction->Frequency)
    {
        case BO_ONCE:
        DeleteTransaction(pTransaction->Number);
        break;

        case BO_DAILY:
        NextDate.Add(wxDateSpan::Day() );
        break;

        case BO_WEEKLY:
        NextDate.Add(wxDateSpan::Week() );
        break;

        case BO_BIWEEKLY:
        NextDate.Add(wxDateSpan::Weeks(2) );
        break;

// FIXME (USER#1#): Twice Monthly is not right
        case BO_TWICEMONTHLY:
        NextDate.Add(wxDateSpan::Weeks(2) );
        break;

        case BO_MONTHLY:
        NextDate.Add(wxDateSpan::Month() );
        break;

        case BO_QUARTERLY:
        NextDate.Add(wxDateSpan::Months(3) );
        break;

        case BO_TWICEANNUALLY:
        NextDate.Add(wxDateSpan::Months(6) );
        break;

        case BO_ANNUALLY:
        NextDate.Add(wxDateSpan::Year() );
        break;
    }
    pTransaction->NextOccurance = NextDate.Format(_("%m/%d/%Y") );
}

bool CReoccuringTransactionListDlg::HasMoreTransactions()
{
    if (m_iCurrentTransaction != m_vTransactionList.end() && !m_vTransactionList.empty() )
        return true;
    return false;
}

const SReoccuringTransaction& CReoccuringTransactionListDlg::GetNextTransaction()
{
    if (m_iCurrentTransaction != m_vTransactionList.end() )
        m_iCurrentTransaction++;
    return *(m_iCurrentTransaction - 1);
}

bool CReoccuringTransactionListDlg::DeleteTransaction(const ULONG TransactionNumber)
{
    int Selection = -1;
    SReoccuringTransaction* pTransaction = GetTransaction(TransactionNumber);
    if (pTransaction->Frequency != BO_ONCE)
    {
        wxArrayString Choices;
        Choices.Add(_("Delete this occurance only") );
        Choices.Add(_("Delete the entire series") );
        Selection = wxGetSingleChoiceIndex(_("Delete this occurance only or the entire series?"),
                                           _("Delete Reoccuring Transaction"),
                                           Choices, this, -1, -1, true, 125, 100);
    }
    else
    {
        Selection = 1;
    }
    switch (Selection)
    {
        case -1:
        // user selected cancel
        return true;

        case 0:
        UpdateTransaction(pTransaction);
        m_pTransactionListPanel->Refresh();
        break;

        case 1:
        for (m_iCurrentTransaction = m_vTransactionList.begin(); m_iCurrentTransaction != m_vTransactionList.end(); ++m_iCurrentTransaction)
        {
            if (m_iCurrentTransaction->Number == TransactionNumber)
            {
                m_vTransactionList.erase(m_iCurrentTransaction);
                return true;
            }
        }
        break;

        default:
        wxFAIL_MSG(_("Incorrect Selection") );
    }
    return false;
}

void CReoccuringTransactionListDlg::DeleteCurrentTransaction()
{
    if (m_iCurrentTransaction >= m_vTransactionList.begin() && m_iCurrentTransaction < m_vTransactionList.end() )
    {
        DeleteTransaction(m_iCurrentTransaction->Number);
    }
}

void CReoccuringTransactionListDlg::OnRecordTransaction(wxCommandEvent& event)
{

}

void CReoccuringTransactionListDlg::OnNewTransaction(wxCommandEvent &event)
{
    // set up the transaction
    SReoccuringTransaction Transaction =
    {
        0,
        _(""),
        0,
        0,
        _(""),
        _(""),
        0.00f,
        0.00f,
        0,
        _(""),
        _(""),
        _(""),
        _("")
    };

    // get the transaction
    CViewReoccuringTransactionDlg* pNewTransactionDlg = new CViewReoccuringTransactionDlg(this, Transaction, m_AccountNames, true, true);
    const ULONG Ret = pNewTransactionDlg->ShowModal();
    if (Ret != wxID_OK)
    {
        return;
    }
    Transaction.Number = GetNextTransactionNumber();

    // save the transaction
    m_vTransactionList.push_back(Transaction);
    SortTransactions();
//    sort(m_vTransactionList.begin(), m_vTransactionList.end(), SortTransactionsByDate);
}

void CReoccuringTransactionListDlg::OnExit(wxCommandEvent &event)
{
    Show(false);
}

void CReoccuringTransactionListDlg::SortTransactions()
{
    sort(m_vTransactionList.begin(), m_vTransactionList.end(), SortTransactionsByDate);
}


///** class : CViewReoccuringTransactionDlg **********************************///


CViewReoccuringTransactionDlg::CViewReoccuringTransactionDlg(wxWindow* pParent, SReoccuringTransaction& Transaction,
                                                             wxArrayString AccountNames, const bool IsNew, const bool CanEdit)
: wxDialog(pParent, wxID_ANY, _(""), wxDefaultPosition, wxSize(800, 300), wxCAPTION),
  m_pParent(pParent), m_pTransaction(&Transaction), m_pAccountNames(AccountNames), m_IsNew(IsNew), m_CanEdit(CanEdit)
{
    // set the dialog title, next transaction date and amount
    wxDateTime NextDate, LastDate;
    if (m_IsNew)
    {
        this->SetTitle(_("New Reoccuring Transaction") );
        m_EstAmount = _("0.00");
        NextDate = wxDateTime::Today();
        m_pTransaction->Type = RT_DEBIT;
    }
    else
    {
        (m_CanEdit) ? this->SetTitle(_("Edit Reoccuring Transaction") ) : this->SetTitle(_("View Reoccuring Transaction") );
        m_EstAmount = wxString::Format(_("%#1.2f"), m_pTransaction->EstAmount);
        if ( (NextDate.ParseDate(m_pTransaction->NextOccurance) == NULL) )
        {
            wxFAIL_MSG(_("wxDateTime::ParseDate() returned NULL") );
        }
    }

    // set up the left panel
    wxArrayString TransactionTypes;
    ace::g_pData->GetAllTransactionTypes(TransactionTypes, false);

    wxPanel* pLeftPanel = new wxPanel(this, wxID_ANY);

    m_pRadioBox = new wxRadioBox(pLeftPanel,
                                 idRadioBoxCtrl,
                                 _("Transaction Type"),
                                 wxDefaultPosition,
                                 wxDefaultSize,
                                 TransactionTypes,
                                 0,
                                 wxRA_SPECIFY_ROWS);

    // set up the right panel
    wxPanel* pRightPanel = new wxPanel(this, wxID_ANY);
    wxGridSizer* pGSizer = new wxGridSizer(4, 5, 3);

    // name
    pGSizer->Add(new wxStaticText(pRightPanel, wxID_ANY, _("Name") ),
                 1, wxALIGN_RIGHT | wxLEFT | wxRIGHT, 10);
    if (m_CanEdit)
    {
        pGSizer->Add(m_pNameCtrl = new wxTextCtrl(pRightPanel,
                                                  idNameCtrl,
                                                  m_pTransaction->Name,
                                                  wxDefaultPosition,
                                                  wxDefaultSize,
                                                  0,
                                                  wxTextValidator(wxFILTER_ASCII,
                                                                  &m_pTransaction->Name) ),
                     3, wxEXPAND | wxLEFT | wxRIGHT, 10);
    }
    else
    {
        pGSizer->Add(new wxStaticText(pRightPanel, wxID_ANY, m_pTransaction->Name) );
    }

    // frequency
    wxArrayString FrequencyChoices;
    ace::g_pData->GetAllFrequencyTypes(FrequencyChoices);
    pGSizer->Add(new wxStaticText(pRightPanel, wxID_ANY, _("Frequency") ),
                 1, wxALIGN_RIGHT | wxLEFT | wxRIGHT, 10);
    if (m_CanEdit)
    {
        pGSizer->Add(m_pFrequencyCtrl = new wxComboBox(pRightPanel,
                                                       idFrequencyCtrl,
                                                       ace::g_pData->GetFrequencyTypeString(m_pTransaction->Frequency),
                                                       wxDefaultPosition,
                                                       wxDefaultSize,
                                                       FrequencyChoices,
                                                       0,
                                                       wxGenericValidator(&m_Frequency) ),
                     3, wxEXPAND | wxLEFT | wxRIGHT, 10);
    }
    else
    {
        pGSizer->Add(new wxStaticText(pRightPanel, wxID_ANY, m_pTransaction->Category) );
    }

    // last occurance
    pGSizer->Add(new wxStaticText(pRightPanel, wxID_ANY, _("Last Payment Date") ),
                 1, wxALIGN_RIGHT | wxLEFT | wxRIGHT, 10);
    pGSizer->Add(new wxStaticText(pRightPanel, wxID_ANY, m_pTransaction->LastOccurance) );

    // next occurance
    pGSizer->Add(new wxStaticText(pRightPanel, wxID_ANY, _("Next Payment Date") ),
                 1, wxALIGN_RIGHT | wxLEFT | wxRIGHT, 10);
    if (m_CanEdit)
    {
        pGSizer->Add(m_pNextDateCtrl = new wxDatePickerCtrl(pRightPanel,
                                                            idNextDateCtrl,
                                                            NextDate,
                                                            wxDefaultPosition,
                                                            wxDefaultSize,
                                                            wxDP_DROPDOWN,
                                                            wxDefaultValidator),
                     3, wxALIGN_LEFT | wxLEFT | wxRIGHT, 10);
    }
    else
    {
        pGSizer->Add(new wxStaticText(pRightPanel, wxID_ANY, m_pTransaction->NextOccurance) );
    }

    // last amount
    pGSizer->Add(new wxStaticText(pRightPanel, wxID_ANY, _("Last Amount Paid") ),
                 1, wxALIGN_RIGHT | wxLEFT | wxRIGHT, 10);
    pGSizer->Add(new wxStaticText(pRightPanel, wxID_ANY,
                                  wxString::Format(_("$%#1.2f"), m_pTransaction->LastAmount) ) );

    // est amount
    pGSizer->Add(new wxStaticText(pRightPanel, wxID_ANY, _("Est Amount") ),
                 1, wxALIGN_RIGHT | wxLEFT | wxRIGHT, 10);
    if (m_CanEdit)
    {
        pGSizer->Add(m_pEstAmountCtrl = new wxTextCtrl(pRightPanel,
                                                       idEstAmountCtrl,
                                                       m_EstAmount,
                                                       wxDefaultPosition,
                                                       wxDefaultSize,
                                                       0,
                                                       wxTextValidator(wxFILTER_NUMERIC,
                                                                       &m_EstAmount) ),
                     3, wxALIGN_LEFT | wxLEFT | wxRIGHT, 10);
    }
    else
    {
        pGSizer->Add(new wxStaticText(pRightPanel, wxID_ANY,
                                      wxString::Format(_("$%#1.2f"), m_pTransaction->EstAmount) ) );
    }

    // pay from account
    pGSizer->Add(new wxStaticText(pRightPanel, wxID_ANY, _("From Account") ),
                 1, wxALIGN_RIGHT | wxLEFT | wxRIGHT, 10);
    if (m_CanEdit)
    {
        pGSizer->Add(m_pPayFromAccountCtrl = new wxComboBox(pRightPanel,
                                                            idPayFromAccountCtrl,
                                                            m_pTransaction->PayFromAccountName,
                                                            wxDefaultPosition,
                                                            wxDefaultSize,
                                                            m_pAccountNames,
                                                            0,
                                                            wxGenericValidator(&(m_pTransaction->PayFromAccountName) ) ),
                     3, wxEXPAND | wxLEFT | wxRIGHT, 10);
    }
    else
    {
        pGSizer->Add(new wxStaticText(pRightPanel, wxID_ANY, m_pTransaction->PayFromAccountName) );
    }

    // deposit to account
    pGSizer->Add(new wxStaticText(pRightPanel, wxID_ANY, _("To Account") ),
                 1, wxALIGN_RIGHT | wxLEFT | wxRIGHT, 10);
    if (m_CanEdit)
    {
        pGSizer->Add(m_pDepositToAccountCtrl = new wxComboBox(pRightPanel,
                                                              idDepositToAccountCtrl,
                                                              m_pTransaction->DepositToAccountName,
                                                              wxDefaultPosition,
                                                              wxDefaultSize,
                                                              m_pAccountNames,
                                                              0,
                                                              wxGenericValidator(&(m_pTransaction->DepositToAccountName) ) ),
                     3, wxEXPAND | wxLEFT | wxRIGHT, 10);
    }
    else
    {
        pGSizer->Add(new wxStaticText(pRightPanel, wxID_ANY, m_pTransaction->DepositToAccountName) );
    }

    // category
    wxArrayString Categories;
    ace::g_pData->GetAllCategories(Categories);
    pGSizer->Add(new wxStaticText(pRightPanel, wxID_ANY, _("Category") ),
                 1, wxALIGN_RIGHT | wxLEFT | wxRIGHT, 10);
    if (m_CanEdit)
    {
        pGSizer->Add(m_pCategoryCtrl = new wxComboBox(pRightPanel,
                                                      idCategoryCtrl,
                                                      m_pTransaction->Category,
                                                      wxDefaultPosition,
                                                      wxDefaultSize,
                                                      Categories,
                                                      0,
                                                      wxGenericValidator(&(m_pTransaction->Category) ) ),
                     3, wxEXPAND | wxLEFT | wxRIGHT, 10);
    }
    else
    {
        pGSizer->Add(new wxStaticText(pRightPanel, wxID_ANY, m_pTransaction->Category) );
    }

    // sub category
    wxArrayString SubCategories;
    pGSizer->Add(new wxStaticText(pRightPanel, wxID_ANY, _("Sub Category") ),
                 1, wxALIGN_RIGHT | wxLEFT | wxRIGHT, 10);
    if (m_CanEdit)
    {
        pGSizer->Add(m_pSubCategoryCtrl = new wxComboBox(pRightPanel,
                                                         idSubCategoryCtrl,
                                                         m_pTransaction->SubCategory,
                                                         wxDefaultPosition,
                                                         wxDefaultSize,
                                                         SubCategories,
                                                         0,
                                                         wxGenericValidator(&(m_pTransaction->SubCategory) ) ),
                     3, wxEXPAND | wxLEFT | wxRIGHT, 10);
    }
    else
    {
        pGSizer->Add(new wxStaticText(pRightPanel, wxID_ANY, m_pTransaction->SubCategory) );
    }

    pRightPanel->SetSizer(pGSizer);

    // set up the buttons
    wxStdDialogButtonSizer* pBtnSizer = new wxStdDialogButtonSizer();
    if (m_IsNew)
        pBtnSizer->AddButton(new wxButton(this, wxID_OK, _("Save") ) );
    pBtnSizer->AddButton(new wxButton(this, wxID_CANCEL, _("Cancel") ) );
    pBtnSizer->Realize();

    // put it all together
    wxBoxSizer* pHSizer = new wxBoxSizer(wxHORIZONTAL);
    pHSizer->Add(pLeftPanel, 0, wxALL, 10);
    pHSizer->Add(pRightPanel, 0, wxALL, 10);

    wxBoxSizer* pMSizer = new wxBoxSizer(wxVERTICAL);
    pMSizer->Add(pHSizer, 3, wxEXPAND, 5);
    pMSizer->Add(new wxStaticLine(this), 0, wxALIGN_CENTER | wxALL | wxEXPAND, 15);
    pMSizer->Add(pBtnSizer, 1);
    this->SetSizer(pMSizer);

    // set the starting transaction type
    if (m_IsNew)
    {
        m_LastRadioBoxSelection = _("Debit");
        m_pRadioBox->SetStringSelection(m_LastRadioBoxSelection);
    }
    else
    {
        m_pRadioBox->SetStringSelection(ace::g_pData->GetTransactionTypeString(m_pTransaction->TransactionType) );
    }
    ShowControls(m_pTransaction->TransactionType);
    if (!m_CanEdit) m_pRadioBox->Enable(false);

    // attach handlers
    Connect(wxID_OK, wxEVT_COMMAND_BUTTON_CLICKED,
            wxCommandEventHandler(CViewReoccuringTransactionDlg::OnSave) );
    Connect(idCategoryCtrl, wxEVT_COMMAND_COMBOBOX_SELECTED,
            wxCommandEventHandler(CViewReoccuringTransactionDlg::OnCategorySelection) );
    Connect(idRadioBoxCtrl, wxEVT_COMMAND_RADIOBOX_SELECTED ,
            wxCommandEventHandler(CViewReoccuringTransactionDlg::OnRadioButtonSelect) );
}

CViewReoccuringTransactionDlg::~CViewReoccuringTransactionDlg()
{

}

void CViewReoccuringTransactionDlg::ShowControls(const ULONG type)
{
    m_pTransaction->TransactionType = type;
    switch (type)
    {
        case TT_ATM:
        m_pNameCtrl->ChangeValue(_("ATM Withdrawal") );
        m_pNameCtrl->Show(false);
        m_pCategoryCtrl->SetValue(_("ATM Withdrawal") );
        m_pSubCategoryCtrl->SetValue(_("") );
        m_pSubCategoryCtrl->Show(false);
        break;

        case TT_DEPOSIT:
        m_pNameCtrl->ChangeValue(_("") );
        m_pNameCtrl->Show(true);
        m_pCategoryCtrl->SetValue(_("") );
        m_pSubCategoryCtrl->SetValue(_("") );
        m_pSubCategoryCtrl->Show(true);
        break;

        case TT_CHECK:
        m_pNameCtrl->ChangeValue(_("") );
        m_pNameCtrl->Show(true);
        m_pCategoryCtrl->SetValue(_("") );
        m_pSubCategoryCtrl->SetValue(_("") );
        m_pSubCategoryCtrl->Show(true);
        break;

        case TT_DEBIT:
        m_pNameCtrl->ChangeValue(_("") );
        m_pNameCtrl->Show(true);
        m_pCategoryCtrl->SetValue(_("") );
        m_pSubCategoryCtrl->SetValue(_("") );
        m_pSubCategoryCtrl->Show(true);
        break;

        case TT_TRANSFER:
        m_pNameCtrl->ChangeValue(_("") );
        m_pNameCtrl->Show(true);
        m_pCategoryCtrl->SetValue(_("Account Transfer") );
        m_pCategoryCtrl->Show(true);
        m_pSubCategoryCtrl->SetValue(_("") );
        m_pSubCategoryCtrl->Show(false);
        break;
    }
}

bool CViewReoccuringTransactionDlg::TransferDataFromWindow()
{
    wxString Type = m_pRadioBox->GetStringSelection();

    // name
    m_pTransaction->Name = m_pNameCtrl->GetValue();
    if ( (m_pTransaction->Name).IsEmpty() )
    {
        aceMSGBOX(this, ace::MB_INFO, _("You must give this transaction a name."), NULL);
        return false;
    }
    // type
    if (Type == _("Deposit") )        m_pTransaction->Type = RT_DEPOSIT;
    else if (Type == _("Transfer") )  m_pTransaction->Type = RT_TRANSFER;
    else                                m_pTransaction->Type = RT_DEBIT;
    // frequency
    m_Frequency = m_pFrequencyCtrl->GetValue();
    if ( (m_Frequency).IsEmpty() )
    {
        aceMSGBOX(this, ace::MB_INFO, _("You must choose a valid frequency from the dropdown list."), NULL);
        return false;
    }
    m_pTransaction->Frequency = ace::g_pData->GetFrequencyTypeValue(m_pFrequencyCtrl->GetValue() );
    // next occurance
    wxDateTime date = m_pNextDateCtrl->GetValue();
    if (!date.IsValid() )
    {
        aceMSGBOX(this, ace::MB_INFO, _("Invalid date"), NULL);
        return false;
    }
    m_pTransaction->NextOccurance = date.Format(_("%m/%d/%Y") );
    // last occurance
    if (m_IsNew)
    {
        m_pTransaction->LastOccurance = m_pTransaction->NextOccurance;
    }
    else
    {

    }
    // est amount
    wxString s = m_pEstAmountCtrl->GetValue();
    if (s.IsEmpty() )
    {
        m_pTransaction->EstAmount = 0.00f;
    }
    else
    {
        s.ToDouble(&m_pTransaction->EstAmount);
    }
    // last amount
    if (m_IsNew)
    {
        m_pTransaction->LastAmount = 0.00f;
    }
    // transaction type
    m_pTransaction->TransactionType = ace::g_pData->GetTransactionTypeValue(Type);
    // pay from and deposit to accounts
    m_pTransaction->PayFromAccountName = m_pPayFromAccountCtrl->GetValue();
    m_pTransaction->DepositToAccountName = m_pDepositToAccountCtrl->GetValue();
    if ( (m_pTransaction->Type == RT_DEBIT && (m_pTransaction->PayFromAccountName).IsEmpty() ) ||
         (m_pTransaction->Type == RT_DEPOSIT && (m_pTransaction->DepositToAccountName).IsEmpty() ) ||
         (m_pTransaction->Type == RT_TRANSFER && ( (m_pTransaction->PayFromAccountName).IsEmpty() || (m_pTransaction->DepositToAccountName).IsEmpty() ) ) )
    {
        aceMSGBOX(this, ace::MB_INFO, _("You must choose a valid account name from the drop down list."), NULL);
        return false;
    }
    // category
    m_pTransaction->Category == m_pCategoryCtrl->GetValue();
    //sub category
    m_pTransaction->SubCategory == m_pSubCategoryCtrl->GetValue();

    return true;
}

void CViewReoccuringTransactionDlg::OnSave(wxCommandEvent &event)
{
    wxCHECK_RET(Validate(), _("Could not validate data!") );
    if (TransferDataFromWindow() )
    {
        EndModal(wxID_OK);
    }
}

void CViewReoccuringTransactionDlg::OnCategorySelection(wxCommandEvent &event)
{
    wxArrayString SubCategoryChoices;
    if (m_pSubCategoryCtrl->GetCount() != 0)
        m_pSubCategoryCtrl->Clear();
    wxString Category = m_pCategoryCtrl->GetValue();
    ace::g_pData->GetAllSubCategories(Category, SubCategoryChoices);
    m_pSubCategoryCtrl->Append(SubCategoryChoices);
}

void CViewReoccuringTransactionDlg::OnRadioButtonSelect(wxCommandEvent &event)
{
    // cannot change type of an existing transaction!
    if (!m_IsNew)
    {
        wxString msg = _("You can not change the type of an existing transaction!\n");
        msg += _("If you want to change the type, you must first delete this one.");
        aceMSGBOX(this, ace::MB_INFO, msg, NULL);
        m_pRadioBox->SetStringSelection(m_LastRadioBoxSelection);
        return;
    }

    wxString str = m_pRadioBox->GetStringSelection();

    m_LastRadioBoxSelection = str;
    ShowControls(ace::g_pData->GetTransactionTypeValue(str) );
}
