/*
 * Copyright 2019       Thomas Baumgart <tbaumgart@kde.org>
 *
 * 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "ledgerfilterbase.h"
#include "ledgerfilterbase_p.h"

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

#include <QDate>

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

#include <KLocalizedString>

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

#include "mymoneyenums.h"
#include "mymoneyfile.h"
#include "journalmodel.h"
#include "accountsmodel.h"
#include "specialdatesmodel.h"

using namespace eMyMoney;

LedgerFilterBase::LedgerFilterBase(LedgerFilterBasePrivate* dd, QObject* parent)
: QSortFilterProxyModel(parent)
, d_ptr(dd)
{
  Q_D(LedgerFilterBase);
  d->concatModel = new KConcatenateRowsProxyModel(parent);

  setFilterRole(eMyMoney::Model::Roles::SplitAccountIdRole);
  setFilterKeyColumn(0);
  setSortRole(eMyMoney::Model::Roles::TransactionPostDateRole);
}

LedgerFilterBase::~LedgerFilterBase()
{
}

void LedgerFilterBase::setAccountType(Account::Type type)
{
  Q_D(LedgerFilterBase);
  d->accountType = type;
}

QVariant LedgerFilterBase::headerData(int section, Qt::Orientation orientation, int role) const
{
  if(orientation == Qt::Horizontal && role == Qt::DisplayRole) {
    Q_D(const LedgerFilterBase);
    switch(section) {
      case JournalModel::Column::Payment:
        switch(d->accountType) {
          case Account::Type::CreditCard:
            return i18nc("Payment made with credit card", "Charge");

          case Account::Type::Asset:
          case Account::Type::AssetLoan:
            return i18nc("Decrease of asset/liability value", "Decrease");

          case Account::Type::Liability:
          case Account::Type::Loan:
            return i18nc("Increase of asset/liability value", "Increase");

          case Account::Type::Income:
          case Account::Type::Expense:
            return i18n("Income");

          default:
            break;
        }
        break;

      case JournalModel::Column::Deposit:
        switch(d->accountType) {
          case Account::Type::CreditCard:
            return i18nc("Payment towards credit card", "Payment");

          case Account::Type::Asset:
          case Account::Type::AssetLoan:
            return i18nc("Increase of asset/liability value", "Increase");

          case Account::Type::Liability:
          case Account::Type::Loan:
            return i18nc("Decrease of asset/liability value", "Decrease");

          case Account::Type::Income:
          case Account::Type::Expense:
            return i18n("Expense");

          default:
            break;
        }
        break;
    }
  }
  return QSortFilterProxyModel::headerData(section, orientation, role);
}

bool LedgerFilterBase::lessThan(const QModelIndex& left, const QModelIndex& right) const
{
  Q_D(const LedgerFilterBase);

  // make sure that the dummy transaction is shown last in any case
  if(left.data(eMyMoney::Model::IdRole).toString().isEmpty()) {
    return false;

  } else if(right.data(eMyMoney::Model::IdRole).toString().isEmpty()) {
    return true;
  }

  const auto model = MyMoneyFile::baseModel();
  // make sure that the online balance is the last entry of a day
  // and the date headers are the first
  if (left.data(eMyMoney::Model::TransactionPostDateRole).toDate() == right.data(eMyMoney::Model::TransactionPostDateRole).toDate()) {
    const auto leftModel = model->baseModel(left);
    const auto rightModel = model->baseModel(right);
    if (leftModel != rightModel) {
      if (d->isAccountsModel(leftModel)) {
        return false;
      } else if (d->isAccountsModel(rightModel)) {
        return true;
      } else if(d->isSpecialDatesModel(leftModel)) {
        return true;
      } else if(d->isSpecialDatesModel(rightModel)) {
        return false;
      }
    }
    // same model and same post date, the ids decide
    return left.data(eMyMoney::Model::IdRole).toString() < right.data(eMyMoney::Model::IdRole).toString();
  }

  // otherwise use normal sorting
  return QSortFilterProxyModel::lessThan(left, right);
}

bool LedgerFilterBase::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
{
  Q_D(const LedgerFilterBase);

  // if no filter is set, we don't display anything
  if (d->filterIds.isEmpty())
    return false;

  // in case it's a special date entry, we accept it
  QModelIndex idx = sourceModel()->index(source_row, 0, source_parent);
  const auto baseModel = MyMoneyFile::baseModel()->baseModel(idx);
  if (d->isSpecialDatesModel(baseModel)) {
    return (sortRole() == eMyMoney::Model::TransactionPostDateRole);
  }

  // now do the filtering
  const auto id = idx.data(filterRole()).toString();
  bool rc = d->filterIds.contains(id);

  // in case a journal entry has no id, it is the new transaction placeholder
  if(!rc) {
    rc = idx.data(eMyMoney::Model::IdRole).toString().isEmpty();
  }
  return rc;
}

void LedgerFilterBase::setFilterFixedString(const QString& id)
{
  Q_D(LedgerFilterBase);
  setFilterFixedStrings(QStringList() << id);
}

void LedgerFilterBase::setFilterFixedStrings(const QStringList& filters)
{
  Q_D(LedgerFilterBase);
  d->filterIds = filters;
  invalidateFilter();
}

QStringList LedgerFilterBase::filterFixedStrings() const
{
  Q_D(const LedgerFilterBase);
  return d->filterIds;
}


void LedgerFilterBase::setShowEntryForNewTransaction(bool show)
{
  Q_D(LedgerFilterBase);

  if (show) {
    addSourceModel(MyMoneyFile::instance()->journalModel()->newTransaction());
  } else {
    removeSourceModel(MyMoneyFile::instance()->journalModel()->newTransaction());
  }
}


void LedgerFilterBase::addSourceModel(QAbstractItemModel* model)
{
  Q_D(LedgerFilterBase);
  if (model && !d->sourceModels.contains(model)) {
    d->concatModel->addSourceModel(model);
    d->sourceModels.insert(model);
    invalidateFilter();
  }
}

void LedgerFilterBase::removeSourceModel(QAbstractItemModel* model)
{
  Q_D(LedgerFilterBase);
  if (model && d->sourceModels.contains(model)) {
    d->concatModel->removeSourceModel(model);
    d->sourceModels.remove(model);
    invalidateFilter();
  }
}
