/***************************************************************************
                          kmymoneycalculator.cpp  -  description
                             -------------------
    begin                : Sat Oct 19 2002
    copyright            : (C) 2000-2002 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.                                   *
 *                                                                         *
 ***************************************************************************/

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

#include <tqlabel.h>
#include <tqsignalmapper.h>
#include <tqregexp.h>

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

#include <kglobal.h>
#include <klocale.h>

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

#include "kmymoneycalculator.h"

kMyMoneyCalculator::kMyMoneyCalculator(TQWidget* parent, const char *name)
  : TQFrame(parent, name)
{
  m_comma = KGlobal::locale()->monetaryDecimalSymbol()[0];
  m_clearOperandOnDigit = false;

  TQGridLayout* grid = new TQGridLayout(this, 5, 5, 1, 2);

  display = new TQLabel(this);
  display->setBackgroundColor(TQColor("#BDFFB4"));

  display->setFrameStyle( TQFrame::Panel | TQFrame::Sunken );
  display->tqsetAlignment(TQt::AlignRight | TQt::AlignVCenter);
  grid->addMultiCellWidget(display, 0, 0, 0, 4);

  buttons[0] = new KPushButton("0", this);
  buttons[1] = new KPushButton("1", this);
  buttons[2] = new KPushButton("2", this);
  buttons[3] = new KPushButton("3", this);
  buttons[4] = new KPushButton("4", this);
  buttons[5] = new KPushButton("5", this);
  buttons[6] = new KPushButton("6", this);
  buttons[7] = new KPushButton("7", this);
  buttons[8] = new KPushButton("8", this);
  buttons[9] = new KPushButton("9", this);
  buttons[PLUS] = new KPushButton("+", this);
  buttons[MINUS] = new KPushButton("-", this);
  buttons[STAR] = new KPushButton("X", this);
  buttons[COMMA] = new KPushButton(m_comma, this);
  buttons[ETQUAL] = new KPushButton("=", this);
  buttons[SLASH] = new KPushButton("/", this);
  buttons[CLEAR] = new KPushButton("C", this);
  buttons[CLEARALL] = new KPushButton("AC", this);
  buttons[PLUSMINUS] = new KPushButton("+-", this);
  buttons[PERCENT] = new KPushButton("%", this);

  grid->addWidget(buttons[7], 1, 0);
  grid->addWidget(buttons[8], 1, 1);
  grid->addWidget(buttons[9], 1, 2);
  grid->addWidget(buttons[4], 2, 0);
  grid->addWidget(buttons[5], 2, 1);
  grid->addWidget(buttons[6], 2, 2);
  grid->addWidget(buttons[1], 3, 0);
  grid->addWidget(buttons[2], 3, 1);
  grid->addWidget(buttons[3], 3, 2);
  grid->addWidget(buttons[0], 4, 1);

  grid->addWidget(buttons[COMMA], 4, 0);
  grid->addWidget(buttons[PLUS], 3, 3);
  grid->addWidget(buttons[MINUS], 4, 3);
  grid->addWidget(buttons[STAR], 3, 4);
  grid->addWidget(buttons[SLASH], 4, 4);
  grid->addWidget(buttons[ETQUAL], 4, 2);
  grid->addWidget(buttons[PLUSMINUS], 2, 3);
  grid->addWidget(buttons[PERCENT], 2, 4);
  grid->addWidget(buttons[CLEAR], 1, 3);
  grid->addWidget(buttons[CLEARALL], 1, 4);

  buttons[ETQUAL]->setFocus();

  op1 = 0.0;
  stackedOp = op = 0;
  operand = TQString();
  changeDisplay("0");

  // connect the digit signals through a signal mapper
  TQSignalMapper* mapper = new TQSignalMapper(TQT_TQOBJECT(this));
  for(int i = 0; i < 10; ++i) {
    mapper->setMapping(TQT_TQOBJECT(buttons[i]), i);
    connect(buttons[i], TQT_SIGNAL(clicked()), mapper, TQT_SLOT(map()));
  }
  connect(mapper, TQT_SIGNAL(mapped(int)), this, TQT_SLOT(digitClicked(int)));

  // connect the calculation operations through another mapper
  mapper = new TQSignalMapper(TQT_TQOBJECT(this));
  for(int i = PLUS; i <= ETQUAL; ++i) {
    mapper->setMapping(TQT_TQOBJECT(buttons[i]), i);
    connect(buttons[i], TQT_SIGNAL(clicked()), mapper, TQT_SLOT(map()));
  }
  connect(mapper, TQT_SIGNAL(mapped(int)), this, TQT_SLOT(calculationClicked(int)));

  // connect all remaining signals
  connect(buttons[COMMA], TQT_SIGNAL(clicked()), TQT_SLOT(commaClicked()));
  connect(buttons[PLUSMINUS], TQT_SIGNAL(clicked()), TQT_SLOT(plusminusClicked()));
  connect(buttons[PERCENT], TQT_SIGNAL(clicked()), TQT_SLOT(percentClicked()));
  connect(buttons[CLEAR], TQT_SIGNAL(clicked()), TQT_SLOT(clearClicked()));
  connect(buttons[CLEARALL], TQT_SIGNAL(clicked()), TQT_SLOT(clearAllClicked()));

  for(int i = 0; i < MAX_BUTTONS; ++i) {
    buttons[i]->setMinimumSize(40, 30);
    buttons[i]->setMaximumSize(40, 30);
  }
  int height = 4 * (buttons[0]->minimumHeight()+6) + 15;
  int width = 5 * (buttons[0]->minimumWidth()+6);

  setMinimumSize(width, height);
  setMaximumSize(width, height);

  show();
}

kMyMoneyCalculator::~kMyMoneyCalculator()
{
}

void kMyMoneyCalculator::digitClicked(int button)
{
  if(m_clearOperandOnDigit) {
    operand = TQString();
    m_clearOperandOnDigit = false;
  }

  operand += TQChar(button + 0x30);
  if(operand.length() > 16)
    operand = operand.left(16);
  changeDisplay(operand);
}

void kMyMoneyCalculator::commaClicked(void)
{
  if(operand.length() == 0)
    operand = "0";
  if(operand.contains('.', FALSE) == 0)
    operand.append('.');

  if(operand.length() > 16)
    operand = operand.left(16);
  changeDisplay(operand);
}

void kMyMoneyCalculator::plusminusClicked(void)
{
  if(operand.length() == 0 && m_result.length() > 0)
    operand = m_result;

  if(operand.length() > 0) {
    if(operand.find('-') != -1)
      operand.replace('-', TQString());
    else
      operand.prepend('-');
    changeDisplay(operand);
  }
}

void kMyMoneyCalculator::calculationClicked(int button)
{
  if(operand.length() == 0 && op != 0 && button == ETQUAL) {
    op = 0;
    m_result = normalizeString(op1);
    changeDisplay(m_result);

  } else if(operand.length() > 0 && op != 0) {
    // perform operation
    double op2 = operand.toDouble();
    bool error = false;

    // if the pending operation is addition and we now do multiplication
    // we just stack op1 and remember the operation in
    if((op == PLUS || op == MINUS) && (button == STAR || button == SLASH)) {
      op0 = op1;
      stackedOp = op;
      op = 0;
    }

    switch(op) {
      case PLUS:
        op2 = op1 + op2;
        break;
      case MINUS:
        op2 = op1 - op2;
        break;
      case STAR:
        op2 = op1 * op2;
        break;
      case SLASH:
        if(op2 == 0.0)
          error = true;
        else
          op2 = op1 / op2;
        break;
    }

    // if we have a pending addition operation, and the next operation is
    // not multiplication, we calculate the stacked operation
    if(stackedOp && button != STAR && button != SLASH) {
      switch(stackedOp) {
        case PLUS:
          op2 = op0 + op2;
          break;
        case MINUS:
          op2 = op0 - op2;
          break;
      }
      stackedOp = 0;
    }

    if(error) {
      op = 0;
      changeDisplay("Error");
      operand = TQString();
    } else {
      op1 = op2;
      m_result = normalizeString(op1);
      changeDisplay(m_result);
    }
  } else if(operand.length() > 0 && op == 0) {
    op1 = operand.toDouble();
    m_result = normalizeString(op1);
    changeDisplay(m_result);
  }

  if(button != ETQUAL) {
    op = button;
  } else {
    op = 0;
    emit signalResultAvailable();
  }
  operand = TQString();
}

TQString kMyMoneyCalculator::normalizeString(const double& val)
{
  TQString str;
  str.setNum(val, 'f');
  int i = str.length();
  while(i > 1 && str[i-1] == '0') {
    --i;
  }
  // cut off trailing 0's
  str.remove(i, str.length());
  if(str.length() > 0) {
    // possibly remove trailing period
    if(str[str.length()-1] == '.') {
      str.remove(str.length()-1, 1);
    }
  }
  return str;
}

void kMyMoneyCalculator::clearClicked(void)
{
  if(operand.length() > 0) {
    operand = operand.left(operand.length() - 1);
  }
  if(operand.length() == 0)
    changeDisplay("0");
  else
    changeDisplay(operand);
}

void kMyMoneyCalculator::clearAllClicked(void)
{
  operand = TQString();
  op = 0;
  changeDisplay("0");
}

void kMyMoneyCalculator::percentClicked(void)
{
  if(op != 0) {
    double op2 = operand.toDouble();
    switch(op) {
      case PLUS:
      case MINUS:
        op2 = (op1 * op2) / 100;
        break;

      case STAR:
      case SLASH:
        op2 /= 100;
        break;
    }
    operand = normalizeString(op2);
    changeDisplay(operand);
  }
}

const TQString kMyMoneyCalculator::result(void) const
{
  TQString txt = m_result;
  txt.replace(TQRegExp("\\."), m_comma);
  if(txt[0] == '-') {
    txt = txt.mid(1); // get rid of the minus sign
    TQString mask;
    switch(KGlobal::locale()->negativeMonetarySignPosition()) {
      case KLocale::ParensAround:
        mask = "(%1)";
        break;
      case KLocale::AfterQuantityMoney:
        mask = "%1-";
        break;
      case KLocale::AfterMoney:
      case KLocale::BeforeMoney:
        mask = "%1 -";
        break;
      case KLocale::BeforeQuantityMoney:
        mask = "-%1";
        break;
    }
    txt = TQString(mask).tqarg(txt);
  }
  return txt;
}

void kMyMoneyCalculator::changeDisplay(const TQString& str)
{
  TQString txt = str;
  txt.replace(TQRegExp("\\."), m_comma);
  display->setText("<b>" + txt + "</b>");
}

void kMyMoneyCalculator::keyPressEvent(TQKeyEvent* ev)
{
  int button = -1;

  switch(ev->key()) {
    case TQt::Key_0:
    case TQt::Key_1:
    case TQt::Key_2:
    case TQt::Key_3:
    case TQt::Key_4:
    case TQt::Key_5:
    case TQt::Key_6:
    case TQt::Key_7:
    case TQt::Key_8:
    case TQt::Key_9:
      if(m_clearOperandOnDigit) {
        operand = TQString();
        m_clearOperandOnDigit = false;
      }
      button = ev->key() - TQt::Key_0;
      break;
    case TQt::Key_Plus:
      button = PLUS;
      break;
    case TQt::Key_Minus:
      button = MINUS;
      break;
    case TQt::Key_Comma:
    case TQt::Key_Period:
      if(m_clearOperandOnDigit) {
        operand = TQString();
        m_clearOperandOnDigit = false;
      }
      button = COMMA;
      break;
    case TQt::Key_Slash:
      button = SLASH;
      break;
    case TQt::Key_Backspace:
      button = CLEAR;
      break;
    case TQt::Key_Asterisk:
      button = STAR;
      break;
    case TQt::Key_Return:
    case TQt::Key_Enter:
    case TQt::Key_Equal:
      button = ETQUAL;
      break;
    case TQt::Key_Escape:
      button = CLEARALL;
      break;
    case TQt::Key_Percent:
      button = PERCENT;
      break;
    default:
      ev->ignore();
      break;
  }
  if(button != -1)
    buttons[button]->animateClick();

  m_clearOperandOnDigit = false;
}

void kMyMoneyCalculator::setInitialValues(const TQString& value, TQKeyEvent* ev)
{
  bool negative = false;
  // setup operand
  operand = value;
  operand.replace(TQRegExp(TQString("\\")+KGlobal::locale()->thousandsSeparator()), TQString());
  operand.replace(TQRegExp(TQString("\\")+m_comma), ".");
  if(operand.contains('(')) {
    negative = true;
    operand.replace("(", TQString());
    operand.replace(")", TQString());
  }
  if(operand.contains('-')) {
    negative = true;
    operand.replace("-", TQString());
  }
  if(operand.isEmpty())
    operand = "0";
  else if(negative)
    operand = TQString("-%1").tqarg(operand);

  changeDisplay(operand);

  // and operation
  op = 0;
  if(ev)
    keyPressEvent(ev);
  else
    m_clearOperandOnDigit = true;
}

#include "kmymoneycalculator.moc"