/***************************************************************************
                          mymoneystoragexml.cpp  -  description
                             -------------------
    begin                : Thu Oct 24 2002
    copyright            : (C) 2002 by Kevin Tambascio
                           (C) 2004 by Thomas Baumgart
    email                : 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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "config.h"

// ----------------------------------------------------------------------------
// QT Includes

#include <tqfile.h>
#include <tqdom.h>
#include <tqmap.h>
#include <tqxml.h>

// ----------------------------------------------------------------------------
// KDE Includes

#include "kdecompat.h"
#include <klocale.h>
#include <kdebug.h>

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

#include "mymoneystoragexml.h"
#include "../mymoneyreport.h"
#include "../mymoneybudget.h"
#include "../mymoneyinstitution.h"

unsigned int MyMoneyStorageXML::fileVersionRead = 0;
unsigned int MyMoneyStorageXML::fileVersionWrite = 0;


class MyMoneyStorageXML::Private
{
  friend class MyMoneyStorageXML;
public:
  Private() {}

  TQMap<TQString, MyMoneyInstitution> iList;
  TQMap<TQString, MyMoneyAccount> aList;
  TQMap<TQString, MyMoneyTransaction> tList;
  TQMap<TQString, MyMoneyPayee> pList;
  TQMap<TQString, MyMoneySchedule> sList;
  TQMap<TQString, MyMoneySecurity> secList;
  TQMap<TQString, MyMoneyReport> rList;
  TQMap<TQString, MyMoneyBudget> bList;
  TQMap<MyMoneySecurityPair, MyMoneyPriceEntries> prList;

  TQString           m_fromSecurity;
  TQString           m_toSecurity;

};


class MyMoneyXmlContentHandler : public TQXmlContentHandler
{
public:
  MyMoneyXmlContentHandler(MyMoneyStorageXML* reader);
  virtual ~MyMoneyXmlContentHandler() {}
  virtual void setDocumentLocator (TQXmlLocator * locator) { m_loc = locator; }
  virtual bool startDocument (void);
  virtual bool endDocument (void);
  virtual bool startPrefixMapping(const TQString & prefix, const TQString & uri);
  virtual bool endPrefixMapping(const TQString & prefix);
  virtual bool startElement(const TQString & namespaceURI, const TQString & localName, const TQString & qName, const TQXmlAttributes & atts);
  virtual bool endElement(const TQString & namespaceURI, const TQString & localName, const TQString & qName);
  virtual bool characters(const TQString & ch);
  virtual bool ignorableWhitespace(const TQString & ch);
  virtual bool processingInstruction(const TQString & target, const TQString & data);
  virtual bool skippedEntity(const TQString & name);
  virtual TQString errorString(void);

private:
  MyMoneyStorageXML* m_reader;
  TQXmlLocator*       m_loc;
  int                m_level;
  int                m_elementCount;
  TQDomDocument       m_doc;
  TQDomElement        m_baseNode;
  TQDomElement        m_currNode;
  TQString            m_errMsg;
};

MyMoneyXmlContentHandler::MyMoneyXmlContentHandler(MyMoneyStorageXML* reader) :
  m_reader(reader),
  m_loc(0),
  m_level(0),
  m_elementCount(0)
{
}

bool MyMoneyXmlContentHandler::startDocument(void)
{
  qDebug("startDocument");
  return true;
}

bool MyMoneyXmlContentHandler::endDocument(void)
{
  qDebug("endDocument");
  return true;
}

bool MyMoneyXmlContentHandler::skippedEntity (const TQString & /* name */)
{
  // qDebug(TQString("Skipped entity '%1'").tqarg(name));
  return true;
}

bool MyMoneyXmlContentHandler::startPrefixMapping (const TQString& /*prefix */, const TQString & /* uri */)
{
  // qDebug(TQString("start prefix '%1', '%2'").tqarg(prefix).tqarg(uri));
  return true;
}

bool MyMoneyXmlContentHandler::endPrefixMapping (const TQString& /* prefix */)
{
  // qDebug(TQString("end prefix '%1'").tqarg(prefix));
  return true;
}

bool MyMoneyXmlContentHandler::startElement (const TQString& /* namespaceURI */, const TQString& /* localName */, const TQString& qName, const TQXmlAttributes & atts)
{
  if(m_level == 0) {
    TQString s = qName.lower();
    if(s == "transaction"
    || s == "account"
    || s == "price"
    || s == "payee"
    || s == "currency"
    || s == "security"
    || s == "keyvaluepairs"
    || s == "institution"
    || s == "report"
    || s == "budget"
    || s == "fileinfo"
    || s == "user"
    || s == "scheduled_tx") {
      m_baseNode = m_doc.createElement(qName);
      for(int i=0; i < atts.count(); ++i) {
        m_baseNode.setAttribute(atts.qName(i), atts.value(i));
      }
      m_currNode = m_baseNode;
      m_level = 1;

    } else if(s == "transactions") {
      qDebug("reading transactions");
      if(atts.count()) {
        int count = atts.value(TQString("count")).toUInt();
        m_reader->signalProgress(0, count, i18n("Loading transactions..."));
        m_elementCount = 0;
      }
    } else if(s == "accounts") {
      qDebug("reading accounts");
      if(atts.count()) {
        int count = atts.value(TQString("count")).toUInt();
        m_reader->signalProgress(0, count, i18n("Loading accounts..."));
        m_elementCount = 0;
      }
    } else if(s == "securities") {
      qDebug("reading securities");
      if(atts.count()) {
        int count = atts.value(TQString("count")).toUInt();
        m_reader->signalProgress(0, count, i18n("Loading securities..."));
        m_elementCount = 0;
      }
    } else if(s == "reports") {
      qDebug("reading reports");
      if(atts.count()) {
        int count = atts.value(TQString("count")).toUInt();
        m_reader->signalProgress(0, count, i18n("Loading reports..."));
        m_elementCount = 0;
      }
    } else if(s == "prices") {
      qDebug("reading prices");
      m_elementCount = 0;
    } else if(s == "pricepair") {
      if(atts.count()) {
        m_reader->d->m_fromSecurity = atts.value(TQString("from"));
        m_reader->d->m_toSecurity = atts.value(TQString("to"));
      }
    }

  } else {
    m_level++;
    TQDomElement node = m_doc.createElement(qName);
    for(int i=0; i < atts.count(); ++i) {
      node.setAttribute(atts.qName(i), atts.value(i));
    }
    m_currNode.appendChild(node);
    m_currNode = node;
  }
  return true;
}

bool MyMoneyXmlContentHandler::endElement(const TQString& /* namespaceURI */, const TQString& /* localName */, const TQString& qName)
{
  bool rc = true;
  TQString s = qName.lower();
  if(m_level) {
    m_currNode = m_currNode.parentNode().toElement();
    m_level--;
    if(!m_level) {
      try {
        if(s == "transaction") {
          MyMoneyTransaction t(m_baseNode);
          if(!t.id().isEmpty())
            m_reader->d->tList[t.uniqueSortKey()] = t;
        } else if(s == "account") {
          MyMoneyAccount a(m_baseNode);
          if(!a.id().isEmpty())
            m_reader->d->aList[a.id()] = a;
        } else if(s == "payee") {
          MyMoneyPayee p(m_baseNode);
          if(!p.id().isEmpty())
            m_reader->d->pList[p.id()] = p;
        } else if(s == "currency") {
          MyMoneySecurity s(m_baseNode);
          if(!s.id().isEmpty())
            m_reader->d->secList[s.id()] = s;
        } else if(s == "security") {
          MyMoneySecurity s(m_baseNode);
          if(!s.id().isEmpty())
            m_reader->d->secList[s.id()] = s;
        } else if(s == "keyvaluepairs") {
          MyMoneyKeyValueContainer kvp(m_baseNode);
          m_reader->m_storage->setPairs(kvp.pairs());
        } else if(s == "institution") {
          MyMoneyInstitution i(m_baseNode);
          if(!i.id().isEmpty())
            m_reader->d->iList[i.id()] = i;
        } else if(s == "report") {
          MyMoneyReport r(m_baseNode);
          if(!r.id().isEmpty())
            m_reader->d->rList[r.id()] = r;
        } else if(s == "budget") {
          MyMoneyBudget b(m_baseNode);
          if(!b.id().isEmpty())
            m_reader->d->bList[b.id()] = b;
        } else if(s == "fileinfo") {
          rc = m_reader->readFileInformation(m_baseNode);
        } else if(s == "user") {
          rc = m_reader->readUserInformation(m_baseNode);
        } else if(s == "scheduled_tx") {
          MyMoneySchedule s(m_baseNode);
          if(!s.id().isEmpty())
            m_reader->d->sList[s.id()] = s;
        } else if(s == "price") {
          MyMoneyPrice p(m_reader->d->m_fromSecurity, m_reader->d->m_toSecurity, m_baseNode);
          m_reader->d->prList[MyMoneySecurityPair(m_reader->d->m_fromSecurity, m_reader->d->m_toSecurity)][p.date()] = p;
        } else {
          m_errMsg = i18n("Unknown XML tag %1 found in line %2").tqarg(qName).tqarg(m_loc->lineNumber());
          kdWarning() << m_errMsg << endl;
          rc = false;
        }
        m_reader->signalProgress(++m_elementCount, 0);
      } catch(MyMoneyException* e) {
        m_errMsg = i18n("Exception while creating a %1 element: %2").tqarg(s).tqarg(e->what());
        kdWarning() << m_errMsg << endl;
        delete e;
        rc = false;
      }
      m_doc = TQDomDocument();
    }
  } else {
    if(s == "institutions") {
      // last institution read, now dump them into the engine
      m_reader->m_storage->loadInstitutions(m_reader->d->iList);
      m_reader->d->iList.clear();
      m_reader->signalProgress(-1, -1);
    } else if(s == "accounts") {
      // last account read, now dump them into the engine
      m_reader->m_storage->loadAccounts(m_reader->d->aList);
      m_reader->d->aList.clear();
      m_reader->signalProgress(-1, -1);
    } else if(s == "payees") {
      // last payee read, now dump them into the engine
      m_reader->m_storage->loadPayees(m_reader->d->pList);
      m_reader->d->pList.clear();
      m_reader->signalProgress(-1, -1);
    } else if(s == "transactions") {
      // last transaction read, now dump them into the engine
      m_reader->m_storage->loadTransactions(m_reader->d->tList);
      m_reader->d->tList.clear();
      m_reader->signalProgress(-1, -1);
    } else if(s == "schedules") {
      // last schedule read, now dump them into the engine
      m_reader->m_storage->loadSchedules(m_reader->d->sList);
      m_reader->d->sList.clear();
      m_reader->signalProgress(-1, -1);
    } else if(s == "securities") {
      // last security read, now dump them into the engine
      m_reader->m_storage->loadSecurities(m_reader->d->secList);
      m_reader->d->secList.clear();
      m_reader->signalProgress(-1, -1);
    } else if(s == "currencies") {
      // last currency read, now dump them into the engine
      m_reader->m_storage->loadCurrencies(m_reader->d->secList);
      m_reader->d->secList.clear();
      m_reader->signalProgress(-1, -1);
    } else if(s == "reports") {
      // last report read, now dump them into the engine
      m_reader->m_storage->loadReports(m_reader->d->rList);
      m_reader->d->rList.clear();
      m_reader->signalProgress(-1, -1);
    } else if(s == "budgets") {
      // last budget read, now dump them into the engine
      m_reader->m_storage->loadBudgets(m_reader->d->bList);
      m_reader->d->bList.clear();
      m_reader->signalProgress(-1, -1);
    } else if(s == "prices") {
      // last price read, now dump them into the engine
      m_reader->m_storage->loadPrices(m_reader->d->prList);
      m_reader->d->bList.clear();
      m_reader->signalProgress(-1, -1);
    }
  }
  return rc;
}

bool MyMoneyXmlContentHandler::characters(const TQString& /* ch */)
{
  return true;
}

bool MyMoneyXmlContentHandler::ignorableWhitespace(const TQString& /* ch */)
{
  return true;
}

bool MyMoneyXmlContentHandler::processingInstruction(const TQString& /* target */, const TQString& /* data */)
{
  return true;
}

TQString MyMoneyXmlContentHandler::errorString(void)
{
  return m_errMsg;
}







MyMoneyStorageXML::MyMoneyStorageXML() :
  m_storage(0),
  m_doc(0),
  d(new Private())
{
}

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

// Function to read in the file, send to XML parser.
void MyMoneyStorageXML::readFile(TQIODevice* pDevice, IMyMoneySerialize* storage)
{
  Q_CHECK_PTR(storage);
  Q_CHECK_PTR(pDevice);
  if(!storage)
    return;

  m_storage = storage;

  m_doc = new TQDomDocument;
  Q_CHECK_PTR(m_doc);

  qDebug("reading file");
  // creating the TQXmlInputSource object based on a TQIODevice object
  // reads all data of the underlying object into memory. I have not
  // found an object that reads on the fly. I tried to derive one myself,
  // but there could be a severe problem with decoding when reading
  // blocks of data and not a stream. So I left it the way it is. (ipwizard)
  TQXmlInputSource xml(pDevice);

  qDebug("start parsing file");
  MyMoneyXmlContentHandler mmxml(this);
  TQXmlSimpleReader reader;
  reader.setContentHandler(&mmxml);

  if(!reader.parse(&xml, false)) {
    delete m_doc;
    m_doc = NULL;
    signalProgress(-1, -1);
    throw new MYMONEYEXCEPTION("File was not parsable!");
  }

  // check if we need to build up the account balances
  if(fileVersionRead < 2)
    m_storage->rebuildAccountBalances();

  delete m_doc;
  m_doc = NULL;

  // this seems to be nonsense, but it clears the dirty flag
  // as a side-effect.
  m_storage->setLastModificationDate(m_storage->lastModificationDate());
  m_storage = NULL;

  //hides the progress bar.
  signalProgress(-1, -1);
}

void MyMoneyStorageXML::writeFile(TQIODevice* qf, IMyMoneySerialize* storage)
{
  Q_CHECK_PTR(qf);
  Q_CHECK_PTR(storage);
  if(!storage)
  {
    return;
  }
  m_storage = storage;

  // qDebug("XMLWRITER: Starting file write");
  m_doc = new TQDomDocument("KMYMONEY-FILE");
  Q_CHECK_PTR(m_doc);
  TQDomProcessingInstruction instruct = m_doc->createProcessingInstruction("xml", "version=\"1.0\" encoding=\"utf-8\"");
  m_doc->appendChild(instruct);

  TQDomElement mainElement = m_doc->createElement("KMYMONEY-FILE");
  m_doc->appendChild(mainElement);

  TQDomElement fileInfo = m_doc->createElement("FILEINFO");
  writeFileInformation(fileInfo);
  mainElement.appendChild(fileInfo);

  TQDomElement userInfo = m_doc->createElement("USER");
  writeUserInformation(userInfo);
  mainElement.appendChild(userInfo);

  TQDomElement institutions = m_doc->createElement("INSTITUTIONS");
  writeInstitutions(institutions);
  mainElement.appendChild(institutions);

  TQDomElement payees = m_doc->createElement("PAYEES");
  writePayees(payees);
  mainElement.appendChild(payees);

  TQDomElement accounts = m_doc->createElement("ACCOUNTS");
  writeAccounts(accounts);
  mainElement.appendChild(accounts);

  TQDomElement transactions = m_doc->createElement("TRANSACTIONS");
  writeTransactions(transactions);
  mainElement.appendChild(transactions);

  TQDomElement keyvalpairs = writeKeyValuePairs(m_storage->pairs());
  mainElement.appendChild(keyvalpairs);

  TQDomElement schedules = m_doc->createElement("SCHEDULES");
  writeSchedules(schedules);
  mainElement.appendChild(schedules);

  TQDomElement equities = m_doc->createElement("SECURITIES");
  writeSecurities(equities);
  mainElement.appendChild(equities);

  TQDomElement currencies = m_doc->createElement("CURRENCIES");
  writeCurrencies(currencies);
  mainElement.appendChild(currencies);

  TQDomElement prices = m_doc->createElement("PRICES");
  writePrices(prices);
  mainElement.appendChild(prices);

  TQDomElement reports = m_doc->createElement("REPORTS");
  writeReports(reports);
  mainElement.appendChild(reports);

  TQDomElement budgets = m_doc->createElement("BUDGETS");
  writeBudgets(budgets);
  mainElement.appendChild(budgets);

  TQTextStream stream(qf);
  stream.setEncoding(TQTextStream::UnicodeUTF8);
  stream << m_doc->toString();

  delete m_doc;
  m_doc = NULL;

  //hides the progress bar.
  signalProgress(-1, -1);

  // this seems to be nonsense, but it clears the dirty flag
  // as a side-effect.
  m_storage->setLastModificationDate(m_storage->lastModificationDate());

  m_storage = NULL;
}

bool MyMoneyStorageXML::readFileInformation(const TQDomElement& fileInfo)
{
  signalProgress(0, 3, i18n("Loading file information..."));
  bool rc = true;
  TQDomElement temp = findChildElement("CREATION_DATE", fileInfo);
  if (temp == TQDomElement()) {
    rc = false;
  }
  TQString strDate = TQStringEmpty(temp.attribute("date"));
  m_storage->setCreationDate(stringToDate(strDate));
  signalProgress(1, 0);

  temp = findChildElement("LAST_MODIFIED_DATE", fileInfo);
  if (temp == TQDomElement()) {
    rc = false;
  }
  strDate = TQStringEmpty(temp.attribute("date"));
  m_storage->setLastModificationDate(stringToDate(strDate));
  signalProgress(2, 0);

  temp = findChildElement("VERSION", fileInfo);
  if (temp == TQDomElement()) {
    rc = false;
  }
  TQString strVersion = TQStringEmpty(temp.attribute("id"));
  fileVersionRead = strVersion.toUInt(NULL, 16);

  temp = findChildElement("FIXVERSION", fileInfo);
  if (temp != TQDomElement()) {
    TQString strFixVersion = TQStringEmpty(temp.attribute("id"));
    m_storage->setFileFixVersion (strFixVersion.toUInt());
  }
  // FIXME The old version stuff used this rather odd number
  //       We now use increments
  if(fileVersionRead == VERSION_0_60_XML)
    fileVersionRead = 1;
  signalProgress(3, 0);

  return rc;
}

void MyMoneyStorageXML::writeFileInformation(TQDomElement& fileInfo)
{
  TQDomElement creationDate = m_doc->createElement("CREATION_DATE");
  creationDate.setAttribute("date", dateToString(m_storage->creationDate()));
  fileInfo.appendChild(creationDate);

  TQDomElement lastModifiedDate = m_doc->createElement("LAST_MODIFIED_DATE");
  lastModifiedDate.setAttribute("date", dateToString(m_storage->lastModificationDate()));
  fileInfo.appendChild(lastModifiedDate);

  TQDomElement version = m_doc->createElement("VERSION");

  version.setAttribute("id", "1");
  fileInfo.appendChild(version);

  TQDomElement fixVersion = m_doc->createElement("FIXVERSION");
  fixVersion.setAttribute("id", m_storage->fileFixVersion());
  fileInfo.appendChild(fixVersion);
}

void MyMoneyStorageXML::writeUserInformation(TQDomElement& userInfo)
{
  MyMoneyPayee user = m_storage->user();
  userInfo.setAttribute("name", user.name());
  userInfo.setAttribute("email", user.email());

  TQDomElement address = m_doc->createElement("ADDRESS");
  address.setAttribute("street", user.address());
  address.setAttribute("city", user.city());
  address.setAttribute("county", user.state());
  address.setAttribute("zipcode", user.postcode());
  address.setAttribute("telephone", user.telephone());

  userInfo.appendChild(address);
}

bool MyMoneyStorageXML::readUserInformation(const TQDomElement& userElement)
{
  bool rc = true;
  signalProgress(0, 1, i18n("Loading user information..."));

  MyMoneyPayee user;
  user.setName(TQStringEmpty(userElement.attribute("name")));
  user.setEmail(TQStringEmpty(userElement.attribute("email")));

  TQDomElement addressNode = findChildElement("ADDRESS", userElement);
  if(!addressNode.isNull()) {
    user.setAddress(TQStringEmpty(addressNode.attribute("street")));
    user.setCity(TQStringEmpty(addressNode.attribute("city")));
    user.setState(TQStringEmpty(addressNode.attribute("county")));
    user.setPostcode(TQStringEmpty(addressNode.attribute("zipcode")));
    user.setTelephone(TQStringEmpty(addressNode.attribute("telephone")));
  }

  m_storage->setUser(user);
  signalProgress(1, 0);

  return rc;
}

void MyMoneyStorageXML::writeInstitutions(TQDomElement& institutions)
{
  const TQValueList<MyMoneyInstitution> list = m_storage->institutionList();
  TQValueList<MyMoneyInstitution>::ConstIterator it;
  institutions.setAttribute("count", list.count());

  for(it = list.begin(); it != list.end(); ++it)
    writeInstitution(institutions, *it);
}

void MyMoneyStorageXML::writeInstitution(TQDomElement& institution, const MyMoneyInstitution& i)
{
  i.writeXML(*m_doc, institution);
}

void MyMoneyStorageXML::writePayees(TQDomElement& payees)
{
  const TQValueList<MyMoneyPayee> list = m_storage->payeeList();
  TQValueList<MyMoneyPayee>::ConstIterator it;
  payees.setAttribute("count", list.count());

  for(it = list.begin(); it != list.end(); ++it)
    writePayee(payees, *it);
}

void MyMoneyStorageXML::writePayee(TQDomElement& payee, const MyMoneyPayee& p)
{
  p.writeXML(*m_doc, payee);
}

void MyMoneyStorageXML::writeAccounts(TQDomElement& accounts)
{
  TQValueList<MyMoneyAccount> list;
  m_storage->accountList(list);
  TQValueList<MyMoneyAccount>::ConstIterator it;
  accounts.setAttribute("count", list.count()+5);

  writeAccount(accounts, m_storage->asset());
  writeAccount(accounts, m_storage->liability());
  writeAccount(accounts, m_storage->expense());
  writeAccount(accounts, m_storage->income());
  writeAccount(accounts, m_storage->equity());

  signalProgress(0, list.count(), i18n("Saving accounts..."));
  int i = 0;
  for(it = list.begin(); it != list.end(); ++it, ++i) {
    writeAccount(accounts, *it);
    signalProgress(i, 0);
  }
}

void MyMoneyStorageXML::writeAccount(TQDomElement& account, const MyMoneyAccount& p)
{
  p.writeXML(*m_doc, account);
}

void MyMoneyStorageXML::writeTransactions(TQDomElement& transactions)
{
  MyMoneyTransactionFilter filter;
  filter.setReportAllSplits(false);
  TQValueList<MyMoneyTransaction> list;
  m_storage->transactionList(list, filter);
  transactions.setAttribute("count", list.count());

  TQValueList<MyMoneyTransaction>::ConstIterator it;

  signalProgress(0, list.count(), i18n("Saving transactions..."));

  int i = 0;
  for(it = list.begin(); it != list.end(); ++it, ++i)
  {
    writeTransaction(transactions, *it);
    signalProgress(i, 0);
  }
}

void MyMoneyStorageXML::writeTransaction(TQDomElement& transaction, const MyMoneyTransaction& tx)
{
  tx.writeXML(*m_doc, transaction);
}

void MyMoneyStorageXML::writeSchedules(TQDomElement& scheduled)
{
  const TQValueList<MyMoneySchedule> list = m_storage->scheduleList();
  TQValueList<MyMoneySchedule>::ConstIterator it;
  scheduled.setAttribute("count", list.count());

  for(it = list.begin(); it != list.end(); ++it)
  {
    this->writeSchedule(scheduled, *it);
  }
}

void MyMoneyStorageXML::writeSchedule(TQDomElement& scheduledTx, const MyMoneySchedule& tx)
{
  tx.writeXML(*m_doc, scheduledTx);
}

void MyMoneyStorageXML::writeSecurities(TQDomElement& equities)
{
  const TQValueList<MyMoneySecurity> securityList = m_storage->securityList();
  equities.setAttribute("count", securityList.count());
  if(securityList.size())
  {
    for(TQValueList<MyMoneySecurity>::ConstIterator it = securityList.begin(); it != securityList.end(); ++it)
    {
      writeSecurity(equities, (*it));
    }
  }
}

void MyMoneyStorageXML::writeSecurity(TQDomElement& securityElement, const MyMoneySecurity& security)
{
  security.writeXML(*m_doc, securityElement);
}

void MyMoneyStorageXML::writeCurrencies(TQDomElement& currencies)
{
  const TQValueList<MyMoneySecurity> currencyList = m_storage->currencyList();
  currencies.setAttribute("count", currencyList.count());
  if(currencyList.size())
  {
    for(TQValueList<MyMoneySecurity>::ConstIterator it = currencyList.begin(); it != currencyList.end(); ++it)
    {
      writeSecurity(currencies, (*it));
    }
  }
}

void MyMoneyStorageXML::writeReports(TQDomElement& tqparent)
{
  const TQValueList<MyMoneyReport> list = m_storage->reportList();
  TQValueList<MyMoneyReport>::ConstIterator it;
  tqparent.setAttribute("count", list.count());

  signalProgress(0, list.count(), i18n("Saving reports..."));
  unsigned i = 0;
  for(it = list.begin(); it != list.end(); ++it)
  {
    (*it).writeXML(*m_doc, tqparent);
    signalProgress(++i, 0);
  }
}

void MyMoneyStorageXML::writeBudgets(TQDomElement& tqparent)
{
  const TQValueList<MyMoneyBudget> list = m_storage->budgetList();
  TQValueList<MyMoneyBudget>::ConstIterator it;
  tqparent.setAttribute("count", list.count());

  signalProgress(0, list.count(), i18n("Saving budgets..."));
  unsigned i = 0;
  for(it = list.begin(); it != list.end(); ++it)
  {
    writeBudget(tqparent, (*it));
    signalProgress(++i, 0);
  }
}

void MyMoneyStorageXML::writeBudget(TQDomElement& budget, const MyMoneyBudget& b)
{
  b.writeXML(*m_doc, budget);
}


TQDomElement MyMoneyStorageXML::findChildElement(const TQString& name, const TQDomElement& root)
{
  TQDomNode child = root.firstChild();
  while(!child.isNull())
  {
    if(child.isElement())
    {
      TQDomElement childElement = child.toElement();
      if(name == childElement.tagName())
      {
        return childElement;
      }
    }

    child = child.nextSibling();
  }
  return TQDomElement();
}

TQDomElement MyMoneyStorageXML::writeKeyValuePairs(const TQMap<TQString, TQString> pairs)
{
  if(m_doc)
  {
    TQDomElement keyValPairs = m_doc->createElement("KEYVALUEPAIRS");

    TQMap<TQString, TQString>::const_iterator it;
    for(it = pairs.begin(); it != pairs.end(); ++it)
    {
      TQDomElement pair = m_doc->createElement("PAIR");
      pair.setAttribute("key", it.key());
      pair.setAttribute("value", it.data());
      keyValPairs.appendChild(pair);
    }
    return keyValPairs;
  }
  return TQDomElement();
}

void MyMoneyStorageXML::writePrices(TQDomElement& prices)
{
  const MyMoneyPriceList list = m_storage->priceList();
  MyMoneyPriceList::ConstIterator it;
  prices.setAttribute("count", list.count());

  for(it = list.begin(); it != list.end(); ++it)
  {
    TQDomElement price = m_doc->createElement("PRICEPAIR");
    price.setAttribute("from", it.key().first);
    price.setAttribute("to", it.key().second);
    writePricePair(price, *it);
    prices.appendChild(price);
  }
}

void MyMoneyStorageXML::writePricePair(TQDomElement& price, const MyMoneyPriceEntries& p)
{
  MyMoneyPriceEntries::ConstIterator it;
  for(it = p.begin(); it != p.end(); ++it) {
    TQDomElement entry = m_doc->createElement("PRICE");
    writePrice(entry, *it);
    price.appendChild(entry);
  }
}

void MyMoneyStorageXML::writePrice(TQDomElement& price, const MyMoneyPrice& p)
{
  price.setAttribute("date", p.date().toString(Qt::ISODate));
  price.setAttribute("price", p.rate(TQString()).toString());
  price.setAttribute("source", p.source());
}

void MyMoneyStorageXML::setProgressCallback(void(*callback)(int, int, const TQString&))
{
  m_progressCallback = callback;
}

void MyMoneyStorageXML::signalProgress(int current, int total, const TQString& msg)
{
  if(m_progressCallback != 0)
    (*m_progressCallback)(current, total, msg);
}

/*!
    This convenience function returns all of the remaining data in the
    device.

    @note It's copied from the original TQt sources and modified to
          fix a problem with KFilterDev that does not correctly return
          atEnd() status in certain circumstances which caused our
          application to lock at startup.
*/
QByteArray QIODevice::readAll()
{
  if ( TQT_TQIODEVICE(this)->isDirectAccess() ) {
    // we know the size
    int n = size()-TQT_TQIODEVICE(this)->at(); // ### fix for 64-bit or large files?
    int totalRead = 0;
    TQByteArray ba( n );
    char* c = ba.data();
    while ( n ) {
      int r = TQT_TQIODEVICE(this)->readBlock( c, n );
      if ( r < 0 )
        return TQByteArray();
      n -= r;
      c += r;
      totalRead += r;
      // If we have a translated file, then it is possible that
      // we read less bytes than size() reports
      if ( atEnd() ) {
        ba.resize( totalRead );
        break;
      }
    }
    return ba;
  } else {
    // read until we reach the end
    const int blocksize = 512;
    int nread = 0;
    TQByteArray ba;
    int r = 1;
    while ( !atEnd() && r != 0) {
      ba.resize( nread + blocksize );
      r = TQT_TQIODEVICE(this)->readBlock( ba.data()+nread, blocksize );
      if ( r < 0 )
        return TQByteArray();
      nread += r;
    }
    ba.resize( nread );
    return ba;
  }
}