/***************************************************************************
                          konlinebankingsetupwizard.cpp
                             -------------------
    begin                : Sat Jan 7 2006
    copyright            : (C) 2006 by Ace Jones
    email                : acejones@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 <tqtextbrowser.h>
#include <tqlineedit.h>
#include <tqlabel.h>
#include <tqlayout.h>
#include <tqregexp.h>
#include <tqcheckbox.h>
#include <tqtabwidget.h>

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

#include <tdelocale.h>
#include <kdebug.h>
#include <tdemessagebox.h>
#include <tdestandarddirs.h>
#include <kprogress.h>
#include <tdeapplication.h>
#include <tdelistview.h>
#include <tdelistviewsearchline.h>
#include <kcombobox.h>
#include <kurlrequester.h>

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

#include "konlinebankingsetupwizard.h"
#include <../ofxpartner.h>
#include <mymoneyofxconnector.h>

class KOnlineBankingSetupWizard::Private
{
public:
  TQFile       m_fpTrace;
  TQTextStream m_trace;
};

KOnlineBankingSetupWizard::KOnlineBankingSetupWizard(TQWidget *parent, const char *name):
  KOnlineBankingSetupDecl(parent,name),
  d(new Private),
  m_fDone(false),
  m_fInit(false),
  m_appId(0)
{
  m_appId = new OfxAppVersion(m_applicationCombo, "");
  m_headerVersion = new OfxHeaderVersion(m_headerVersionCombo, "");

  // fill the list view with banks
  KProgressDialog* dlg = new KProgressDialog(this, 0, i18n("Loading banklist"), i18n("Getting list of banks from http://moneycentral.msn.com/\nThis may take some time depending on the available bandwidth."), true);
  dlg->setAllowCancel(false);
  // force to show immediately as the call to OfxPartner::BankNames()
  // does not call the processEvents() loop
  dlg->setMinimumDuration(0);
  tdeApp->processEvents();

  tabLayout->insertWidget(0, new TDEListViewSearchLineWidget(m_listFi, tab, 0));

  OfxPartner::setDirectory(locateLocal("appdata", ""));
  TQStringList banks = OfxPartner::BankNames();
  TQStringList::const_iterator it_bank = banks.begin();
  while (it_bank != banks.end())
  {
    new TDEListViewItem( m_listFi, (*it_bank));
    ++it_bank;
  }
  m_fInit = true;
  delete dlg;
}

KOnlineBankingSetupWizard::~KOnlineBankingSetupWizard()
{
  delete m_appId;
  delete d;
}

void KOnlineBankingSetupWizard::next(void)
{
  bool ok = true;

  switch (indexOf(currentPage()))
  {
  case 0:
    ok = finishFiPage();
    break;
  case 1:
    ok = finishLoginPage();
    break;
  case 2:
    m_fDone = ok = finishAccountPage();
    break;
  }

  if (ok)
    KOnlineBankingSetupDecl::next();

  setFinishEnabled(currentPage(), m_fDone );
}

bool KOnlineBankingSetupWizard::finishFiPage(void)
{
  bool result = false;

  m_bankInfo.clear();
  OfxFiServiceInfo info;

  if(m_selectionTab->currentPageIndex() == 0) {

    // Get the fipids for the selected bank
    TQListViewItem* item = m_listFi->currentItem();
    if ( item )
    {
      TQString bank = item->text(0);
      m_textDetails->clear();
      m_textDetails->append(TQString("<p>Details for %1:</p>").arg(bank));
      TQStringList fipids = OfxPartner::FipidForBank(bank);
      TQStringList::const_iterator it_fipid = fipids.begin();
      while ( it_fipid != fipids.end() )
      {
        // For each fipid, get the connection details
        info = OfxPartner::ServiceInfo(*it_fipid);

        // Print them to the text browser
        TQString message = TQString("<p>Fipid: %1<br>").arg(*it_fipid);

        // If the bank supports retrieving statements
        if ( info.accountlist )
        {
          m_bankInfo.push_back(info);

          message += TQString("URL: %1<br>Org: %2<br>Fid: %3<br>").arg(info.url,info.org,info.fid);
          if ( info.statements )
            message += i18n("Supports online statements<br>");
          if ( info.investments )
            message += i18n("Supports investments<br>");
          if ( info.billpay )
            message += i18n("Supports bill payment (but not supported by KMyMoney yet)<br>");
        }
        else
        {
          message += i18n("Does not support online banking</p>");
        }
        m_textDetails->append(message);

        ++it_fipid;
      }
      result = true;
    }
    else
      // error!  No current item
      KMessageBox::sorry(this,i18n("Please choose a bank."));

  } else {  // manual entry of values
    if(m_fid->text().isEmpty()
    || m_url->url().isEmpty()
    || m_bankName->text().isEmpty()) {
      KMessageBox::sorry(this,i18n("Please fill all fields with values."));
    }

    m_textDetails->clear();
    m_textDetails->append(TQString("<p>Details for %1:</p>").arg(m_bankName->text()));

    memset(&info, 0, sizeof(OfxFiServiceInfo));
    strncpy(info.fid, m_fid->text().ascii(), OFX_FID_LENGTH-1);
    strncpy(info.org, m_bankName->text().latin1(), OFX_ORG_LENGTH-1);
    strncpy(info.url, m_url->url().ascii(), OFX_URL_LENGTH-1);
    info.accountlist = 1;
    info.statements = 1;
    info.billpay = 1;
    info.investments = 1;

    m_bankInfo.push_back(info);

    TQString message;
    message += TQString("URL: %1<br>Org: %2<br>Fid: %3<br>").arg(info.url,info.org,info.fid);
    if ( info.statements )
      message += i18n("Supports online statements<br>");
    if ( info.investments )
      message += i18n("Supports investments<br>");
    if ( info.billpay )
      message += i18n("Supports bill payment (but not supported by KMyMoney yet)<br>");
    m_textDetails->append(message);
    result = true;
  }
  return result;
}

bool KOnlineBankingSetupWizard::finishLoginPage(void)
{
  bool result = true;

  TQString username = m_editUsername->text();
  TQString password = m_editPassword->text();

  m_listAccount->clear();

  // Process an account request for each fipid
  m_it_info = m_bankInfo.begin();
  while ( m_it_info != m_bankInfo.end() )
  {
    OfxFiLogin fi;
    memset(&fi,0,sizeof(OfxFiLogin));
    strncpy(fi.fid,(*m_it_info).fid,OFX_FID_LENGTH-1);
    strncpy(fi.org,(*m_it_info).org,OFX_ORG_LENGTH-1);
    strncpy(fi.userid,username.latin1(),OFX_USERID_LENGTH-1);
    strncpy(fi.userpass,password.latin1(),OFX_USERPASS_LENGTH-1);

#if LIBOFX_IS_VERSION(0,9,0)
    // pretend we're Quicken 2008
    // http://ofxblog.wordpress.com/2007/06/06/ofx-appid-and-appver-for-intuit-products/
    // http://ofxblog.wordpress.com/2007/06/06/ofx-appid-and-appver-for-microsoft-money/
    TQString appId = m_appId->appId();
    TQRegExp exp("(.*):(.*)");
    if(exp.search(appId) != -1) {
      strncpy(fi.appid, exp.cap(1).latin1(), OFX_APPID_LENGTH-1);
      strncpy(fi.appver, exp.cap(2).latin1(), OFX_APPVER_LENGTH-1);
    } else {
      strncpy(fi.appid, "QWIN", OFX_APPID_LENGTH-1);
      strncpy(fi.appver, "1700", OFX_APPVER_LENGTH-1);
    }

    TQString hver = m_headerVersion->headerVersion();
    strncpy(fi.header_version, hver.latin1(), OFX_HEADERVERSION_LENGTH-1);
#endif

    // who owns this memory?!?!
    char* request = libofx_request_accountinfo( &fi );

    KURL filename(TQString("%1response.ofx").arg(locateLocal("appdata", "")));
    TQByteArray req;
    req.setRawData(request, strlen(request));
    OfxHttpsRequest("POST", (*m_it_info).url, req, TQMap<TQString, TQString>(), filename, true);
    req.resetRawData(request, strlen(request));

    LibofxContextPtr ctx = libofx_get_new_context();
    TQ_CHECK_PTR(ctx);

    ofx_set_account_cb(ctx, ofxAccountCallback, this);
    ofx_set_status_cb(ctx, ofxStatusCallback, this);
    // Add resulting accounts to the account list
    libofx_proc_file(ctx, filename.path().local8Bit(), AUTODETECT);
    libofx_free_context(ctx);

    ++m_it_info;
  }

  if ( ! m_listAccount->childCount() )
  {
    KMessageBox::sorry(this,i18n("No suitable accounts were found at this bank."));
    result = false;
  }
  return result;
}

bool KOnlineBankingSetupWizard::finishAccountPage(void)
{
  bool result = true;

  if ( ! m_listAccount->currentItem() )
  {
    KMessageBox::sorry(this,i18n("Please choose an account"));
    result = false;
  }

  return result;
}

int KOnlineBankingSetupWizard::ofxAccountCallback(struct OfxAccountData data, void * pv)
{
  KOnlineBankingSetupWizard* pthis = reinterpret_cast<KOnlineBankingSetupWizard*>(pv);
  // Put the account info in the view

  MyMoneyKeyValueContainer kvps;

  if ( data.account_type_valid )
  {
    TQString type;
    switch ( data.account_type )
    {
      case OfxAccountData::OFX_CHECKING:  /**< A standard checking account */
      type = "CHECKING";
      break;
    case OfxAccountData::OFX_SAVINGS:   /**< A standard savings account */
      type = "SAVINGS";
      break;
    case OfxAccountData::OFX_MONEYMRKT: /**< A money market account */
      type = "MONEY MARKET";
      break;
    case OfxAccountData::OFX_CREDITLINE: /**< A line of credit */
      type = "CREDIT LINE";
      break;
    case OfxAccountData::OFX_CMA:       /**< Cash Management Account */
      type = "CMA";
      break;
    case OfxAccountData::OFX_CREDITCARD: /**< A credit card account */
      type = "CREDIT CARD";
      break;
    case OfxAccountData::OFX_INVESTMENT: /**< An investment account */
      type = "INVESTMENT";
      break;
    default:
      break;
    }
    kvps.setValue("type",type);
  }

  if ( data.bank_id_valid )
    kvps.setValue("bankid",data.bank_id);

  if ( data.broker_id_valid )
    kvps.setValue("bankid",data.broker_id);

  if ( data.branch_id_valid )
    kvps.setValue("branchid",data.branch_id);

  if ( data.account_number_valid )
    kvps.setValue("accountid",data.account_number);

  if ( data.account_id_valid )
    kvps.setValue("uniqueId",data.account_id);

  kvps.setValue("username",pthis->m_editUsername->text());
  kvps.setValue("password",pthis->m_editPassword->text());

  kvps.setValue("url",(*(pthis->m_it_info)).url);
  kvps.setValue("fid",(*(pthis->m_it_info)).fid);
  kvps.setValue("org",(*(pthis->m_it_info)).org);
  kvps.setValue("fipid","");
  TQListViewItem* item = pthis->m_listFi->currentItem();
  if ( item )
    kvps.setValue("bankname",item->text(0));

  // I removed the bankid here, because for some users it
  // was not possible to setup the automatic account matching
  // because the bankid was left empty here as well during
  // the statement download. In case we don't have it, we
  // simply use it blank. (ipwizard 2009-06-21)
  if(/* !kvps.value("bankid").isEmpty()
  && */ !kvps.value("uniqueId").isEmpty()) {

    kvps.setValue("kmmofx-acc-ref", TQString("%1-%2").arg(kvps.value("bankid"), kvps.value("uniqueId")));
  } else {
    tqDebug(TQString("Cannot setup kmmofx-acc-ref for '%1'").arg(kvps.value("bankname")));
  }
  kvps.setValue("protocol","OFX");

  new ListViewItem( pthis->m_listAccount, kvps );

  return 0;
}

int KOnlineBankingSetupWizard::ofxStatusCallback(struct OfxStatusData data, void * pv)
{
  KOnlineBankingSetupWizard* pthis = reinterpret_cast<KOnlineBankingSetupWizard*>(pv);

  TQString message;

  if(data.code_valid==true)
  {
    message += TQString("#%1 %2: \"%3\"\n").arg(data.code).arg(data.name,data.description);
  }

  if(data.server_message_valid==true){
    message += i18n("Server message: %1\n").arg(data.server_message);
  }

  if(data.severity_valid==true){
    switch(data.severity){
    case OfxStatusData::INFO :
      break;
    case OfxStatusData::WARN :
      KMessageBox::detailedError( pthis, i18n("Your bank returned warnings when signing on"), i18n("WARNING %1").arg(message) );
      break;
    case OfxStatusData::ERROR :
      KMessageBox::detailedError( pthis, i18n("Error signing onto your bank"), i18n("ERROR %1").arg(message) );
      break;
    default:
      break;
    }
  }
  return 0;
}

bool KOnlineBankingSetupWizard::chosenSettings( MyMoneyKeyValueContainer& settings )
{
  bool result = false;;

  if ( m_fDone )
  {
    TQListViewItem* qitem = m_listAccount->currentItem();
    ListViewItem* item = dynamic_cast<ListViewItem*>(qitem);
    if ( item )
    {
      settings = *item;
      settings.deletePair("appId");
      settings.deletePair("kmmofx-headerVersion");
      TQString appId = m_appId->appId();
      if(!appId.isEmpty())
        settings.setValue("appId", appId);
      TQString hVer = m_headerVersion->headerVersion();
      if(!hVer.isEmpty())
        settings.setValue("kmmofx-headerVersion", hVer);
      result = true;
    }
  }

  return result;
}

KOnlineBankingSetupWizard::ListViewItem::ListViewItem( TQListView* parent, const MyMoneyKeyValueContainer& kvps ):
  MyMoneyKeyValueContainer( kvps ), TQListViewItem( parent )
{
  setText( 0, value("accountid") );
  setText( 1, value("type") );
  setText( 2, value("bankid") );
  setText( 3, value("branchid") );
}

void KOnlineBankingSetupWizard::ListViewItem::x(void) {}

#include "konlinebankingsetupwizard.moc"
