/***************************************************************************
                          mymoneyaccount.cpp
                          -------------------
    copyright            : (C) 2000 by Michael Edwardes
                           (C) 2002 by Thomas Baumagrt
    email                : mte@users.sourceforge.net
                           ipwizard@users.sourceforge.net
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

// ----------------------------------------------------------------------------
// TQt Includes

#include <tqregexp.h>
#include <tdestandarddirs.h>
#include <kiconloader.h>

// ----------------------------------------------------------------------------
// TDE Includes

#include <tdelocale.h>

// ----------------------------------------------------------------------------
// Project Includes

#include <kmymoney/mymoneyexception.h>
#include <kmymoney/mymoneyaccount.h>
#include <kmymoney/mymoneysplit.h>

MyMoneyAccount::MyMoneyAccount() :
  m_fraction(-1)
{
  m_accountType = UnknownAccountType;
}

MyMoneyAccount::~MyMoneyAccount()
{
}

MyMoneyAccount::MyMoneyAccount(const TQString& id, const MyMoneyAccount& right) :
  MyMoneyObject(id)
{
  *this = right;
  setId(id);
}

MyMoneyAccount::MyMoneyAccount(const TQDomElement& node) :
  MyMoneyObject(node),
  MyMoneyKeyValueContainer(node.elementsByTagName("KEYVALUEPAIRS").item(0).toElement()),
  m_fraction(-1)
{
  if("ACCOUNT" != node.tagName())
    throw new MYMONEYEXCEPTION("Node was not ACCOUNT");

  setName(node.attribute("name"));

  // tqDebug(TQString("Reading information for account %1").arg(acc.name()));

  setParentAccountId(TQStringEmpty(node.attribute("parentaccount")));
  setLastModified(stringToDate(TQStringEmpty(node.attribute("lastmodified"))));
  setLastReconciliationDate(stringToDate(TQStringEmpty(node.attribute("lastreconciled"))));

  if(!m_lastReconciliationDate.isValid()) {
    // for some reason, I was unable to access our own kvp at this point through
    // the value() method. It always returned empty strings. The workaround for
    // this is to construct a local kvp the same way as we have done before and
    // extract the value from it.
    //
    // Since we want to get rid of the lastStatementDate record anyway, this seems
    // to be ok for now. (ipwizard - 2008-08-14)
    TQString txt = MyMoneyKeyValueContainer(node.elementsByTagName("KEYVALUEPAIRS").item(0).toElement()).value("lastStatementDate");
    if(!txt.isEmpty()) {
      setLastReconciliationDate(TQDate::fromString(txt, TQt::ISODate));
    }
  }

  setInstitutionId(TQStringEmpty(node.attribute("institution")));
  setNumber(TQStringEmpty(node.attribute("number")));
  setOpeningDate(stringToDate(TQStringEmpty(node.attribute("opened"))));
  setCurrencyId(TQStringEmpty(node.attribute("currency")));

  TQString tmp = TQStringEmpty(node.attribute("type"));
  bool bOK = false;
  int type = tmp.toInt(&bOK);
  if(bOK) {
    setAccountType(static_cast<MyMoneyAccount::accountTypeE>(type));
  } else {
    tqWarning(TQString("XMLREADER: Account %1 had invalid or no account type information.").arg(name()));
  }

  if(node.hasAttribute("openingbalance")) {
    if(!MyMoneyMoney(node.attribute("openingbalance")).isZero()) {
      TQString msg = i18n("Account %1 contains an opening balance. Please use a KMyMoney version >= 0.8 and < 0.9 to correct the problem.").arg(m_name);
      throw new MYMONEYEXCEPTION(msg);
    }
  }
  setDescription(node.attribute("description"));

  m_id = TQStringEmpty(node.attribute("id"));
  // tqDebug(TQString("Account %1 has id of %2, type of %3, parent is %4.").arg(acc.name()).arg(id).arg(type).arg(acc.parentAccountId()));

  //  Process any Sub-Account information found inside the account entry.
  m_accountList.clear();
  TQDomNodeList nodeList = node.elementsByTagName("SUBACCOUNTS");
  if(nodeList.count() > 0) {
    nodeList = nodeList.item(0).toElement().elementsByTagName("SUBACCOUNT");
    for(unsigned int i = 0; i < nodeList.count(); ++i) {
      addAccountId(TQString(nodeList.item(i).toElement().attribute("id")));
    }
  }

  nodeList = node.elementsByTagName("ONLINEBANKING");
  if(nodeList.count() > 0) {
    TQDomNamedNodeMap attributes = nodeList.item(0).toElement().attributes();
    for(unsigned int i = 0; i < attributes.count(); ++i) {
      const TQDomAttr& it_attr = attributes.item(i).toAttr();
      m_onlineBankingSettings.setValue(it_attr.name().utf8(), it_attr.value());
    }
  }

}

void MyMoneyAccount::setName(const TQString& name)
{
  m_name = name;
}

void MyMoneyAccount::setNumber(const TQString& number)
{
  m_number = number;
}

void MyMoneyAccount::setDescription(const TQString& desc)
{
  m_description = desc;
}

void MyMoneyAccount::setInstitutionId(const TQString& id)
{
  m_institution = id;
}

void MyMoneyAccount::setLastModified(const TQDate& date)
{
  m_lastModified = date;
}

void MyMoneyAccount::setOpeningDate(const TQDate& date)
{
  m_openingDate = date;
}

void MyMoneyAccount::setLastReconciliationDate(const TQDate& date)
{
  // FIXME: for a limited time (maybe until we delivered 1.0) we
  // keep the last reconciliation date also in the KVP for backward
  // compatability. After that, the setValue() statemetn should be removed
  // and the XML ctor should remove the value completely from the KVP
  setValue("lastStatementDate", date.toString(TQt::ISODate));
  m_lastReconciliationDate = date;
}

void MyMoneyAccount::setParentAccountId(const TQString& parent)
{
  m_parentAccount = parent;
}

void MyMoneyAccount::setAccountType(const accountTypeE type)
{
  m_accountType = type;
}

void MyMoneyAccount::addAccountId(const TQString& account)
{
  if(!m_accountList.contains(account))
    m_accountList += account;
}

void MyMoneyAccount::removeAccountIds(void)
{
  m_accountList.clear();
}

void MyMoneyAccount::removeAccountId(const TQString& account)
{
  TQStringList::Iterator it;

  it = m_accountList.find(account);
  if(it != m_accountList.end())
    m_accountList.remove(it);
}

MyMoneyAccount::accountTypeE MyMoneyAccount::accountGroup(MyMoneyAccount::accountTypeE type)
{
  switch(type) {
    case MyMoneyAccount::Checkings:
    case MyMoneyAccount::Savings:
    case MyMoneyAccount::Cash:
    case MyMoneyAccount::Currency:
    case MyMoneyAccount::Investment:
    case MyMoneyAccount::MoneyMarket:
    case MyMoneyAccount::CertificateDep:
    case MyMoneyAccount::AssetLoan:
    case MyMoneyAccount::Stock:
      return MyMoneyAccount::Asset;

    case MyMoneyAccount::CreditCard:
    case MyMoneyAccount::Loan:
      return MyMoneyAccount::Liability;

    default:
      return type;
  }
}

bool MyMoneyAccount::operator == (const MyMoneyAccount& right) const
{
  return (MyMoneyKeyValueContainer::operator==(right) &&
      MyMoneyObject::operator==(right) &&
      (m_accountList == right.m_accountList) &&
      (m_accountType == right.m_accountType) &&
      (m_lastModified == right.m_lastModified) &&
      (m_lastReconciliationDate == right.m_lastReconciliationDate) &&
      ((m_name.length() == 0 && right.m_name.length() == 0) || (m_name == right.m_name)) &&
      ((m_number.length() == 0 && right.m_number.length() == 0) || (m_number == right.m_number)) &&
      ((m_description.length() == 0 && right.m_description.length() == 0) || (m_description == right.m_description)) &&
      (m_openingDate == right.m_openingDate) &&
      (m_parentAccount == right.m_parentAccount) &&
      (m_currencyId == right.m_currencyId) &&
      (m_institution == right.m_institution) );
}

MyMoneyAccount::accountTypeE MyMoneyAccount::accountGroup(void) const
{
  return accountGroup(m_accountType);
}

void MyMoneyAccount::setCurrencyId(const TQString& id)
{
  m_currencyId = id;
}

bool MyMoneyAccount::isAssetLiability(void) const
{
  return accountGroup() == Asset || accountGroup() == Liability;
}

bool MyMoneyAccount::isIncomeExpense(void) const
{
  return accountGroup() == Income || accountGroup() == Expense;
}

bool MyMoneyAccount::isLoan(void) const
{
  return accountType() == Loan || accountType() == AssetLoan;
}

bool MyMoneyAccount::isInvest(void) const
{
  return accountType() == Stock;
}


MyMoneyAccountLoan::MyMoneyAccountLoan(const MyMoneyAccount& acc)
 : MyMoneyAccount(acc)
{
}

const MyMoneyMoney MyMoneyAccountLoan::loanAmount(void) const
{
  return MyMoneyMoney(value("loan-amount"));
}

void MyMoneyAccountLoan::setLoanAmount(const MyMoneyMoney& amount)
{
  setValue("loan-amount", amount.toString());
}

const MyMoneyMoney MyMoneyAccountLoan::interestRate(const TQDate& date) const
{
  MyMoneyMoney rate;
  TQString key;
  TQString val;

  if(!date.isValid())
    return rate;

  key.sprintf("ir-%04d-%02d-%02d", date.year(), date.month(), date.day());

  TQRegExp regExp("ir-(\\d{4})-(\\d{2})-(\\d{2})");

  TQMap<TQString, TQString>::ConstIterator it;

  for(it = pairs().begin(); it != pairs().end(); ++it) {
    if(regExp.search(it.key()) > -1) {
      if(it.key() <= key)
        val = *it;
      else
        break;

    } else if(!val.isEmpty())
      break;
  }

  if(!val.isEmpty()) {
    rate = MyMoneyMoney(val);
  }

  return rate;
}

void MyMoneyAccountLoan::setInterestRate(const TQDate& date, const MyMoneyMoney& value)
{
  if(!date.isValid())
    return;

  TQString key;
  key.sprintf("ir-%04d-%02d-%02d", date.year(), date.month(), date.day());
  setValue(key, value.toString());
}

MyMoneyAccountLoan::interestDueE MyMoneyAccountLoan::interestCalculation(void) const
{
  TQString payTime(value("interest-calculation"));
  if(payTime == "paymentDue")
    return paymentDue;
  return paymentReceived;
}

void MyMoneyAccountLoan::setInterestCalculation(const MyMoneyAccountLoan::interestDueE onReception)
{
  if(onReception == paymentDue)
    setValue("interest-calculation", "paymentDue");
  else
    setValue("interest-calculation", "paymentReceived");
}

const TQDate MyMoneyAccountLoan::nextInterestChange(void) const
{
  TQDate rc;

  TQRegExp regExp("(\\d{4})-(\\d{2})-(\\d{2})");
  if(regExp.search(value("interest-nextchange")) != -1) {
    rc.setYMD(regExp.cap(1).toInt(), regExp.cap(2).toInt(), regExp.cap(3).toInt());
  }
  return rc;
}

void MyMoneyAccountLoan::setNextInterestChange(const TQDate& date)
{
  setValue("interest-nextchange", date.toString(TQt::ISODate));
}

int MyMoneyAccountLoan::interestChangeFrequency(int* unit) const
{
  int rc = -1;

  if(unit)
    *unit = 1;

  TQRegExp regExp("(\\d+)/(\\d{1})");
  if(regExp.search(value("interest-changefrequency")) != -1) {
    rc = regExp.cap(1).toInt();
    if(unit != 0) {
      *unit = regExp.cap(2).toInt();
    }
  }
  return rc;
}

void MyMoneyAccountLoan::setInterestChangeFrequency(const int amount, const int unit)
{
  TQString val;
  val.sprintf("%d/%d", amount, unit);
  setValue("interest-changeFrequency", val);
}

const TQString MyMoneyAccountLoan::schedule(void) const
{
  return TQString(value("schedule").latin1());
}

void MyMoneyAccountLoan::setSchedule(const TQString& sched)
{
  setValue("schedule", sched);
}

bool MyMoneyAccountLoan::fixedInterestRate(void) const
{
  // make sure, that an empty kvp element returns true
  return !(value("fixed-interest") == "no");
}

void MyMoneyAccountLoan::setFixedInterestRate(const bool fixed)
{
  setValue("fixed-interest", fixed ? "yes" : "no");
  if(fixed) {
    deletePair("interest-nextchange");
    deletePair("interest-changeFrequency");
  }
}

const MyMoneyMoney MyMoneyAccountLoan::finalPayment(void) const
{
  return MyMoneyMoney(value("final-payment"));
}

void MyMoneyAccountLoan::setFinalPayment(const MyMoneyMoney& finalPayment)
{
  setValue("final-payment", finalPayment.toString());
}

unsigned int MyMoneyAccountLoan::term(void) const
{
  return value("term").toUInt();
}

void MyMoneyAccountLoan::setTerm(const unsigned int payments)
{
  setValue("term", TQString::number(payments));
}

const MyMoneyMoney MyMoneyAccountLoan::periodicPayment(void) const
{
  return MyMoneyMoney(value("periodic-payment"));
}

void MyMoneyAccountLoan::setPeriodicPayment(const MyMoneyMoney& payment)
{
  setValue("periodic-payment", payment.toString());
}

const TQString MyMoneyAccountLoan::payee(void) const
{
  return value("payee");
}

void MyMoneyAccountLoan::setPayee(const TQString& payee)
{
  setValue("payee", payee);
}

const TQString MyMoneyAccountLoan::interestAccountId(void) const
{
  return TQString::null;
}

void MyMoneyAccountLoan::setInterestAccountId(const TQString& /* id */)
{

}

bool MyMoneyAccountLoan::hasReferenceTo(const TQString& id) const
{
  return MyMoneyAccount::hasReferenceTo(id)
         || (id == payee())
         || (id == schedule());
}

void MyMoneyAccountLoan::setInterestCompounding(int frequency)
{
  setValue("compoundingFrequency", TQString("%1").arg(frequency));
}

int MyMoneyAccountLoan::interestCompounding(void) const
{
  return value("compoundingFrequency").toInt();
}

void MyMoneyAccount::writeXML(TQDomDocument& document, TQDomElement& parent) const
{
  TQDomElement el = document.createElement("ACCOUNT");

  writeBaseXML(document, el);

  el.setAttribute("parentaccount", parentAccountId());
  el.setAttribute("lastreconciled", dateToString(lastReconciliationDate()));
  el.setAttribute("lastmodified", dateToString(lastModified()));
  el.setAttribute("institution", institutionId());
  el.setAttribute("opened", dateToString(openingDate()));
  el.setAttribute("number", number());
  // el.setAttribute("openingbalance", openingBalance().toString());
  el.setAttribute("type", accountType());
  el.setAttribute("name", name());
  el.setAttribute("description", description());
  if(!currencyId().isEmpty())
    el.setAttribute("currency", currencyId());

  //Add in subaccount information, if this account has subaccounts.
  if(accountCount())
  {
    TQDomElement subAccounts = document.createElement("SUBACCOUNTS");
    TQStringList::ConstIterator it;
    for(it = accountList().begin(); it != accountList().end(); ++it)
    {
      TQDomElement temp = document.createElement("SUBACCOUNT");
      temp.setAttribute("id", (*it));
      subAccounts.appendChild(temp);
    }

    el.appendChild(subAccounts);
  }

  // Write online banking settings
  if(m_onlineBankingSettings.pairs().count()) {
    TQDomElement onlinesettings = document.createElement("ONLINEBANKING");
    TQMap<TQString,TQString>::const_iterator it_key = m_onlineBankingSettings.pairs().begin();
    while ( it_key != m_onlineBankingSettings.pairs().end() ) {
      onlinesettings.setAttribute(it_key.key(), it_key.data());
      ++it_key;
    }
    el.appendChild(onlinesettings);
  }

  // FIXME drop the lastStatementDate record from the KVP when it is
  // not stored there after setLastReconciliationDate() has been changed
  // See comment there when this will happen
  // deletePair("lastStatementDate");


  //Add in Key-Value Pairs for accounts.
  MyMoneyKeyValueContainer::writeXML(document, el);

  parent.appendChild(el);
}

bool MyMoneyAccount::hasReferenceTo(const TQString& id) const
{
  return (id == m_institution) || (id == m_parentAccount) || (id == m_currencyId);
}

void MyMoneyAccount::setOnlineBankingSettings(const MyMoneyKeyValueContainer& values)
{
  m_onlineBankingSettings = values;
}

const MyMoneyKeyValueContainer& MyMoneyAccount::onlineBankingSettings(void) const
{
  return m_onlineBankingSettings;
}

void MyMoneyAccount::setClosed(bool closed)
{
  if(closed)
    setValue("mm-closed", "yes");
  else
    deletePair("mm-closed");
}

bool MyMoneyAccount::isClosed(void) const
{
  return !(value("mm-closed").isEmpty());
}

int MyMoneyAccount::fraction(const MyMoneySecurity& sec) const
{
  int fraction;
  if(m_accountType == Cash)
    fraction = sec.smallestCashFraction();
  else
    fraction = sec.smallestAccountFraction();
  return fraction;
}

int MyMoneyAccount::fraction(const MyMoneySecurity& sec)
{
  if(m_accountType == Cash)
    m_fraction = sec.smallestCashFraction();
  else
    m_fraction = sec.smallestAccountFraction();
  return m_fraction;
}

int MyMoneyAccount::fraction(void) const
{
  Q_ASSERT(m_fraction != -1);

  return m_fraction;
}

bool MyMoneyAccount::isCategory(void) const
{
  return m_accountType == Income || m_accountType == Expense;
}

TQString MyMoneyAccount::brokerageName(void) const
{
  if(m_accountType == Investment)
    return TQString("%1 (%2)").arg(m_name, i18n("Brokerage (suffix for account names)", "Brokerage"));
  return m_name;
}

void MyMoneyAccount::adjustBalance(const MyMoneySplit& s, bool reverse)
{
  if(s.action() == MyMoneySplit::ActionSplitShares) {
    if(reverse)
      m_balance = m_balance / s.shares();
    else
      m_balance = m_balance * s.shares();
  } else {
    if(reverse)
      m_balance -= s.shares();
    else
      m_balance += s.shares();
  }

}

TQPixmap MyMoneyAccount::accountPixmap(bool reconcileFlag, int size) const
{
  TQString icon;
  switch(accountType()) {
    default:
      if(accountGroup() == MyMoneyAccount::Asset)
        icon = "account-types_asset";
      else
        icon = "account-types_liability";
      break;

    case MyMoneyAccount::Investment:
    case MyMoneyAccount::Stock:
    case MyMoneyAccount::MoneyMarket:
    case MyMoneyAccount::CertificateDep:
      icon = "account-types_investments";
      break;

    case MyMoneyAccount::Checkings:
      icon = "account-types_checking";
      break;
    case MyMoneyAccount::Savings:
      icon = "account-types_savings";
      break;

    case MyMoneyAccount::AssetLoan:
    case MyMoneyAccount::Loan:
      icon = "account-types_loan";
      break;

    case MyMoneyAccount::CreditCard:
      icon = "account-types_credit-card";
      break;

    case MyMoneyAccount::Asset:
      icon = "account-types_asset";
      break;

    case MyMoneyAccount::Cash:
      icon = "account-types_cash";
      break;

    case MyMoneyAccount::Income:
      icon = "account-types_income";
      break;

    case MyMoneyAccount::Expense:
      icon = "account-types_expense";
      break;

    case MyMoneyAccount::Equity:
      icon = "account";
      break;
  }

  TQPixmap result = DesktopIcon(icon, size);
  if(isClosed()) {
    TQPixmap ovly = DesktopIcon("account-types_closed", size);
    bitBlt(&result, 0, 0, &ovly, 0, 0, ovly.width(), ovly.height(), TQt::CopyROP, false);
  } else if(reconcileFlag) {
    TQPixmap ovly = DesktopIcon("account-types_reconcile.png", size);
    bitBlt(&result, 0, 0, &ovly, 0, 0, ovly.width(), ovly.height(), TQt::CopyROP, false);
  } else if(!onlineBankingSettings().value("provider").isEmpty()) {
    TQPixmap ovly = DesktopIcon("account-types_online.png", size);
    bitBlt(&result, 0, 0, &ovly, 0, 0, ovly.width(), ovly.height(), TQt::CopyROP, false);
  }
  return result;
}

TQString MyMoneyAccount::accountTypeToString(const MyMoneyAccount::accountTypeE accountType)
{
  TQString returnString;

  switch (accountType) {
    case MyMoneyAccount::Checkings:
      returnString = i18n("Checking");
      break;
    case MyMoneyAccount::Savings:
      returnString = i18n("Savings");
      break;
    case MyMoneyAccount::CreditCard:
      returnString = i18n("Credit Card");
      break;
    case MyMoneyAccount::Cash:
      returnString = i18n("Cash");
      break;
    case MyMoneyAccount::Loan:
      returnString = i18n("Loan");
      break;
    case MyMoneyAccount::CertificateDep:
      returnString = i18n("Certificate of Deposit");
      break;
    case MyMoneyAccount::Investment:
      returnString = i18n("Investment");
      break;
    case MyMoneyAccount::MoneyMarket:
      returnString = i18n("Money Market");
      break;
    case MyMoneyAccount::Asset:
      returnString = i18n("Asset");
      break;
    case MyMoneyAccount::Liability:
      returnString = i18n("Liability");
      break;
    case MyMoneyAccount::Currency:
      returnString = i18n("Currency");
      break;
    case MyMoneyAccount::Income:
      returnString = i18n("Income");
      break;
    case MyMoneyAccount::Expense:
      returnString = i18n("Expense");
      break;
    case MyMoneyAccount::AssetLoan:
      returnString = i18n("Investment Loan");
      break;
    case MyMoneyAccount::Stock:
      returnString = i18n("Stock");
      break;
    case MyMoneyAccount::Equity:
      returnString = i18n("Equity");
      break;
    default:
      returnString = i18n("Unknown");
  }

  return returnString;
}
