/*  "calcqt.cpp" : Qtopia source code for "RPN calculator on Zaurus"
 * 
 *    This is a Qtopia program to realize "RPN caculator" on Sharp Zaurus,
 *  which consists of header file "calcqt.h", main program code "calcqt.cpp"
 *  (this file), and image file "calcqt.png".
 *  Package file "calcqt_YYYY.MM.DD_arm.ipk" is also available.
 *    The author, T. Nakatsuka, does not insist any right on this program,
 *  i.e., free to re-distribute to any place with/without any modification.
 *    The author acknowledges Hewlett-Packard Corp. for their intelligent
 *  product of micro-calculators, which were borne in mind during designing,
 *  and Trolltech for providing comprehensive environment of Qt/Qtopia.
 */

#include "calcqt.h"
#include <qpe/qpeapplication.h>
#include <qfont.h>
#include <math.h>
#define PI      3.141592653589793
#define EXP     2.718281828459045
#define PNGFILE "/opt/QtPalmtop/bin/calcqt.png"

MainWin::MainWin( QWidget *parent, const char *name, WFlags f )
 : QLabel( parent, name, f )
{
  QFont fnt("lcfont", 18, QFont::Bold);
  this->setFont(fnt);
  this->setGeometry(0, 0, 332, 412);
  keymap.load(PNGFILE);
  this->setPixmap(keymap);
  deg = new QRadioButton("Degree", this);
  deg->setGeometry(106, 6, 90, 30);
  deg->setBackgroundColor(QColor(255,255,255));
  deg->setChecked(TRUE);
  rad = new QRadioButton("Radian", this);
  rad->setGeometry(216, 6, 90, 30);
  rad->setBackgroundColor(QColor(255,255,255));
  rad->setChecked(FALSE);
  regT = new QLabel(this, "regT");
  regT->setGeometry(46, 43, 270, 19);
  regT->setBackgroundColor(QColor(128,128,128));
  regT->setText("0");
  regZ = new QLabel(this, "regZ");
  regZ->setGeometry(46, 69, 270, 19);
  regZ->setBackgroundColor(QColor(128,128,128));
  regZ->setText("0");
  regY = new QLabel(this, "regY");
  regY->setGeometry(46, 95, 270, 19);
  regY->setBackgroundColor(QColor(128,128,128));
  regY->setText("0");
  regX = new QLabel(this, "regX");
  regX->setGeometry(46, 121, 270, 19);
  regX->setBackgroundColor(QColor(255,255,255));
  regX->setText("0");
  connect(deg, SIGNAL(clicked()), this, SLOT(radio1event()));
  connect(rad, SIGNAL(clicked()), this, SLOT(radio2event()));
  xstr = "0";
  x = 0.; y = 0.; z = 0.; t = 0.; r = 0.; tmp = 0.;
  key = 0; l = 1; lce = 0; kdeg = 1;
}

MainWin::~MainWin()
{
  delete regX; delete regY; delete regZ; delete regT;
  delete rad; delete deg;
}

/*
 *  key : Status for Register-X
 *      = 0 : Register-X shall be cleared by New-Value entry
 *             [after ENTER/CLR/CLX/STO operation]
 *      = 1 : CHS input after key=0
 *      = 2 : Register-X shall be pushed (on stack) by New-Value entry
 *             [after Arithmetic or SWAP/ROT operation,
 *              or definite value entry by RCL/'pi'/'e']
 *      = 3 : CHS input after key=2
 *      =4-7: on the way of New-Value entry
 *             [after Numeric input keys (0-9/./EEX)]
 *            =4 : neither . nor EEX input yet
 *            =5 : . already input but EEX not yet
 *            =6 : EEX already input but no exponent digit yet
 *            =7 : at least 1 exponent digit input already
 *      = 9 : error detected (use CLR/CLX to reset)
 */

void MainWin::keyPressEvent( QKeyEvent *ke )
{
  int kc = ke->key();
  if (key < 9) {
    switch (kc) {               /* Only limited key inputs are supported */
      case '0': num(0); break;                  /* '0'-'9' */
      case '1': num(1); break;
      case '2': num(2); break;
      case '3': num(3); break;
      case '4': num(4); break;
      case '5': num(5); break;
      case '6': num(6); break;
      case '7': num(7); break;
      case '8': num(8); break;
      case '9': num(9); break;
      case '^': chs(); break;                   /* ^ for ch +/- */
      case 'E':                                 /* E(e) for Eex */
      case 'e': if (key<6) eex(); break;
      case '.': if (key<5) dot(); break;        /* decimal point */
      case '*': y *= x; pop(); break;           /* arithmetic *,/,+,- */
      case '/': y /= x; pop(); break;
      case '+': y += x; pop(); break;
      case '-': if (key == 6) chs();            /* Only after Eex, '-'   */
                else { y -= x; pop(); }         /* may be used to ch +/- */
                break;
      case Key_Return:
      case Key_Enter:                           /* 'Enter' */
                push(0); showx(); key=0; break;
    }
  }
}

void MainWin::mousePressEvent( QMouseEvent *me )
{
  int mx, my;
  mx = (me->x() + 44) / 60; my = (426 - me->y()) / 31;
  if (key == 9) {
    if (mx != 5) return;
    if ((my<4) || (my>5)) return;
  }
  switch (my) {
    case 1:
      switch (mx) {
        case 1: y += x; pop(); break;                           /* '+' */
        case 2: num(0); break;                                  /* '0' */
        case 3: if (key<5) dot(); break;                        /* '.' */
        case 4: if (key==3) push(1); else if (key>=2) push(0);  /* 'PI' */
                x = PI; if ((key==1) || (key==3)) x = -x;
                showx(); break;
        case 5: if (key==3) push(1); else if (key>=2) push(0);  /* 'e' */
                x = EXP; if ((key==1) || (key==3)) x = -x;
                showx(); break;
      }
      break;
    case 2:
      switch (mx) {
        case 1: y -= x; pop(); break;                           /* '-' */
        case 2: num(1); break;                                  /* '1' */
        case 3: num(2); break;                                  /* '2' */
        case 4: num(3); break;                                  /* '3' */
        case 5: if (key==3) push(1); else if (key>=2) push(0);  /* RCL */
                x = r; if ((key==1) || (key==3)) x = -x;
                showx(); break;
      }
      break;
    case 3:
      switch (mx) {
        case 1: y *= x; pop(); break;                           /* '*' */
        case 2: num(4); break;                                  /* '4' */
        case 3: num(5); break;                                  /* '5' */
        case 4: num(6); break;                                  /* '6' */
        case 5: r = x; showx(); key=0; break;                   /* STO */
      }
      break;
    case 4:
      switch (mx) {
        case 1: y /= x; pop(); break;                           /* '/' */
        case 2: num(7); break;                                  /* '7' */
        case 3: num(8); break;                                  /* '8' */
        case 4: num(9); break;                                  /* '9' */
        case 5: x = 0.; showx(); key=0; break;                  /* CLX */
      }
      break;
    case 5:
      switch (mx) {
        case 1:
        case 2: push(0); showx(); key=0; break;                 /* Enter */
        case 3: chs(); break;                                   /* ch +/- */
        case 4: if (key<6) eex(); break;                        /* Eex */
        case 5: x = 0.; y = 0.; z = 0.; push(0); showx();       /* CLR */
                r = 0.; key=0; break;
      }
      break;
    case 6:
      switch (mx) {
        case 1: y = pow(y, x); pop(); break;                    /* y**x */
        case 2: x = 1./x; showx(); break;                       /* 1./x */
        case 3: x = sqrt(x); showx(); break;                    /* sqrt */
        case 4: tmp = x; x = y; y = tmp; showx();               /* swap */
                regY->setText(QString::number(y,'g',15)); break;
        case 5: tmp = x; pop(); t = tmp;                        /* rot */
                regT->setText(QString::number(t,'g',15)); break;
      }
      break;
    case 7:
      switch (mx) {
        case 1: x = exp(x); showx(); break;                     /* exp */
        case 2: x = log(x); showx(); break;                     /* ln */
        case 3: rad1(); x = sin(x); showx(); break;             /* sin */
        case 4: rad1(); x = cos(x); showx(); break;             /* cos */
        case 5: rad1(); x = tan(x); showx(); break;             /* tan */
      }
      break;
    case 8:
      switch (mx) {
        case 1: x = pow(10., x); showx(); break;                /* 10**x */
        case 2: x = log10(x); showx(); break;                   /* Log10 */
        case 3: x = asin(x); rad0(); showx(); break;            /* arcsin */
        case 4: x = acos(x); rad0(); showx(); break;            /* arccos */
        case 5: x = atan(x); rad0(); showx(); break;            /* arctan */
      }
      break;
  }
}

void MainWin::radio1event()
{
  deg->setChecked(TRUE);
  if (kdeg == 0) { kdeg = 1; rad->setChecked(FALSE); }
}

void MainWin::radio2event()
{
  rad->setChecked(TRUE);
  if (kdeg != 0) { kdeg = 0; deg->setChecked(FALSE); }
}

void MainWin::showx()
{ if (isnan(x) || isinf(x)) error();
  else {
    xstr.setNum(x,'g',15); l = xstr.length(); key=2; regX->setText(xstr);
  }
}
void MainWin::push(int k)
{ t = z; z = y;
  if (k==0) y = x; else y = -x;
  regY->setText(QString::number(y,'g',15));
  regZ->setText(QString::number(z,'g',15));
  regT->setText(QString::number(t,'g',15));
}
void MainWin::pop()
{ x = y; y = z; z = t; showx();
  regY->setText(QString::number(y,'g',15));
  regZ->setText(QString::number(z,'g',15));
}
void MainWin::rad0() { if (kdeg==1) x /= PI/180.; }
void MainWin::rad1() { if (kdeg==1) x *= PI/180.; }
void MainWin::num(int i)
{ if (key<4) {
    if (key==2) push(0); else if (key==3) push(1);
    if ((key==0) || (key==2)) { xstr = ""; l=0; }
    else { xstr = "-"; l=1; }
    key=4;
  }
  xstr.append(QString::number(i)); l++;
  if (key==6) key=7;
  x = xstr.toDouble(); regX->setText(xstr);
}
void MainWin::dot()
{ if (key<4) {
    if (key==2) push(0); else if (key==3) push(1);
    if ((key==1) || (key==3)) { xstr = "-0"; l=2; x = 0.; }
    else { xstr = "0"; l=1; x = 0.; }
  }
  xstr.append("."); l++; key=5; regX->setText(xstr);
}
void MainWin::eex()
{ if (key<4) {
    if (key==2) push(0); else if (key==3) push(1);
    xstr = "";
    if ((key==1) || (key==3)) { xstr = "-1."; l=3; x = -1.; }
    else { xstr = "1."; l=2; x = 1.; }
  }
  xstr.append("E"); lce=l++; key=6; regX->setText(xstr);
}
void MainWin::chs()
{ if (key<6) {
    if ((key==0) || (key==2)) key++;
    if ((key==1) || (key==3)) key--;
    if (xstr.at(0) == '-') { xstr.remove(0,1); l--; }
    else { xstr.insert(0, "-"); l++; }
    x = -x;
  }
  else if (key==6) {
    if (xstr.at(lce) == 'E') { lce++; xstr.append("-"); l++; }
    else { xstr.remove(lce--,1); l--; }
  }
  else if (key==7) {
    if (xstr.at(lce) == 'E') { xstr.insert(++lce, "-"); l++; }
    else { xstr.remove(lce--,1); l--; }
    x = xstr.toDouble();
  }
  regX->setText(xstr);
}
void MainWin::error()
{ regX->setText(" E R R O R !!"); x = 0.; key=9; }

int main( int argc, char *argv[] )
{
  QPEApplication app(argc, argv);
  QWidget palmtop;
  palmtop.setCaption(" RPN calculator on Qtopia ");
  palmtop.setGeometry(0, 0, 332, 412);
  palmtop.setBackgroundColor(QColor(128,128,128));
  MainWin win(&palmtop, "win", 0);
  app.setMainWidget(&palmtop);
  palmtop.show();
  return app.exec();
}