/***************************************************************************
                          kmymoneyutils.cpp  -  description
                             -------------------
    begin                : Wed Feb 5 2003
    copyright            : (C) 2000-2003 by Michael Edwardes
    email                : mte@users.sourceforge.net
                           Javier Campos Morales <javi_c@users.sourceforge.net>
                           Felix Rodriguez <frodriguez@users.sourceforge.net>
                           John C <thetacoturtle@users.sourceforge.net>
                           Thomas Baumgart <ipwizard@users.sourceforge.net>
                           Kevin Tambascio <ktambascio@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

// ----------------------------------------------------------------------------
// TDE Headers

#include <tdelocale.h>
#include <tdeglobal.h>
#include <kiconloader.h>
#include <tdemessagebox.h>
#include <tdeconfig.h>
#include <tdestandarddirs.h>

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

#include <kmymoney/mymoneyfile.h>
#include <kmymoney/mymoneyforecast.h>
#include <kmymoney/kmymoneyglobalsettings.h>
#include <kmymoney/investtransactioneditor.h>

#include "kmymoneyutils.h"

KMyMoneyUtils::KMyMoneyUtils()
{
}

KMyMoneyUtils::~KMyMoneyUtils()
{
}

const TQString KMyMoneyUtils::accountTypeToString(const MyMoneyAccount::accountTypeE accountType)
{
  return MyMoneyAccount::accountTypeToString(accountType);
}

MyMoneyAccount::accountTypeE KMyMoneyUtils::stringToAccountType(const TQString& type)
{
  MyMoneyAccount::accountTypeE rc = MyMoneyAccount::UnknownAccountType;
  TQString tmp = type.lower();

  if(tmp == i18n("Checking").lower())
    rc = MyMoneyAccount::Checkings;
  else if(tmp == i18n("Savings").lower())
    rc = MyMoneyAccount::Savings;
  else if(tmp == i18n("Credit Card").lower())
    rc = MyMoneyAccount::CreditCard;
  else if(tmp == i18n("Cash").lower())
    rc = MyMoneyAccount::Cash;
  else if(tmp == i18n("Loan").lower())
    rc = MyMoneyAccount::Loan;
  else if(tmp == i18n("Certificate of Deposit").lower())
    rc = MyMoneyAccount::CertificateDep;
  else if(tmp == i18n("Investment").lower())
    rc = MyMoneyAccount::Investment;
  else if(tmp == i18n("Money Market").lower())
    rc = MyMoneyAccount::MoneyMarket;
  else if(tmp == i18n("Asset").lower())
    rc = MyMoneyAccount::Asset;
  else if(tmp == i18n("Liability").lower())
    rc = MyMoneyAccount::Liability;
  else if(tmp == i18n("Currency").lower())
    rc = MyMoneyAccount::Currency;
  else if(tmp == i18n("Income").lower())
    rc = MyMoneyAccount::Income;
  else if(tmp == i18n("Expense").lower())
    rc = MyMoneyAccount::Expense;
  else if(tmp == i18n("Investment Loan").lower())
    rc = MyMoneyAccount::AssetLoan;
  else if(tmp == i18n("Stock").lower())
    rc = MyMoneyAccount::Stock;
  else if(tmp == i18n("Equity").lower())
    rc = MyMoneyAccount::Equity;

  return rc;
}

MyMoneySecurity::eSECURITYTYPE KMyMoneyUtils::stringToSecurity(const TQString& txt)
{
  MyMoneySecurity::eSECURITYTYPE rc = MyMoneySecurity::SECURITY_NONE;
  TQString tmp = txt.lower();

  if(tmp == i18n("Stock").lower())
    rc = MyMoneySecurity::SECURITY_STOCK;
  else if(tmp == i18n("Mutual Fund").lower())
    rc = MyMoneySecurity::SECURITY_MUTUALFUND;
  else if(tmp == i18n("Bond").lower())
    rc = MyMoneySecurity::SECURITY_BOND;
  else if(tmp == i18n("Currency").lower())
    rc = MyMoneySecurity::SECURITY_CURRENCY;

  return rc;
}

const TQString KMyMoneyUtils::securityTypeToString(const MyMoneySecurity::eSECURITYTYPE securityType)
{
  return i18n(MyMoneySecurity::securityTypeToString(securityType).utf8());
}

const TQString KMyMoneyUtils::occurenceToString(const MyMoneySchedule::occurenceE occurence)
{
  return i18n(MyMoneySchedule::occurenceToString(occurence).utf8());
}

const TQString KMyMoneyUtils::paymentMethodToString(MyMoneySchedule::paymentTypeE paymentType)
{
  return i18n(MyMoneySchedule::paymentMethodToString(paymentType).utf8());
}

const TQString KMyMoneyUtils::weekendOptionToString(MyMoneySchedule::weekendOptionE weekendOption)
{
  return i18n(MyMoneySchedule::weekendOptionToString(weekendOption).utf8());
}

const TQString KMyMoneyUtils::scheduleTypeToString(MyMoneySchedule::typeE type)
{
  return i18n(MyMoneySchedule::scheduleTypeToString(type).utf8());
}

KGuiItem KMyMoneyUtils::scheduleNewGuiItem(void)
{
  TDEIconLoader *ic = TDEGlobal::iconLoader();

  KGuiItem splitGuiItem(  i18n("&New Schedule..."),
                          TQIconSet(ic->loadIcon("document-new", TDEIcon::Small, TDEIcon::SizeSmall)),
                          i18n("Create a new schedule."),
                          i18n("Use this to create a new schedule."));

  return splitGuiItem;
}

KGuiItem KMyMoneyUtils::accountsFilterGuiItem(void)
{
  TDEIconLoader *ic = TDEGlobal::iconLoader();

  KGuiItem splitGuiItem(  i18n("&Filter"),
                          TQIconSet(ic->loadIcon("filter", TDEIcon::Small, TDEIcon::SizeSmall)),
                          i18n("Filter out accounts"),
                          i18n("Use this to filter out accounts"));

  return splitGuiItem;
}

TQPixmap KMyMoneyUtils::billScheduleIcon(int size)
{
  TDEIconLoader *ic = TDEGlobal::iconLoader();
  return ic->loadIcon("billschedule", TDEIcon::User, size);
}

TQPixmap KMyMoneyUtils::depositScheduleIcon(int size)
{
  TDEIconLoader *ic = TDEGlobal::iconLoader();
  return ic->loadIcon("depositschedule", TDEIcon::User, size);
}

TQPixmap KMyMoneyUtils::transferScheduleIcon(int size)
{
  TDEIconLoader *ic = TDEGlobal::iconLoader();
  return ic->loadIcon("transferschedule", TDEIcon::User, size);
}

TQPixmap KMyMoneyUtils::scheduleIcon(int size)
{
  TDEIconLoader *ic = TDEGlobal::iconLoader();
  return ic->loadIcon("schedule", TDEIcon::User, size);
}

const char* homePageItems[] = {
  I18N_NOOP("Payments"),
  I18N_NOOP("Preferred accounts"),
  I18N_NOOP("Payment accounts"),
  I18N_NOOP("Favorite reports"),
  I18N_NOOP("Forecast (schedule)"),
  I18N_NOOP("Networth forecast"),
  I18N_NOOP("Forecast (history)"),
  I18N_NOOP("Assets and Liabilities"),
  I18N_NOOP("Budget"),
  I18N_NOOP("CashFlow"),
  // insert new items above this comment
  0
};

const TQString KMyMoneyUtils::homePageItemToString(const int idx)
{
  TQString rc;
  if(abs(idx) > 0 && abs(idx) < static_cast<int>(sizeof(homePageItems)/sizeof(homePageItems[0]))) {
    rc = i18n(homePageItems[abs(idx-1)]);
  }
  return rc;
}

int KMyMoneyUtils::stringToHomePageItem(const TQString& txt)
{
  int idx = 0;
  for(idx = 0; homePageItems[idx] != 0; ++idx) {
    if(txt == i18n(homePageItems[idx]))
      return idx+1;
  }
  return 0;
}

bool KMyMoneyUtils::appendCorrectFileExt(TQString& str, const TQString& strExtToUse)
{
  bool rc = false;

  if(!str.isEmpty()) {
    //find last . delminator
    int nLoc = str.findRev('.');
    if(nLoc != -1) {
      TQString strExt, strTemp;
      strTemp = str.left(nLoc + 1);
      strExt = str.right(str.length() - (nLoc + 1));
      if(strExt.find(strExtToUse, 0, false) == -1) {
        // if the extension given contains a period, we remove our's
        if(strExtToUse.find('.') != -1)
          strTemp = strTemp.left(strTemp.length()-1);
        //append extension to make complete file name
        strTemp.append(strExtToUse);
        str = strTemp;
        rc = true;
      }
    } else {
      str.append(".");
      str.append(strExtToUse);
      rc = true;
    }
  }
  return rc;
}

void KMyMoneyUtils::checkConstants(void)
{
  Q_ASSERT(static_cast<int>(TDELocale::ParensAround) == static_cast<int>(MyMoneyMoney::ParensAround));
  Q_ASSERT(static_cast<int>(TDELocale::BeforeQuantityMoney) == static_cast<int>(MyMoneyMoney::BeforeQuantityMoney));
  Q_ASSERT(static_cast<int>(TDELocale::AfterQuantityMoney) == static_cast<int>(MyMoneyMoney::AfterQuantityMoney));
  Q_ASSERT(static_cast<int>(TDELocale::BeforeMoney) == static_cast<int>(MyMoneyMoney::BeforeMoney));
  Q_ASSERT(static_cast<int>(TDELocale::AfterMoney) == static_cast<int>(MyMoneyMoney::AfterMoney));
}

TQString KMyMoneyUtils::variableCSS(void)
{
  TQColor tcolor = TDEGlobalSettings::textColor();

  TQString css;
  css += "<style type=\"text/css\">\n<!--\n";
  css += TQString(".row-even, .item0 { background-color: %1; color: %2 }\n")
    .arg((KMyMoneyGlobalSettings::listBGColor()).name()).arg(tcolor.name());
  css += TQString(".row-odd, .item1  { background-color: %1; color: %2 }\n")
    .arg((KMyMoneyGlobalSettings::listColor()).name()).arg(tcolor.name());
  css += "-->\n</style>\n";
  return css;
}

TQString KMyMoneyUtils::findResource(const char* type, const TQString& filename)
{
  TQString language = TDEGlobal::locale()->language();
  TQString country = TDEGlobal::locale()->country();
  TQString rc, mask;

  // check that the placeholder is present
  if(!filename.find("%1")) {
    tqWarning(TQString("%%1 not found in '%1'").arg(filename));
    return filename;
  }

  // search the given resource
  mask = filename.arg("_%1.%2");
  rc = TDEGlobal::dirs()->findResource(type, mask.arg(country).arg(language));
  if(rc.isEmpty()) {
    mask = filename.arg("_%1");
    rc = TDEGlobal::dirs()->findResource(type, mask.arg(language));
  }
  if(rc.isEmpty()) {
    // tqDebug(TQString("html/home_%1.html not found").arg(country));
    rc = TDEGlobal::dirs()->findResource(type, mask.arg(country));
  }
  if(rc.isEmpty()) {
    rc = TDEGlobal::dirs()->findResource(type, filename.arg(""));
  }

  if(rc.isEmpty()) {
    tqWarning(TQString("No resource found for (%1,%2)").arg(type).arg(filename));
  }
  return rc;
}

const MyMoneySplit KMyMoneyUtils::stockSplit(const MyMoneyTransaction& t)
{
  TQValueList<MyMoneySplit>::ConstIterator it_s;
  MyMoneySplit investmentAccountSplit;
  for(it_s = t.splits().begin(); it_s != t.splits().end(); ++it_s) {
    if(!(*it_s).accountId().isEmpty()) {
      MyMoneyAccount acc = MyMoneyFile::instance()->account((*it_s).accountId());
      if(acc.isInvest()) {
        return *it_s;
      }
      // if we have a reference to an investment account, we remember it here
      if(acc.accountType() == MyMoneyAccount::Investment)
        investmentAccountSplit = *it_s;
    }
  }
  // if we haven't found a stock split, we see if we've seen
  // an investment account on the way. If so, we return it.
  if(!investmentAccountSplit.id().isEmpty())
    return investmentAccountSplit;

  // if none was found, we return an empty split.
  return MyMoneySplit();
}

KMyMoneyUtils::transactionTypeE KMyMoneyUtils::transactionType(const MyMoneyTransaction& t)
{
  if(!stockSplit(t).id().isEmpty())
    return InvestmentTransaction;

  if(t.splitCount() < 2) {
    return Unknown;
  } else if(t.splitCount() > 2) {
    // FIXME check for loan transaction here
    return SplitTransaction;
  }
  TQString ida, idb;
  ida = t.splits()[0].accountId();
  idb = t.splits()[1].accountId();
  if(ida.isEmpty() || idb.isEmpty())
    return Unknown;

  MyMoneyAccount a, b;
  a = MyMoneyFile::instance()->account(ida);
  b = MyMoneyFile::instance()->account(idb);
  if((a.accountGroup() == MyMoneyAccount::Asset
   || a.accountGroup() == MyMoneyAccount::Liability)
  && (b.accountGroup() == MyMoneyAccount::Asset
   || b.accountGroup() == MyMoneyAccount::Liability))
    return Transfer;
  return Normal;
}

void KMyMoneyUtils::calculateAutoLoan(const MyMoneySchedule& schedule, MyMoneyTransaction& transaction, const TQMap<TQString, MyMoneyMoney>& balances)
{
  try {
    MyMoneyForecast::calculateAutoLoan(schedule, transaction, balances);
  } catch (MyMoneyException* e) {
    KMessageBox::detailedError(0, i18n("Unable to load schedule details"), e->what());
    delete e;
  }
}

TQString KMyMoneyUtils::nextCheckNumber(const MyMoneyAccount& acc)
{
  // determine next check number
  TQString number;
  TQRegExp exp(TQString("(.*\\D)?(\\d+)(\\D.*)?"));
  if(exp.search(acc.value("lastNumberUsed")) != -1) {
    number = TQString("%1%2%3").arg(exp.cap(1)).arg(exp.cap(2).toULongLong() + 1).arg(exp.cap(3));
  } else {
    number = "1";
  }
  return number;
}

TQString KMyMoneyUtils::reconcileStateToString(MyMoneySplit::reconcileFlagE flag, bool text)
{
  TQString txt;
  if(text) {
    switch(flag) {
      case MyMoneySplit::NotReconciled:
        txt = i18n("Reconcile state 'Not reconciled'", "Not reconciled");
        break;
      case MyMoneySplit::Cleared:
        txt = i18n("Reconcile state 'Cleared'", "Cleared");
        break;
      case MyMoneySplit::Reconciled:
        txt = i18n("Reconcile state 'Reconciled'", "Reconciled");
        break;
      case MyMoneySplit::Frozen:
        txt = i18n("Reconcile state 'Frozen'", "Frozen");
        break;
      default:
        txt = i18n("Unknown");
        break;
    }
  } else {
    switch(flag) {
      case MyMoneySplit::NotReconciled:
        break;
      case MyMoneySplit::Cleared:
        txt = i18n("Reconcile flag C", "C");
        break;
      case MyMoneySplit::Reconciled:
        txt = i18n("Reconcile flag R", "R");
        break;
      case MyMoneySplit::Frozen:
        txt = i18n("Reconcile flag F", "F");
        break;
      default:
        txt = i18n("Flag for unknown reconciliation state", "?");
        break;
    }
  }
  return txt;
}

MyMoneyTransaction KMyMoneyUtils::scheduledTransaction(const MyMoneySchedule& schedule)
{
  MyMoneyTransaction t = schedule.transaction();

  try {
    if (schedule.type() == MyMoneySchedule::TYPE_LOANPAYMENT) {
      calculateAutoLoan(schedule, t, TQMap<TQString, MyMoneyMoney>());
    }
  } catch (MyMoneyException* e) {
    tqDebug(TQString("Unable to load schedule details for '%1' during transaction match: %1").arg(schedule.name()).arg(e->what()));
    delete e;
  }

  t.clearId();
  t.setEntryDate(TQDate());
  return t;
}

void KMyMoneyUtils::previouslyUsedCategories(const TQString& investmentAccount, TQString& feesId, TQString& interestId)
{
  feesId = interestId = TQString();
  MyMoneyFile* file = MyMoneyFile::instance();
  try {
    MyMoneyAccount acc = file->account(investmentAccount);
    MyMoneyTransactionFilter filter(investmentAccount);
    filter.setReportAllSplits(false);
    // since we assume an investment account here, we need to collect the stock accounts as well
    filter.addAccount(acc.accountList());
    TQValueList< TQPair<MyMoneyTransaction, MyMoneySplit> > list;
    file->transactionList(list, filter);
    TQValueList< TQPair<MyMoneyTransaction, MyMoneySplit> >::const_iterator it_t;
    for(it_t = list.begin(); it_t != list.end(); ++it_t) {
      const MyMoneyTransaction& t = (*it_t).first;
      const MyMoneySplit&s = (*it_t).second;
      MyMoneySplit assetAccountSplit;
      TQValueList<MyMoneySplit> feeSplits;
      TQValueList<MyMoneySplit> interestSplits;
      MyMoneySecurity security;
      MyMoneySecurity currency;
      MyMoneySplit::investTransactionTypeE transactionType;
      InvestTransactionEditor::dissectTransaction(t, s, assetAccountSplit, feeSplits, interestSplits, security, currency, transactionType);
      if(feeSplits.count() == 1) {
        feesId = feeSplits.first().accountId();
      }
      if(interestSplits.count() == 1) {
        interestId = interestSplits.first().accountId();
      }
    }
  } catch(MyMoneyException *e) {
    delete e;
  }

}


