/***************************************************************************
                             investtransactioneditor.cpp
                             ----------
    begin                : Fri Dec 15 2006
    copyright            : (C) 2006 by Thomas Baumgart
    email                : Thomas Baumgart <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

#include <typeinfo>

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

#include <tqlabel.h>
#include <tqapplication.h>
#include <tqeventloop.h>
#include <tqradiobutton.h>
#include <tqbuttongroup.h>

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

#include <ktextedit.h>
#include <tdelocale.h>
#include <kcombobox.h>
#include <kpushbutton.h>
#include <tdemessagebox.h>
#include <kstdguiitem.h>

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

#include <kmymoney/investtransactioneditor.h>
#include <kmymoney/kmymoneycategory.h>
#include <kmymoney/kmymoneydateinput.h>
#include <kmymoney/kmymoneyedit.h>
#include <kmymoney/kmymoneylineedit.h>
#include <kmymoney/kmymoneyaccountcompletion.h>
#include <kmymoney/kmymoneyaccountselector.h>
#include <kmymoney/mymoneyfile.h>
#include <kmymoney/transactionform.h>

#include "../dialogs/ksplittransactiondlg.h"
#include "../dialogs/kcurrencycalculator.h"

#include "../kmymoneyglobalsettings.h"

#include "investactivities.h"

using namespace KMyMoneyRegister;
using namespace KMyMoneyTransactionForm;
using namespace Invest;

class InvestTransactionEditor::Private {
  friend class Invest::Activity;

public:
  Private(InvestTransactionEditor* parent) :
    m_parent(parent),
    m_activity(0)
  {
    m_phonyAccount = MyMoneyAccount("Phony-ID", MyMoneyAccount());
  }

  ~Private() {
    delete m_activity;
  }

  TQWidget* haveWidget(const TQString& name) { return m_parent->haveWidget(name); }

  InvestTransactionEditor* m_parent;
  Activity*                m_activity;
  MyMoneyAccount           m_phonyAccount;
  MyMoneySplit             m_phonySplit;
};


InvestTransactionEditor::InvestTransactionEditor() :
  d(new Private(this))
{
}

InvestTransactionEditor::~InvestTransactionEditor()
{
  delete d;
}

InvestTransactionEditor::InvestTransactionEditor(TransactionEditorContainer* regForm, KMyMoneyRegister::InvestTransaction* item, const KMyMoneyRegister::SelectedTransactions& list, const TQDate& lastPostDate) :
  TransactionEditor(regForm, item, list, lastPostDate),
  d(new Private(this))
{
  // dissect the transaction into its type, splits, currency, security etc.
  dissectTransaction(m_transaction, m_split,
                     m_assetAccountSplit,
                     m_feeSplits,
                     m_interestSplits,
                     m_security,
                     m_currency,
                     m_transactionType);

  // determine initial activity object
  activityFactory(m_transactionType);
}

void InvestTransactionEditor::activityFactory(MyMoneySplit::investTransactionTypeE type)
{
  if(!d->m_activity || type != d->m_activity->type()) {
    delete d->m_activity;
    switch(type) {
      default:
      case MyMoneySplit::BuyShares:
        d->m_activity = new Buy(this);
        break;
      case MyMoneySplit::SellShares:
        d->m_activity = new Sell(this);
        break;
      case MyMoneySplit::Dividend:
      case MyMoneySplit::Yield:
        d->m_activity = new Div(this);
        break;
      case MyMoneySplit::ReinvestDividend:
        d->m_activity = new Reinvest(this);
        break;
      case MyMoneySplit::AddShares:
        d->m_activity = new Add(this);
        break;
      case MyMoneySplit::RemoveShares:
        d->m_activity = new Remove(this);
        break;
      case MyMoneySplit::SplitShares:
        d->m_activity = new Split(this);
        break;
    }
  }
}

void InvestTransactionEditor::dissectTransaction(const MyMoneyTransaction& transaction, const MyMoneySplit& split, MyMoneySplit& assetAccountSplit, TQValueList<MyMoneySplit>& feeSplits, TQValueList<MyMoneySplit>& interestSplits, MyMoneySecurity& security, MyMoneySecurity& currency, MyMoneySplit::investTransactionTypeE& transactionType)
{
  // collect the splits. split references the stock account and should already
  // be set up. assetAccountSplit references the corresponding asset account (maybe
  // empty), feeSplits is the list of all expenses and interestSplits
  // the list of all incomes
  MyMoneyFile* file = MyMoneyFile::instance();
  TQValueList<MyMoneySplit>::ConstIterator it_s;
  for(it_s = transaction.splits().begin(); it_s != transaction.splits().end(); ++it_s) {
    MyMoneyAccount acc = file->account((*it_s).accountId());
    if((*it_s).id() == split.id()) {
      security = file->security(acc.currencyId());
    } else if(acc.accountGroup() == MyMoneyAccount::Expense) {
      feeSplits.append(*it_s);
      // feeAmount += (*it_s).value();
    } else if(acc.accountGroup() == MyMoneyAccount::Income) {
      interestSplits.append(*it_s);
      // interestAmount += (*it_s).value();
    } else {
      assetAccountSplit = *it_s;
    }
  }

  // determine transaction type
  if(split.action() == MyMoneySplit::ActionAddShares) {
    transactionType = (!split.shares().isNegative()) ? MyMoneySplit::AddShares : MyMoneySplit::RemoveShares;
  } else if(split.action() == MyMoneySplit::ActionBuyShares) {
    transactionType = (!split.value().isNegative()) ? MyMoneySplit::BuyShares : MyMoneySplit::SellShares;
  } else if(split.action() == MyMoneySplit::ActionDividend) {
    transactionType = MyMoneySplit::Dividend;
  } else if(split.action() == MyMoneySplit::ActionReinvestDividend) {
    transactionType = MyMoneySplit::ReinvestDividend;
  } else if(split.action() == MyMoneySplit::ActionYield) {
    transactionType = MyMoneySplit::Yield;
  } else if(split.action() == MyMoneySplit::ActionSplitShares) {
    transactionType = MyMoneySplit::SplitShares;
  } else
    transactionType = MyMoneySplit::BuyShares;

  currency.setTradingSymbol("???");
  try {
    currency = file->security(transaction.commodity());
  } catch(MyMoneyException *e) {
    delete e;
  }
}

void InvestTransactionEditor::createEditWidgets(void)
{
  KMyMoneyActivityCombo* activity = new KMyMoneyActivityCombo();
  m_editWidgets["activity"] = activity;
  connect(activity, TQ_SIGNAL(activitySelected(MyMoneySplit::investTransactionTypeE)), this, TQ_SLOT(slotUpdateActivity(MyMoneySplit::investTransactionTypeE)));
  connect(activity, TQ_SIGNAL(activitySelected(MyMoneySplit::investTransactionTypeE)), this, TQ_SLOT(slotUpdateButtonState()));

  m_editWidgets["postdate"] = new kMyMoneyDateInput;
  connect(m_editWidgets["postdate"], TQ_SIGNAL(dateChanged(const TQDate&)), this, TQ_SLOT(slotUpdateButtonState()));

  KMyMoneySecurity* security = new KMyMoneySecurity;
  security->setHint(i18n("Security"));
  m_editWidgets["security"] = security;
  connect(security, TQ_SIGNAL(itemSelected(const TQString&)), this, TQ_SLOT(slotUpdateSecurity(const TQString&)));
  connect(security, TQ_SIGNAL(textChanged(const TQString&)), this, TQ_SLOT(slotUpdateButtonState()));
  connect(security, TQ_SIGNAL(createItem(const TQString&, TQString&)), this, TQ_SLOT(slotCreateSecurity(const TQString&, TQString&)));
  connect(security, TQ_SIGNAL(objectCreation(bool)), this, TQ_SIGNAL(objectCreation(bool)));

  KMyMoneyCategory* asset = new KMyMoneyCategory(0, 0, false);
  asset->setHint(i18n("Asset account"));
  m_editWidgets["asset-account"] = asset;
  connect(asset, TQ_SIGNAL(textChanged(const TQString&)), this, TQ_SLOT(slotUpdateButtonState()));
  connect(asset, TQ_SIGNAL(objectCreation(bool)), this, TQ_SIGNAL(objectCreation(bool)));

  KMyMoneyCategory* fees = new KMyMoneyCategory(0, 0, true);
  fees->setHint(i18n("Fees"));
  m_editWidgets["fee-account"] = fees;
  connect(fees, TQ_SIGNAL(itemSelected(const TQString&)), this, TQ_SLOT(slotUpdateFeeCategory(const TQString&)));
  connect(fees, TQ_SIGNAL(textChanged(const TQString&)), this, TQ_SLOT(slotUpdateButtonState()));
  connect(fees, TQ_SIGNAL(textChanged(const TQString&)), this, TQ_SLOT(slotUpdateFeeVisibility(const TQString&)));
  connect(fees, TQ_SIGNAL(createItem(const TQString&, TQString&)), this, TQ_SLOT(slotCreateFeeCategory(const TQString&, TQString&)));
  connect(fees, TQ_SIGNAL(objectCreation(bool)), this, TQ_SIGNAL(objectCreation(bool)));
  connect(fees->splitButton(), TQ_SIGNAL(clicked()), this, TQ_SLOT(slotEditFeeSplits()));

  KMyMoneyCategory* interest = new KMyMoneyCategory(0, 0, true);
  interest->setHint(i18n("Interest"));
  m_editWidgets["interest-account"] = interest;
  connect(interest, TQ_SIGNAL(itemSelected(const TQString&)), this, TQ_SLOT(slotUpdateInterestCategory(const TQString&)));
  connect(interest, TQ_SIGNAL(textChanged(const TQString&)), this, TQ_SLOT(slotUpdateButtonState()));
  connect(interest, TQ_SIGNAL(textChanged(const TQString&)), this, TQ_SLOT(slotUpdateInterestVisibility(const TQString&)));
  connect(interest, TQ_SIGNAL(createItem(const TQString&, TQString&)), this, TQ_SLOT(slotCreateInterestCategory(const TQString&, TQString&)));
  connect(interest, TQ_SIGNAL(objectCreation(bool)), this, TQ_SIGNAL(objectCreation(bool)));
  connect(interest->splitButton(), TQ_SIGNAL(clicked()), this, TQ_SLOT(slotEditInterestSplits()));

  KTextEdit* memo = new KTextEdit;
  memo->setTabChangesFocus(true);
  m_editWidgets["memo"] = memo;

  kMyMoneyEdit* value = new kMyMoneyEdit;
  value->setHint(i18n("Shares"));
  value->setResetButtonVisible(false);
  m_editWidgets["shares"] = value;
  connect(value, TQ_SIGNAL(textChanged(const TQString&)), this, TQ_SLOT(slotUpdateButtonState()));
  connect(value, TQ_SIGNAL(valueChanged(const TQString&)), this, TQ_SLOT(slotUpdateTotalAmount()));

  value = new kMyMoneyEdit;
  value->setHint(i18n("Price"));
  value->setResetButtonVisible(false);
  value->setPrecision(KMyMoneyGlobalSettings::pricePrecision());
  m_editWidgets["price"] = value;
  connect(value, TQ_SIGNAL(textChanged(const TQString&)), this, TQ_SLOT(slotUpdateButtonState()));
  connect(value, TQ_SIGNAL(valueChanged(const TQString&)), this, TQ_SLOT(slotUpdateTotalAmount()));

  value = new kMyMoneyEdit;
  // TODO once we have the selected transactions as array of Transaction
  // we can allow multiple splits for fee and interest
  value->setResetButtonVisible(false);
  m_editWidgets["fee-amount"] = value;
  connect(value, TQ_SIGNAL(textChanged(const TQString&)), this, TQ_SLOT(slotUpdateButtonState()));
  connect(value, TQ_SIGNAL(valueChanged(const TQString&)), this, TQ_SLOT(slotUpdateTotalAmount()));

  value = new kMyMoneyEdit;
  // TODO once we have the selected transactions as array of Transaction
  // we can allow multiple splits for fee and interest
  value->setResetButtonVisible(false);
  m_editWidgets["interest-amount"] = value;
  connect(value, TQ_SIGNAL(textChanged(const TQString&)), this, TQ_SLOT(slotUpdateButtonState()));
  connect(value, TQ_SIGNAL(valueChanged(const TQString&)), this, TQ_SLOT(slotUpdateTotalAmount()));

  KMyMoneyReconcileCombo* reconcile = new KMyMoneyReconcileCombo;
  m_editWidgets["status"] = reconcile;
  connect(reconcile, TQ_SIGNAL(itemSelected(const TQString&)), this, TQ_SLOT(slotUpdateButtonState()));

  KMyMoneyRegister::TQWidgetContainer::iterator it_w;
  for(it_w = m_editWidgets.begin(); it_w != m_editWidgets.end(); ++it_w) {
    (*it_w)->installEventFilter(this);
  }

  TQLabel* label = new TQLabel("", 0);
  label->setAlignment(TQt::AlignVCenter | TQt::AlignRight | TQt::DontClip);
  m_editWidgets["total"] = label;

  label = new TQLabel("", 0);
  label->setAlignment(TQt::AlignVCenter | TQt::DontClip);
  m_editWidgets["total-label"] = label;

  label = new TQLabel("", 0);
  label->setAlignment(TQt::AlignVCenter | TQt::DontClip);
  m_editWidgets["asset-label"] = label;

  label = new TQLabel("", 0);
  label->setAlignment(TQt::AlignVCenter | TQt::DontClip);
  m_editWidgets["fee-label"] = label;

  label = new TQLabel("", 0);
  label->setAlignment(TQt::AlignVCenter | TQt::DontClip);
  m_editWidgets["fee-amount-label"] = label;

  label = new TQLabel("", 0);
  label->setAlignment(TQt::AlignVCenter | TQt::DontClip);
  m_editWidgets["interest-label"] = label;

  label = new TQLabel("", 0);
  label->setAlignment(TQt::AlignVCenter | TQt::DontClip);
  m_editWidgets["interest-amount-label"] = label;

  label = new TQLabel("", 0);
  label->setAlignment(TQt::AlignVCenter | TQt::DontClip);
  m_editWidgets["price-label"] = label;

  label = new TQLabel("", 0);
  label->setAlignment(TQt::AlignVCenter | TQt::DontClip);
  m_editWidgets["shares-label"] = label;

  // if we don't have more than 1 selected transaction, we don't need
  // the "don't change" item in some of the combo widgets
  if(m_transactions.count() < 2) {
    reconcile->removeDontCare();
  }
}

int InvestTransactionEditor::slotEditFeeSplits(void)
{
  return editSplits("fee-account", "fee-amount", m_feeSplits, false, TQ_SLOT(slotEditFeeSplits()));
}

int InvestTransactionEditor::slotEditInterestSplits(void)
{
  return editSplits("interest-account", "interest-amount", m_interestSplits, true, TQ_SLOT(slotEditInterestSplits()));
}

int InvestTransactionEditor::editSplits(const TQString& categoryWidgetName, const TQString& amountWidgetName, TQValueList<MyMoneySplit>& splits, bool isIncome, const char* slotEditSplits)
{
  int rc = TQDialog::Rejected;

  if(!m_openEditSplits) {
  // only get in here in a single instance
    m_openEditSplits = true;

    // force focus change to update all data
    KMyMoneyCategory* category = dynamic_cast<KMyMoneyCategory*>(m_editWidgets[categoryWidgetName]);
    TQWidget* w = category->splitButton();
    if(w)
      w->setFocus();

    kMyMoneyEdit* amount = dynamic_cast<kMyMoneyEdit*>(haveWidget(amountWidgetName));

    MyMoneyTransaction transaction;
    transaction.setCommodity(m_currency.id());
    if(splits.count() == 0 && !category->selectedItem().isEmpty()) {
      MyMoneySplit s;
      s.setAccountId(category->selectedItem());
      s.setShares(amount->value());
      s.setValue(s.shares());
      splits << s;
    }
    // use the transactions commodity as the currency indicator for the splits
    // this is used to allow some useful setting for the fractions in the amount fields
    try {
      d->m_phonyAccount.setCurrencyId(m_transaction.commodity());
      d->m_phonyAccount.fraction(MyMoneyFile::instance()->security(m_transaction.commodity()));
    } catch(MyMoneyException *e) {
      tqDebug("Unable to setup precision");
      delete e;
    }

    if(createPseudoTransaction(transaction, splits)) {
      MyMoneyMoney value;

      KSplitTransactionDlg* dlg = new KSplitTransactionDlg(transaction,
                                                          d->m_phonySplit,
                                                          d->m_phonyAccount,
                                                          false,
                                                          isIncome,
                                                          0,
                                                          m_priceInfo,
                                                          m_regForm);
      // connect(dlg, TQ_SIGNAL(newCategory(MyMoneyAccount&)), this, TQ_SIGNAL(newCategory(MyMoneyAccount&)));

      if((rc = dlg->exec()) == TQDialog::Accepted) {
        transaction = dlg->transaction();
        // collect splits out of the transaction
        splits.clear();
        TQValueList<MyMoneySplit>::const_iterator it_s;
        MyMoneyMoney fees;
        for(it_s = transaction.splits().begin(); it_s != transaction.splits().end(); ++it_s) {
          if((*it_s).accountId() == d->m_phonyAccount.id())
            continue;
          splits << *it_s;
          fees += (*it_s).shares();
        }
        if(isIncome)
          fees = -fees;

        TQString categoryId;
        setupCategoryWidget(category, splits, categoryId, slotEditSplits);
        amount->setValue(fees);
        slotUpdateTotalAmount();
      }

      delete dlg;
    }

    // focus jumps into the memo field
    if((w = haveWidget("memo")) != 0) {
      w->setFocus();
    }

    m_openEditSplits = false;
  }
  return rc;
}

bool InvestTransactionEditor::createPseudoTransaction(MyMoneyTransaction& t, const TQValueList<MyMoneySplit>& splits)
{
  t.removeSplits();

  MyMoneySplit split;
  split.setAccountId(d->m_phonyAccount.id());
  split.setValue(-subtotal(splits));
  split.setShares(split.value());
  t.addSplit(split);
  d->m_phonySplit = split;

  TQValueList<MyMoneySplit>::const_iterator it_s;
  for(it_s = splits.begin(); it_s != splits.end(); ++it_s) {
    split = *it_s;
    split.clearId();
    t.addSplit(split);
  }
  return true;
}

void InvestTransactionEditor::slotCreateSecurity(const TQString& name, TQString& id)
{
  MyMoneyAccount acc;
  TQRegExp exp("([^:]+)");
  if(exp.search(name) != -1) {
    acc.setName(exp.cap(1));

    emit createSecurity(acc, m_account);

    // return id
    id = acc.id();

    if(!id.isEmpty()) {
      slotUpdateSecurity(id);
    }
  }
}

void InvestTransactionEditor::slotCreateFeeCategory(const TQString& name, TQString& id)
{
  MyMoneyAccount acc;
  acc.setName(name);

  emit createCategory(acc, MyMoneyFile::instance()->expense());

  // return id
  id = acc.id();
}

void InvestTransactionEditor::slotUpdateFeeCategory(const TQString& id)
{
  haveWidget("fee-amount")->setDisabled(id.isEmpty());
}

void InvestTransactionEditor::slotUpdateFeeVisibility(const TQString& txt)
{
  haveWidget("fee-amount")->setHidden(txt.isEmpty());
  TQWidget* w = haveWidget("fee-amount-label");
  if(w)
    w->setShown(haveWidget("fee-amount")->isVisible());
}

void InvestTransactionEditor::slotUpdateInterestCategory(const TQString& id)
{
  haveWidget("interest-amount")->setDisabled(id.isEmpty());
}

void InvestTransactionEditor::slotUpdateInterestVisibility(const TQString& txt)
{
  KMyMoneyCategory* interest = dynamic_cast<KMyMoneyCategory*>(haveWidget("interest-account"));
  TQWidget* w = haveWidget("interest-amount-label");

  if(dynamic_cast<Reinvest*>(d->m_activity)) {
    interest->splitButton()->hide();
    haveWidget("interest-amount")->setHidden(true);
    // for the reinvest case, we don't ever hide the label do avoid a shine through
    // of the underlying transaction data.
    w = 0;
  } else {
    haveWidget("interest-amount")->setHidden(txt.isEmpty());
    // FIXME once we can handle split interest, we need to uncomment the next line
    // interest->splitButton()->show();
  }

  if(w)
    w->setShown(haveWidget("interest-amount")->isVisible());
}

void InvestTransactionEditor::slotCreateInterestCategory(const TQString& name, TQString& id)
{
  MyMoneyAccount acc;
  acc.setName(name);

  emit createCategory(acc, MyMoneyFile::instance()->income());

  // return id
  id = acc.id();
}

void InvestTransactionEditor::slotReloadEditWidgets(void)
{
  KMyMoneyCategory* interest = dynamic_cast<KMyMoneyCategory*>(haveWidget("interest-account"));
  KMyMoneyCategory* fees = dynamic_cast<KMyMoneyCategory*>(haveWidget("fee-account"));
  KMyMoneySecurity* security = dynamic_cast<KMyMoneySecurity*>(haveWidget("security"));

  AccountSet aSet;
  TQString id;

  // interest-account
  aSet.clear();
  aSet.addAccountGroup(MyMoneyAccount::Income);
  aSet.load(interest->selector());
  setupCategoryWidget(interest, m_interestSplits, id, TQ_SLOT(slotEditInterestSplits()));

  // fee-account
  aSet.clear();
  aSet.addAccountGroup(MyMoneyAccount::Expense);
  aSet.load(fees->selector());
  setupCategoryWidget(fees, m_feeSplits, id, TQ_SLOT(slotEditFeeSplits()));

  // security
  aSet.clear();
  aSet.load(security->selector(), i18n("Security"), m_account.accountList(), true);
}

void InvestTransactionEditor::loadEditWidgets(KMyMoneyRegister::Action /* action */)
{
  TQString id;

  kMyMoneyDateInput* postDate = dynamic_cast<kMyMoneyDateInput*>(haveWidget("postdate"));
  KMyMoneyReconcileCombo* reconcile = dynamic_cast<KMyMoneyReconcileCombo*>(haveWidget("status"));
  KMyMoneySecurity* security = dynamic_cast<KMyMoneySecurity*>(haveWidget("security"));
  KMyMoneyActivityCombo* activity = dynamic_cast<KMyMoneyActivityCombo*>(haveWidget("activity"));
  KMyMoneyCategory* asset = dynamic_cast<KMyMoneyCategory*>(haveWidget("asset-account"));
  KTextEdit* memo = dynamic_cast<KTextEdit*>(m_editWidgets["memo"]);
  kMyMoneyEdit* value;
  KMyMoneyCategory* interest = dynamic_cast<KMyMoneyCategory*>(haveWidget("interest-account"));
  KMyMoneyCategory* fees = dynamic_cast<KMyMoneyCategory*>(haveWidget("fee-account"));

  // check if the current transaction has a reference to an equity account
  bool haveEquityAccount = false;
  TQValueList<MyMoneySplit>::const_iterator it_s;
  for(it_s = m_transaction.splits().begin(); !haveEquityAccount && it_s != m_transaction.splits().end(); ++it_s) {
    MyMoneyAccount acc = MyMoneyFile::instance()->account((*it_s).accountId());
    if(acc.accountType() == MyMoneyAccount::Equity)
      haveEquityAccount = true;
  }

  // asset-account
  AccountSet aSet;
  aSet.clear();
  aSet.addAccountType(MyMoneyAccount::Checkings);
  aSet.addAccountType(MyMoneyAccount::Savings);
  aSet.addAccountType(MyMoneyAccount::Cash);
  aSet.addAccountType(MyMoneyAccount::Asset);
  aSet.addAccountType(MyMoneyAccount::Currency);
  aSet.addAccountType(MyMoneyAccount::CreditCard);
  if(KMyMoneyGlobalSettings::expertMode() || haveEquityAccount)
    aSet.addAccountGroup(MyMoneyAccount::Equity);
  aSet.load(asset->selector());

  // security
  security->setSuppressObjectCreation(false);    // allow object creation on the fly
  aSet.clear();
  aSet.load(security->selector(), i18n("Security"), m_account.accountList(), true);

  if(!isMultiSelection()) {
    // date
    if(m_transaction.postDate().isValid())
      postDate->setDate(m_transaction.postDate());
    else if(m_lastPostDate.isValid())
      postDate->setDate(m_lastPostDate);
    else
      postDate->setDate(TQDate::currentDate());

    // security (but only if it's not the investment account)
    if(m_split.accountId() != m_account.id()) {
      security->completion()->setSelected(m_split.accountId());
      security->slotItemSelected(m_split.accountId());
    }

    // activity
    activity->setActivity(d->m_activity->type());
    slotUpdateActivity(activity->activity());

    asset->completion()->setSelected(m_assetAccountSplit.accountId());
    asset->slotItemSelected(m_assetAccountSplit.accountId());

    // interest-account
    aSet.clear();
    aSet.addAccountGroup(MyMoneyAccount::Income);
    aSet.load(interest->selector());
    setupCategoryWidget(interest, m_interestSplits, id, TQ_SLOT(slotEditInterestSplits()));
    slotUpdateInterestVisibility(interest->currentText());

    // fee-account
    aSet.clear();
    aSet.addAccountGroup(MyMoneyAccount::Expense);
    aSet.load(fees->selector());
    setupCategoryWidget(fees, m_feeSplits, id, TQ_SLOT(slotEditFeeSplits()));
    slotUpdateFeeVisibility(fees->currentText());

    // memo
    memo->setText(m_split.memo());

    // shares
    // don't set the value if the number of shares is zero so that
    // we can see the hint
    value = dynamic_cast<kMyMoneyEdit*>(haveWidget("shares"));
    if(typeid(*(d->m_activity)) != typeid(Invest::Split(this)))
      value->setPrecision(MyMoneyMoney::denomToPrec(m_security.smallestAccountFraction()));
    else
      value->setPrecision(-1);

    if(!m_split.shares().isZero())
      value->setValue(m_split.shares().abs());

    // price
    updatePriceMode(m_split);

    // fee amount
    value = dynamic_cast<kMyMoneyEdit*>(haveWidget("fee-amount"));
    value->setValue(subtotal(m_feeSplits));

    // interest amount
    value = dynamic_cast<kMyMoneyEdit*>(haveWidget("interest-amount"));
    value->setValue(-subtotal(m_interestSplits));

    // total
    slotUpdateTotalAmount();

    // status
    if(m_split.reconcileFlag() == MyMoneySplit::Unknown)
      m_split.setReconcileFlag(MyMoneySplit::NotReconciled);
    reconcile->setState(m_split.reconcileFlag());

  } else {
    postDate->loadDate(TQDate());
    reconcile->setState(MyMoneySplit::Unknown);
    memo->setText(TQString());

    // We don't allow to change the activity
    activity->setActivity(d->m_activity->type());
    slotUpdateActivity(activity->activity());
    activity->setDisabled(true);

    // scan the list of selected transactions and check that they have
    // the same activity.
    KMyMoneyRegister::SelectedTransactions::iterator it_t = m_transactions.begin();
    const TQString& action = m_item->split().action();
    bool isNegative = m_item->split().shares().isNegative();
    bool allSameActivity = true;
    for(it_t = m_transactions.begin(); allSameActivity && (it_t != m_transactions.end()); ++it_t) {
      allSameActivity = (action == (*it_t).split().action() && (*it_t).split().shares().isNegative() == isNegative);
    }

    TQStringList fields;
    fields << "shares" << "price" << "fee-amount" << "interest-amount";
    TQStringList::const_iterator it_f;
    for(it_f = fields.begin(); it_f != fields.end(); ++it_f) {
      value = dynamic_cast<kMyMoneyEdit*>(haveWidget((*it_f)));
      value->setText("");
      value->setAllowEmpty();
    }

    // if we have transactions with different activities, disable some more widgets
    if(!allSameActivity) {
      fields << "asset-account" << "fee-account" << "interest-account";
      TQStringList::const_iterator it_f;
      for(it_f = fields.begin(); it_f != fields.end(); ++it_f) {
        haveWidget(*it_f)->setDisabled(true);
      }
    }
  }
}

TQWidget* InvestTransactionEditor::firstWidget(void) const
{
  return 0; // let the creator use the first widget in the tab order
}

bool InvestTransactionEditor::isComplete(TQString& reason) const
{
  reason = TQString();
  return d->m_activity->isComplete(reason);
}

MyMoneyMoney InvestTransactionEditor::subtotal(const TQValueList<MyMoneySplit>& splits) const
{
  TQValueList<MyMoneySplit>::const_iterator it_s;
  MyMoneyMoney sum;

  for(it_s = splits.begin(); it_s != splits.end(); ++it_s) {
    sum += (*it_s).value();
  }

  return sum;
}

void InvestTransactionEditor::slotUpdateSecurity(const TQString& stockId)
{
  MyMoneyFile* file = MyMoneyFile::instance();
  MyMoneyAccount stock = file->account(stockId);
  m_security = file->security(stock.currencyId());
  m_currency = file->security(m_security.tradingCurrency());
  bool currencyKnown = !m_currency.id().isEmpty();
  if(!currencyKnown) {
    m_currency.setTradingSymbol("???");
  } else {
    if(typeid(*(d->m_activity)) != typeid(Invest::Split(this))) {
      dynamic_cast<kMyMoneyEdit*>(haveWidget("shares"))->setPrecision(MyMoneyMoney::denomToPrec(m_security.smallestAccountFraction()));
    } else {
      dynamic_cast<kMyMoneyEdit*>(haveWidget("shares"))->setPrecision(-1);
    }
  }

  updatePriceMode();

  d->m_activity->preloadAssetAccount();

  haveWidget("shares")->setEnabled(currencyKnown);
  haveWidget("price")->setEnabled(currencyKnown);
  haveWidget("fee-amount")->setEnabled(currencyKnown);
  haveWidget("interest-amount")->setEnabled(currencyKnown);

  slotUpdateTotalAmount();
}

void InvestTransactionEditor::totalAmount(MyMoneyMoney& amount) const
{
  KMyMoneyActivityCombo* activityCombo = dynamic_cast<KMyMoneyActivityCombo*>(haveWidget("activity"));
  kMyMoneyEdit* sharesEdit = dynamic_cast<kMyMoneyEdit*>(haveWidget("shares"));
  kMyMoneyEdit* priceEdit = dynamic_cast<kMyMoneyEdit*>(haveWidget("price"));
  kMyMoneyEdit* feesEdit = dynamic_cast<kMyMoneyEdit*>(haveWidget("fee-amount"));
  kMyMoneyEdit* interestEdit = dynamic_cast<kMyMoneyEdit*>(haveWidget("interest-amount"));

  if(priceMode() == InvestTransactionEditor::PricePerTransaction)
    amount = priceEdit->value().abs();
  else
    amount = sharesEdit->value().abs() * priceEdit->value().abs();

  if(feesEdit->isVisible()) {
    MyMoneyMoney fee = feesEdit->value();
    MyMoneyMoney factor(-1,1);
    switch(activityCombo->activity()) {
      case MyMoneySplit::BuyShares:
      case MyMoneySplit::ReinvestDividend:
        factor = MyMoneyMoney(1,1);
        break;
      default:
        break;
    }
    amount += (fee * factor);
  }

  if(interestEdit->isVisible()) {
    MyMoneyMoney interest = interestEdit->value();
    MyMoneyMoney factor(1,1);
    switch(activityCombo->activity()) {
      case MyMoneySplit::BuyShares:
        factor = MyMoneyMoney(-1,1);
        break;
      default:
        break;
    }
    amount += (interest * factor);
  }
}

void InvestTransactionEditor::slotUpdateTotalAmount(void)
{
  TQLabel* total = dynamic_cast<TQLabel*>(haveWidget("total"));

  if(total && total->isVisible()) {
    MyMoneyMoney amount;
    totalAmount(amount);
    total->setText(amount.formatMoney(m_currency.tradingSymbol(), MyMoneyMoney::denomToPrec(m_security.smallestAccountFraction())));
  }
}

void InvestTransactionEditor::slotUpdateActivity(MyMoneySplit::investTransactionTypeE activity)
{
  // create new activity object if required
  activityFactory(activity);

  KMyMoneyCategory* cat;

  // hide all dynamic widgets (make sure to use the parentWidget for the
  // category widgets)
  haveWidget("interest-account")->parentWidget()->hide();
  haveWidget("fee-account")->parentWidget()->hide();

  TQStringList dynwidgets;
  dynwidgets << "total-label" << "asset-label" << "fee-label" << "fee-amount-label" << "interest-label" << "interest-amount-label" << "price-label" << "shares-label";

  // hiding labels works by clearing them. hide() does not do the job
  // as the underlying text in the TQTable object will shine through
  TQStringList::const_iterator it_s;
  for(it_s = dynwidgets.begin(); it_s != dynwidgets.end(); ++it_s) {
    TQLabel* w = dynamic_cast<TQLabel*>(haveWidget(*it_s));
    if(w)
      w->setText(" ");
  }

  // real widgets can be hidden
  dynwidgets.clear();
  dynwidgets << "asset-account" << "interest-amount" << "fee-amount" << "shares" << "price" << "total";

  for(it_s = dynwidgets.begin(); it_s != dynwidgets.end(); ++it_s) {
    TQWidget* w = haveWidget(*it_s);
    if(w)
      w->hide();
  }

  d->m_activity->showWidgets();

  d->m_activity->preloadAssetAccount();

  cat = dynamic_cast<KMyMoneyCategory*>(haveWidget("interest-account"));
  if(cat->parentWidget()->isVisible())
    slotUpdateInterestVisibility(cat->currentText());

  cat = dynamic_cast<KMyMoneyCategory*>(haveWidget("fee-account"));
  if(cat->parentWidget()->isVisible())
    slotUpdateFeeVisibility(cat->currentText());
}

InvestTransactionEditor::priceModeE InvestTransactionEditor::priceMode(void) const
{
  priceModeE mode = static_cast<priceModeE>(0);
  KMyMoneySecurity* sec = dynamic_cast<KMyMoneySecurity*>(m_editWidgets["security"]);
  TQString accId;
  if(!sec->currentText().isEmpty()) {
    accId = sec->selectedItem();
    if(accId.isEmpty())
      accId = m_account.id();
  }
  while(!accId.isEmpty() && mode == 0) {
    MyMoneyAccount acc = MyMoneyFile::instance()->account(accId);
    if(acc.value("priceMode").isEmpty())
      accId = acc.parentAccountId();
    else
      mode = static_cast<priceModeE>(acc.value("priceMode").toInt());
  }

  // if it's still <default> then use that default
  if(mode == 0)
    mode = PricePerShare;
  return mode;
}

bool InvestTransactionEditor::setupPrice(const MyMoneyTransaction& t, MyMoneySplit& split)
{
  MyMoneyFile* file = MyMoneyFile::instance();
  MyMoneyAccount acc = file->account(split.accountId());
  MyMoneySecurity toCurrency(file->security(acc.currencyId()));
  int fract = acc.fraction();

  if(acc.currencyId() != t.commodity()) {
    TQMap<TQString, MyMoneyMoney>::Iterator it_p;
    TQString key = t.commodity() + "-" + acc.currencyId();
    it_p = m_priceInfo.find(key);

    // if it's not found, then collect it from the user first
    MyMoneyMoney price;
    if(it_p == m_priceInfo.end()) {
      MyMoneySecurity fromCurrency = file->security(t.commodity());
      MyMoneyMoney fromValue, toValue;

      fromValue = split.value();
      MyMoneyPrice priceInfo = MyMoneyFile::instance()->price(fromCurrency.id(), toCurrency.id());
      toValue = split.value() * priceInfo.rate(toCurrency.id());

      KCurrencyCalculator calc(fromCurrency,
                                toCurrency,
                                fromValue,
                                toValue,
                                t.postDate(),
                                fract,
                                m_regForm, "currencyCalculator");

      if(calc.exec() == TQDialog::Rejected) {
        return false;
      }
      price = calc.price();
      m_priceInfo[key] = price;
    } else {
      price = (*it_p);
    }

    // update shares if the transaction commodity is the currency
    // of the current selected account
    split.setShares((split.value() * price).convert(fract));
  } else {
    split.setShares(split.value().convert(fract));
  }

  return true;
}

bool InvestTransactionEditor::createTransaction(MyMoneyTransaction& t, const MyMoneyTransaction& torig, const MyMoneySplit& sorig, bool /* skipPriceDialog */)
{
  MyMoneyFile* file = MyMoneyFile::instance();
  // we start with the previous values, make sure we can add them later on
  t = torig;
  MyMoneySplit s0 = sorig;
  s0.clearId();

  KMyMoneySecurity* sec = dynamic_cast<KMyMoneySecurity*>(m_editWidgets["security"]);
  if(!isMultiSelection() || (isMultiSelection() && !sec->currentText().isEmpty())) {
    TQString securityId = sec->selectedItem();
    if(!securityId.isEmpty()) {
      s0.setAccountId(securityId);
      MyMoneyAccount stockAccount = file->account(securityId);
      TQString currencyId = stockAccount.currencyId();
      MyMoneySecurity security = file->security(currencyId);

      t.setCommodity(security.tradingCurrency());
    } else {
      s0.setAccountId(m_account.id());
      t.setCommodity(m_account.currencyId());
    }
  }

  // extract price info from original transaction
  m_priceInfo.clear();
  TQValueList<MyMoneySplit>::const_iterator it_s;
  if(!torig.id().isEmpty()) {
    for(it_s = torig.splits().begin(); it_s != torig.splits().end(); ++it_s) {
      if((*it_s).id() != sorig.id()) {
        MyMoneyAccount cat = file->account((*it_s).accountId());
        if(cat.currencyId() != m_account.currencyId()) {
          if(!(*it_s).shares().isZero() && !(*it_s).value().isZero()) {
            m_priceInfo[cat.currencyId()] = ((*it_s).shares() / (*it_s).value()).reduce();
          }
        }
      }
    }
  }

  t.removeSplits();

  kMyMoneyDateInput* postDate = dynamic_cast<kMyMoneyDateInput*>(m_editWidgets["postdate"]);
  if(postDate->date().isValid()) {
    t.setPostDate(postDate->date());
  }

  // memo and number field are special: if we have multiple transactions selected
  // and the edit field is empty, we treat it as "not modified".
  // FIXME a better approach would be to have a 'dirty' flag with the widgets
  //       which identifies if the originally loaded value has been modified
  //       by the user
  KTextEdit* memo = dynamic_cast<KTextEdit*>(m_editWidgets["memo"]);
  if(memo) {
    if(!isMultiSelection() || (isMultiSelection() && !memo->text().isEmpty() ) )
      s0.setMemo(memo->text());
  }

  MyMoneySplit assetAccountSplit;
  TQValueList<MyMoneySplit> feeSplits;
  TQValueList<MyMoneySplit> interestSplits;
  MyMoneySecurity security, currency;
  MyMoneySplit::investTransactionTypeE transactionType;

  // extract the splits from the original transaction
  dissectTransaction(torig, sorig,
                     assetAccountSplit,
                     feeSplits,
                     interestSplits,
                     security,
                     currency,
                     transactionType);

  // check if the trading currency is the same if the security has changed
  // in case it differs, check that we have a price (request from user)
  // and convert all splits
  // TODO

  // do the conversions here
  // TODO

  // keep the current activity object and create a new one
  // that can be destroyed later on
  Activity* activity = d->m_activity;
  d->m_activity = 0;      // make sure we create a new one
  activityFactory(activity->type());

  // if the activity is not set in the combo widget, we keep
  // the one which is used in the original transaction
  KMyMoneyActivityCombo* activityCombo = dynamic_cast<KMyMoneyActivityCombo*>(haveWidget("activity"));
  if(activityCombo->activity() == MyMoneySplit::UnknownTransactionType) {
    activityFactory(transactionType);
  }

  // if we mark the split reconciled here, we'll use today's date if no reconciliation date is given
  KMyMoneyReconcileCombo* status = dynamic_cast<KMyMoneyReconcileCombo*>(m_editWidgets["status"]);
  if(status->state() != MyMoneySplit::Unknown)
    s0.setReconcileFlag(status->state());

  if(s0.reconcileFlag() == MyMoneySplit::Reconciled && !s0.reconcileDate().isValid())
    s0.setReconcileDate(TQDate::currentDate());

  // call the creation logic for the current selected activity
  bool rc = d->m_activity->createTransaction(t, s0, assetAccountSplit, feeSplits, m_feeSplits, interestSplits, m_interestSplits, security, currency);

  // now switch back to the original activity
  delete d->m_activity;
  d->m_activity = activity;

  // add the splits to the transaction
  if(rc) {
    if(!assetAccountSplit.accountId().isEmpty()) {
      assetAccountSplit.clearId();
      t.addSplit(assetAccountSplit);
    }

    t.addSplit(s0);

    TQValueList<MyMoneySplit>::iterator it_s;
    for(it_s = feeSplits.begin(); it_s != feeSplits.end(); ++it_s) {
      (*it_s).clearId();
      t.addSplit(*it_s);
    }

    for(it_s = interestSplits.begin(); it_s != interestSplits.end(); ++it_s) {
      (*it_s).clearId();
      t.addSplit(*it_s);
    }
  }

  // adjust the value to the smallestAccountFraction found
  // for the commodity of the transaction.
  for(it_s = t.splits().begin(); it_s != t.splits().end(); ++it_s) {
    MyMoneySplit s = (*it_s);
    s.setValue((*it_s).value().convert(currency.smallestAccountFraction()));
    t.modifySplit(s);
  }

  return rc;
}

void InvestTransactionEditor::updatePriceMode(const MyMoneySplit& split)
{
  TQLabel* label = dynamic_cast<TQLabel*>(haveWidget("price-label"));
  if(label) {
    kMyMoneyEdit* sharesEdit = dynamic_cast<kMyMoneyEdit*>(haveWidget("shares"));
    kMyMoneyEdit* priceEdit = dynamic_cast<kMyMoneyEdit*>(haveWidget("price"));
    MyMoneyMoney price;
    if(!split.id().isEmpty())
      price = split.price().reduce();
    else
      price = priceEdit->value().abs();

    if(priceMode() == PricePerTransaction && label->text() != i18n("Price")) {
      label->setText(i18n("Price"));
      if(!sharesEdit->value().isZero())
        priceEdit->setValue(sharesEdit->value().abs() * price);

    } else if(priceMode() == PricePerShare && label->text() == i18n("Price")) {
      label->setText(i18n("Price/Share"));
      if(!sharesEdit->value().isZero())
        priceEdit->setValue(price / sharesEdit->value().abs());

    } else if(priceMode() == PricePerTransaction) {
      priceEdit->setValue(sharesEdit->value().abs() * price);

    } else
      priceEdit->setValue(price);
  }
}

void InvestTransactionEditor::setupFinalWidgets(void)
{
  addFinalWidget(haveWidget("memo"));
}

#include "investtransactioneditor.moc"

