/**
    File        : Account.cpp
    Description : Encapsulates a single account
    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 "Account.h"

CAccount::CAccount(wxWindow* pParent, IPluginHost* pHost, SAccountInfo& AccountInfo)
: wxSplitterWindow(pParent, idAccountWindow),
  m_pParent(pParent), m_pHost(pHost), m_AccountInfo(AccountInfo), m_HideReconciled(true),
  m_LastTransactionNumber(0), m_LastTransferNumber(0), m_pViewer(NULL)
{
    // get the transaction viewer from the list of loaded plugins
    GetViewer();

    // set the filename and load transactions
    m_Filename = wxString::Format(wxT("Data/%s.adf"), AccountInfo.AccountName);
    LoadTransactionsFromDisk();

    // *** set up the top panel ***
    m_pTopPanel = new wxPanel(this, idTopPanel);
    m_pTopPanel->SetBackgroundColour(wxColour(240, 240, 240) );

    // date box
    wxStaticBoxSizer *pDateSizer = new wxStaticBoxSizer(wxVERTICAL,
                                                        m_pTopPanel,
                                                        wxT("Today Is") );
    wxDateTime TimeNow = wxDateTime::Now();
    wxString TimeStr = TimeNow.Format(wxT("%A\n%B %d, %Y"),
                                      wxDateTime::Local).c_str();
    pDateSizer->Add(new wxStaticText(m_pTopPanel, wxID_ANY, TimeStr),
                    1,
                    wxALIGN_CENTER | wxALL | wxEXPAND,
                    10);

    // transactions box
    wxStaticBoxSizer *pTransactionSizer = new wxStaticBoxSizer(wxVERTICAL,
                                                               m_pTopPanel,
                                                               wxT("Transactions") );
    wxButton* pNewTransactionBtn = new wxButton(m_pTopPanel, idNewTransactionBtn, wxT("New Transaction") );
    wxButton* pSearchTransactionBtn = new wxButton(m_pTopPanel, idSearchTransactionBtn, wxT("Search Transactions") );
    pTransactionSizer->Add(pNewTransactionBtn, 1, wxALIGN_CENTER | wxALL | wxEXPAND, 4);
    pTransactionSizer->Add(pSearchTransactionBtn, 1, wxALIGN_CENTER | wxALL | wxEXPAND, 4);
    pTransactionSizer->AddStretchSpacer(2);

    // account box
    wxStaticBoxSizer *pAccountSizer = new wxStaticBoxSizer(wxVERTICAL,
                                                               m_pTopPanel,
                                                               wxT("Accounts") );
    wxButton* pBalanceAccountBtn = new wxButton(m_pTopPanel, idBalanceAccountBtn, wxT("Balance Account") );
    pAccountSizer->Add(pBalanceAccountBtn, 1, wxALIGN_CENTER | wxALL | wxEXPAND, 4);
    pAccountSizer->AddStretchSpacer(3);

    // account balance box
    wxStaticBoxSizer *pBalanceSizer = new wxStaticBoxSizer(wxVERTICAL,
                                                           m_pTopPanel,
                                                           wxT("Balance") );
    m_pBalanceCtrl = new wxTextCtrl(m_pTopPanel, idBalanceCtrl);
    pBalanceSizer->Add(m_pBalanceCtrl, 1, wxALIGN_CENTER | wxALL, 4);
    pBalanceSizer->AddStretchSpacer(3);

    // info box
    wxStaticBoxSizer *pInfoSizer = new wxStaticBoxSizer(wxVERTICAL,
                                                        m_pTopPanel,
                                                        wxT("") );
    wxStaticText* pText1 = new wxStaticText(m_pTopPanel, wxID_ANY, wxT("Left click a transaction to view or edit it") );
    wxStaticText* pText2 = new wxStaticText(m_pTopPanel, wxID_ANY, wxT("Right click a transaction for more options") );
    m_pHideReconciledCtrl = new wxCheckBox(m_pTopPanel, idHideReconciledCtrl, wxT("Hide Reconciled Transactions") );
    m_pHideReconciledCtrl->SetValue(true);
    pInfoSizer->Add(pText1, 1, wxALIGN_CENTER | wxALL | wxEXPAND, 4);
    pInfoSizer->Add(pText2, 1, wxALIGN_CENTER | wxALL | wxEXPAND, 4);
    pInfoSizer->AddStretchSpacer(1);
    pInfoSizer->Add(m_pHideReconciledCtrl, 1, wxALIGN_CENTER | wxALL | wxEXPAND, 4);

    // set up the sizer
    wxBoxSizer *pHSizer = new wxBoxSizer(wxHORIZONTAL);
    pHSizer->Add(pDateSizer, 5, wxALIGN_CENTER | wxALL | wxEXPAND, 10);
    pHSizer->Add(pTransactionSizer, 8, wxALIGN_CENTER | wxALL | wxEXPAND, 10);
    pHSizer->Add(pAccountSizer, 8, wxALIGN_CENTER | wxALL | wxEXPAND, 10);
    pHSizer->Add(pBalanceSizer, 5, wxALIGN_CENTER | wxALL | wxEXPAND, 10);
    pHSizer->Add(pInfoSizer, 12, wxALIGN_CENTER | wxALL | wxEXPAND, 10);
    m_pTopPanel->SetSizer(pHSizer);

    // *** set up the bottom panel ***
    m_pBottomPanel = new wxPanel(this, idBottomPanel);
    m_pBottomPanel->SetBackgroundColour(wxColour(250, 250,250) );

    // create the TransactionList (wxDataViewCtrl and wxDataViewListModel)
    m_pTransactionListCtrl = new wxDataViewCtrl(m_pBottomPanel, idTransactionListCtrl);
    m_pTransactionListModel = new CTransactionList();
    m_pTransactionListCtrl->AssociateModel(m_pTransactionListModel);

    // add columns to the list
    std::vector<std::pair<wxString, int> > HeaderList;
    std::vector<std::pair<wxString, int> >::iterator iter;
    int n = 0;
    int NumColumns = m_pViewer->GetHeaderToDisplay(HeaderList);
    m_pTransactionListModel->SetNumberOfCols(NumColumns);
    for (iter = HeaderList.begin(); iter != HeaderList.end(); ++iter)
    {
        m_pTransactionListCtrl->AppendTextColumn(iter->first, n, wxDATAVIEW_CELL_INERT, iter->second);
        n++;
    }

    // add transactions to the list
    std::vector<STransaction>::iterator i;
    for (i = m_TransactionList.begin(); i != m_TransactionList.end(); ++i)
    {
//        if ( (!m_HideReconciled) || (m_HideReconciled && i->Status != TS_RECONCILED) )
//        {
            wxArrayString TransactionArray;
            m_pViewer->GetItemsToDisplay(*i, TransactionArray);
            m_pTransactionListModel->AddLine(TransactionArray);
//        }
    }

    // create the sizer and add the list ctrl
    wxBoxSizer *pVSizer = new wxBoxSizer(wxVERTICAL);
    pVSizer->Add(m_pTransactionListCtrl, 1, wxALIGN_CENTER | wxALL | wxEXPAND, 30);
    m_pBottomPanel->SetSizer(pVSizer);

    // *** split the window ***
    this->SplitHorizontally(m_pTopPanel, m_pBottomPanel, 150);
}

CAccount::~CAccount()
{
    SaveTransactionsToDisk();
}

void CAccount::GetViewer()
{
    wxCHECK_RET(!m_AccountInfo.Viewer.IsEmpty(), wxT("Transaction Viewer name is empty") );
    m_pHost->GetViewer(m_AccountInfo.Viewer, &m_pViewer);
    wxCHECK_RET(m_pViewer != NULL, wxT("No viewer availiable for this account type!") );
}

bool CAccount::LoadTransactionsFromDisk()
{
    // check for transaction file and create it if there is not one
    wxFile File;
    wxFFile iFile;
    if (!File.Exists(m_Filename) )
    {
        File.Create(m_Filename);
    }

    // open the transaction file
    if (!iFile.Open(m_Filename) )
    {
        wxFAIL_MSG(wxT("Cannot open input file!") );
        return false;
    }
    else
    {
        if (iFile.Length() > 0)
        {
            wxString TransactionList;
            iFile.ReadAll(&TransactionList);
            wxStringTokenizer ST1(TransactionList, wxT("\n"), wxTOKEN_RET_EMPTY);
            while (ST1.HasMoreTokens() )
            {
                wxString str;
                STransaction transaction;
                wxStringInputStream TransactionData(ST1.GetNextToken() );
                wxTextInputStream iData(TransactionData, RECORD_SEPERATOR);

                // transaction number
                str                     = iData.ReadWord();
                str.ToULong(&transaction.TransactionNumber);
                // transfer number
                str                     = iData.ReadWord();
                str.ToULong(&transaction.TransferNumber);
                // account name
                transaction.AccountName = iData.ReadWord();
                // date
                transaction.Date        = iData.ReadWord();
                // type
                str                     = iData.ReadWord();
                str.ToULong(&transaction.Type);
                // check number
                str                     = iData.ReadWord();
                str.ToULong(&transaction.CheckNumber);
                // payee
                transaction.Payee       = iData.ReadWord();
                // amount
                str                     = iData.ReadWord();
                str.ToDouble(&transaction.Amount);
                // category
                transaction.Category    = iData.ReadWord();
                // sub category
                transaction.SubCategory = iData.ReadWord();
                // comment
                transaction.Comment     = iData.ReadWord();
                // status
                str                     = iData.ReadWord();
                str.ToULong(&transaction.Status);
                // is deposit
                str                     = iData.ReadWord();
                (str == wxT("y") ) ? transaction.IsDeposit = true : transaction.IsDeposit = false;

                if (transaction.TransactionNumber > m_LastTransactionNumber)
                {
                    m_LastTransactionNumber = transaction.TransactionNumber;
                }
                if (transaction.TransferNumber > m_LastTransferNumber)
                {
                    m_LastTransferNumber = transaction.TransferNumber;
                }

                m_TransactionList.push_back(transaction);
            }
        }
    }
    iFile.Close();
    return true;
}

bool CAccount::SaveTransactionsToDisk()
{
    // do not save anything if there are no transactions
    if (m_TransactionList.empty() )
        return false;

    wxFFileOutputStream oFile(m_Filename);
    if (!oFile.IsOk() )
    {
        wxFAIL_MSG(wxT("Cannot open output file!") );
        return false;
    }

    wxTextOutputStream oText(oFile);
    wxString List;
    wxString str;
    std::vector<STransaction>::iterator iter;
    for (iter = m_TransactionList.begin(); iter != m_TransactionList.end(); ++iter)
    {
        str = wxString::Format(wxT("%u"), iter->TransactionNumber);
        oText << str               << RECORD_SEPERATOR;
        str = wxString::Format(wxT("%u"), iter->TransferNumber);
        oText << str               << RECORD_SEPERATOR;
        oText << iter->AccountName << RECORD_SEPERATOR;
        oText << iter->Date        << RECORD_SEPERATOR;
        str = wxString::Format(wxT("%u"), iter->Type);
        oText << str               << RECORD_SEPERATOR;
        str = wxString::Format(wxT("%u"), iter->CheckNumber);
        oText << str               << RECORD_SEPERATOR;
        oText << iter->Payee       << RECORD_SEPERATOR;
        str = wxString::Format(wxT("%#1.2f"), iter->Amount);
        oText << str               << RECORD_SEPERATOR;
        oText << iter->Category    << RECORD_SEPERATOR;
        oText << iter->SubCategory << RECORD_SEPERATOR;
        oText << iter->Comment     << RECORD_SEPERATOR;
        str = wxString::Format(wxT("%u"), iter->Status);
        oText << str               << RECORD_SEPERATOR;
        (iter->IsDeposit) ? oText << wxT("y") : oText << wxT("n");
        oText << TRANSACTION_SEPERATOR;
    }
    oFile.Close();

    return true;
}



CTransactionList::CTransactionList()
: m_NumColumns(0)
{

}

CTransactionList::~CTransactionList()
{

}

void CTransactionList::SetNumberOfCols(unsigned int n)
{
    m_NumColumns = n;
}

unsigned int CTransactionList::GetNumberOfCols()
{
    return m_NumColumns;
}

unsigned int CTransactionList::GetNumberOfRows()
{
    return m_TransactionList.size();
}

wxString CTransactionList::GetColType(unsigned int col)
{
    // Right now all the columns are going to be wxStrings.  If this changes
    // a new renderer will have to be written to handle the type of data to be
    // displayed in each individual column.  After that, you can simply switch
    // the column number [ switch(col) ] and have one case statement for each
    // column number that will return the type of data to be displayed for that
    // column [ return wxT("string") ]
    return wxT("string");
}

void CTransactionList::GetValue(wxVariant& variant, unsigned int col, unsigned int row)
{
    if (col > m_NumColumns)
    {
        variant = wxT("");
    }
    else
    {
        wxArrayString ArrStr = *(m_TransactionList.begin() + row);
        variant = ArrStr[col];
    }
}

bool CTransactionList::SetValue(wxVariant& variant, unsigned int col, unsigned int row)
{
    if (col > m_NumColumns)
    {
        return false;
    }
    else
    {
        wxArrayString& ArrStr = *(m_TransactionList.begin() + row);
        ArrStr[col] = variant.GetString();
    }
    return true;
}

