
#include "appwin.h"
#include "ui/label.h"
#include "ui/point.h"
#include "ui/xrbtngrp.h"
#include "ui/toglbtn.h"
#include "ui/containr.h"
#include "ui/pushbtn.h"
#include "ui/xrtglbtn.h"
#include "ui/cntroler.h"

enum {
    ID_CLEAR       = 23,   // View id of  the "Clear" button
    ID_RADIX       = 30,   // View id of the radix selection box
    ID_DIGITS_BASE = 1,    // First view id used by digits buttons
    ID_OPS_BASE    = 100   // First view id used by operations buttons
};

const char* ops[] = {
    "+", "-", "*", "/", "%",
#if defined(__MS_WINDOWS__)
    "&&",
#else
    "&",
#endif        
    "|", ">>", "<<", "=", 0};

const char digits[] = "0123456789ABCDEF";

AppWindow::AppWindow ()
: UI_Dialog (NULL, NULL, UI_Rectangle (50, 50, 400, 300))
{
    Title() = "YACL Calculator";
    _op = '=';
    _top = 0;
    _stack[0] = 0;
    _stack[1] = 0;

    // Create the label
    _label = new UI_Label (this, UI_Rectangle (20, 5, 150, 25));
    _label->Title() = "0";
    _label->ShowBorder ();
    _label->SetTextStyle (UIText_Right);

    // Create the "Clear" button
    UI_PushButton*  btn = new UI_PushButton
        (this, UI_Rectangle (30, 200, 60, 30), ID_CLEAR);
    btn->Title() = "Clear";

    UI_Rectangle result      (40,    5, 150,  25);
    UI_Rectangle digitsPanel (10,   40, 140, 140);
    UI_Rectangle opsPanel    (160,  40, 175,  70);
    UI_Rectangle radixRect   (160, 110, 220, 180);
    
    MakeDigitButtons (digitsPanel);    // Create the digit buttons
    MakeOpButtons    (opsPanel);       // Create the operations buttons
    MakeRadixPanel   (radixRect);      // Create the radix panel
    
    DoRadix (10);                      // Initial radix is decimal
}


void AppWindow::MakeDigitButtons (const UI_Rectangle& panel)
{
    UI_PushButton* btn;
    UI_PointSequence seq = panel.RowMajor (35, 35);
    short n = seq.Size();
    for (short i = 0; i < n; i++) {
        UI_Rectangle shape (seq[i], 25, 25);
        btn = new UI_PushButton (this, shape, i + ID_DIGITS_BASE);
        btn->Title() = CL_String (digits[i], 1);
    }
}


void AppWindow::MakeOpButtons (const UI_Rectangle& panel)
{
    UI_PushButton* btn;
    UI_PointSequence seq = panel.RowMajor (35, 35);
    for (short i = 0; ops[i] != 0; i++) {
        btn = new UI_PushButton (this, UI_Rectangle (seq[i], 25, 25),
             (UI_ViewID) (ID_OPS_BASE + ops[i][0])); // Map id's to operations
        btn->Title() = ops[i];
    }
}


void AppWindow::MakeRadixPanel (const UI_Rectangle& panel)
{
    UI_ExOrButtonGroup* grp = new UI_ExOrButtonGroup (this, panel, ID_RADIX);
    grp->Title() = "Radix";
    UI_ExOrToggleButton* xor_btn;
    UI_PointSequence seq = UI_Rectangle (15, 25, 210, 140).RowMajor (50, 30);
    for (short i = 2; i <= 16; i++) {
        UI_Rectangle shape (seq[i-2], 45, 25);
        xor_btn = new UI_ExOrToggleButton (grp, shape, i + ID_RADIX);
        xor_btn->Title() = CL_String (i);
    }
    grp->Selection() = ID_RADIX + 10; // Decimal
}


bool AppWindow::HandleChildEvent (const UI_Event& e)
{
    UI_Dialog::HandleChildEvent (e);
    if (e.Type() == Event_KeyTyped) {
        short key = e.Key();
        bool b = (key >= '0' && key < _radix + '0') 
          ?  DoDigit (key - '0')
          :  DoOp (key);
//         if (!b)
//             _Controller->Beep();
        return TRUE;
    }
    if (e.Type() != Event_Select)
        return FALSE;
    UI_VisualObject& origin = *(e.Origin());
    UI_ViewID id = origin.ViewID();
    if (id == ID_RADIX) { // User changed the current radix
        short radix = ((UI_ExOrButtonGroup*) (*this)[ID_RADIX])
            ->Selection() - ID_RADIX;
        DoRadix (radix);
    }
    else if (id == ID_CLEAR) { // User clicked the Clear button
        DoClear();
        return TRUE;
    }
    else if (id >= ID_DIGITS_BASE && id < ID_DIGITS_BASE + 16)
        // User clicked on a digit button
        DoDigit (id - ID_DIGITS_BASE);
    else  // User clicked on an operation button
        DoOp (id - ID_OPS_BASE);
    return TRUE;
}


void AppWindow::DoRadix (short radix)
{
    _radix = radix;
    _label->Title() = CL_Integer(_stack[_top]).InRadix (_radix);
    // Enable the appropriate buttons
    short i;
    for (i = 0; i < _radix; i++)
        (*this)[i+ID_DIGITS_BASE]->Enable();
    for (i = _radix; i < 16; i++)
        (*this)[i+ID_DIGITS_BASE]->Disable();
}


void AppWindow::DoClear ()
{
    _top = 0;
    _stack[0] = 0;
    _label->Title() = "0";
}

bool AppWindow::DoDigit (short digit)
{
    _stack[_top] = _stack[_top] * _radix + digit;
    _label->Title() = CL_Integer(_stack[_top]).InRadix (_radix);
    return TRUE;
}


bool AppWindow::DoOp (char op)
{
    if (op != '+' && op != '-' && op != '*' && op != '/' && op != '%'
        && op != '<' && op != '>' && op != '&' && op != '|' && op != '=')
        return FALSE;
    if (op != '=' && _top == 0) {
        _top = 1;
        _op = op;
        _stack[_top] = 0;
        _label->Title() = "0";
        return TRUE;
    }
    switch (_op) {
    case '+': _stack[_top-1] += _stack[_top]; break;
        
    case '-': _stack[_top-1] -= _stack[_top]; break;
        
    case '*': _stack[_top-1] *= _stack[_top]; break;
        
    case '/': _stack[_top-1] /= _stack[_top]; break;
        
    case '%': _stack[_top-1] %= _stack[_top]; break;
        
    case '&': _stack[_top-1] &= _stack[_top]; break;
        
    case '|': _stack[_top-1] |= _stack[_top]; break;
        
    case '>': _stack[_top-1] >>= _stack[_top]; break;
        
    case '<': _stack[_top-1] <<= _stack[_top]; break;
        
    case '=': if (_top == 1)
                  _stack[_top-1] = _stack[_top--];
              break;

    default: return FALSE;
    };

    if (op == '=') {
        _top = 0;
    }
    else {
        _top = 1;
        _stack[_top] = 0;
    }
    _op = op;
    _label->Title() = CL_Integer (_stack[0]).InRadix (_radix);
    return TRUE;
}
