
-- collect any program arguments
local app_args = {...}

-- create the application's main frame
local appFrame = win.create_app_frame ()


local APP_TITLE      = "Calculator"

-- ids should be between 1 - 65535

-- control ids
local ID_MENUBTN     = 101
local ID_DISPLAY     = 200
local ID_ZERO        = 201
local ID_ONE         = 202
local ID_TWO         = 203
local ID_THREE       = 204
local ID_FOUR        = 205
local ID_FIVE        = 206
local ID_SIX         = 207
local ID_SEVEN       = 208
local ID_EIGHT       = 209
local ID_NINE        = 210
local ID_CLEAR       = 211
local ID_PLUS        = 212
local ID_MINUS       = 213
local ID_MULTIPLY    = 214
local ID_DIVIDE      = 215
local ID_EQUALS      = 216
local ID_DOT         = 217
local ID_BACK        = 218
local ID_ROOT        = 219

-- command ids
local IDM_COPY       = 1001
local IDM_PASTE      = 1002
local IDM_QUITAPP    = 1003


local MODE_OPERAND   = 1
local MODE_OPERATOR  = 2



-- handle control, wanted and custom events
function appFrame:on_event (event, p1, p2, p3, p4, p5, ...)
   -- handle events and return true
   if event == "btn_click" then
      if p1:get_id () == ID_MENUBTN then
         self:on_menu ()
         return true
      end

      if p1:get_id () == ID_ZERO then
         return self:on_command (ID_ZERO)
      end

      if p1:get_id () == ID_ONE then
         return self:on_command (ID_ONE)
      end

      if p1:get_id () == ID_TWO then
         return self:on_command (ID_TWO)
      end

      if p1:get_id () == ID_THREE then
         return self:on_command (ID_THREE)
      end

      if p1:get_id () == ID_FOUR then
         return self:on_command (ID_FOUR)
      end

      if p1:get_id () == ID_FIVE then
         return self:on_command (ID_FIVE)
      end

      if p1:get_id () == ID_SIX then
         return self:on_command (ID_SIX)
      end

      if p1:get_id () == ID_SEVEN then
         return self:on_command (ID_SEVEN)
      end

      if p1:get_id () == ID_EIGHT then
         return self:on_command (ID_EIGHT)
      end

      if p1:get_id () == ID_NINE then
         return self:on_command (ID_NINE)
      end

      if p1:get_id () == ID_CLEAR then
         return self:on_command (ID_CLEAR)
      end

      if p1:get_id () == ID_BACK then
         return self:on_command (ID_BACK)
      end

      if p1:get_id () == ID_PLUS then
         return self:on_command (ID_PLUS)
      end

      if p1:get_id () == ID_MINUS then
         return self:on_command (ID_MINUS)
      end

      if p1:get_id () == ID_MULTIPLY then
         return self:on_command (ID_MULTIPLY)
      end

      if p1:get_id () == ID_DIVIDE then
         return self:on_command (ID_DIVIDE)
      end

      if p1:get_id () == ID_EQUALS then
         return self:on_command (ID_EQUALS)
      end

      if p1:get_id () == ID_ROOT then
         return self:on_command (ID_ROOT)
      end

      if p1:get_id () == ID_DOT then
         return self:on_command (ID_DOT)
      end

   elseif event == "menu_cmd" then
      return self:on_command (p1)
   end

   return false
end


-- if needed, low priority, called about every 1/3 second
-- when nothing happening - but keep it brief
function appFrame:on_idle (idle_count)
   return false
end


-- if needed
function appFrame:on_alarm (alarm_id)
   return false
end


-- if needed
function appFrame:on_timer (timer_id)
   return false
end


-- to reposition control windows, app frames should only
-- move if monitor device changes size/resolution or
-- fullscreen mode changes
function appFrame:on_move ()
   return false
end


-- for clean up, if needed
function appFrame:on_destroy_wnd ()
end


-- to implement accelerator keys. best to use with combo keys
-- to try and avoid char event following to child
function appFrame:on_child_key (wnd, key, ctrl, alt, shift)
   -- base implements tab key navigation
   if win.applicationFrame.on_child_key (self, wnd, key, ctrl, alt, shift) then
      return true
   end

   -- return true if handled so child does not receive key event

   -- accelerators to menu commands
   if ctrl and not alt and not shift then
      if key == keys.KEY_C then
         return self:on_command (IDM_COPY)
      end

      if key == keys.KEY_B then
         return self:on_command (IDM_PASTE)
      end
   end


   if not ctrl and not alt and not shift then
      if key == keys.KEY_DELETE then
         return self:on_command (ID_CLEAR)
      end

      if key == keys.KEY_BACKSPACE then
         return self:on_command (ID_BACK)
      end

      if key == keys.KEY_ENTER then
         return self:on_command (ID_EQUALS)
      end
   end

   return false
end


function appFrame:on_child_char (wnd, char, ascii)
   if win.applicationFrame.on_child_char (self, wnd, char, ascii) then
      return true
   end

   -- return true if handled so child does not receive char event

   if char == "0" then
      return self:on_command (ID_ZERO)
   end

   if char == "1" then
      return self:on_command (ID_ONE)
   end

   if char == "2" then
      return self:on_command (ID_TWO)
   end

   if char == "3" then
      return self:on_command (ID_THREE)
   end

   if char == "4" then
      return self:on_command (ID_FOUR)
   end

   if char == "5" then
      return self:on_command (ID_FIVE)
   end

   if char == "6" then
      return self:on_command (ID_SIX)
   end

   if char == "7" then
      return self:on_command (ID_SEVEN)
   end

   if char == "8" then
      return self:on_command (ID_EIGHT)
   end

   if char == "9" then
      return self:on_command (ID_NINE)
   end

   if char == "." then
      return self:on_command (ID_DOT)
   end

   if char == "+" then
      return self:on_command (ID_PLUS)
   end

   if char == "-" then
      return self:on_command (ID_MINUS)
   end

   if char == "*" then
      return self:on_command (ID_MULTIPLY)
   end

   if char == "/" then
      return self:on_command (ID_DIVIDE)
   end

   if char == "=" then
      return self:on_command (ID_EQUALS)
   end

   if char == "r" or char == "R" then
      return self:on_command (ID_ROOT)
   end

   return false
end


-- called when app becomes and stops being the active app
function appFrame:on_frame_activate (active)
end


-- called when app is quitting
function appFrame:on_quit ()
   -- return true to stop app from quitting

   return false
end


-- method to create and display menu
function appFrame:on_menu ()
   local menu = win.menuWindow:new (self)

   menu:add_string ("Copy  Ctrl+C",   IDM_COPY)
   menu:add_string ("Paste Ctrl+B",   IDM_PASTE)
   menu:add_string ("------------")
   menu:add_string ("Quit App",       IDM_QUITAPP)

   menu:track (0, 1)
end


-- command handler
function appFrame:on_command (cmd_id)
   if cmd_id == IDM_COPY then
      self:on_copy ()
      return true
   end

   if cmd_id == IDM_PASTE then
      self:on_paste ()
      return true
   end

   if cmd_id == ID_ZERO then
      self:on_operand ("0")
      return true
   end

   if cmd_id == ID_ONE then
      self:on_operand ("1")
      return true
   end

   if cmd_id == ID_TWO then
      self:on_operand ("2")
      return true
   end

   if cmd_id == ID_THREE then
      self:on_operand ("3")
      return true
   end

   if cmd_id == ID_FOUR then
      self:on_operand ("4")
      return true
   end

   if cmd_id == ID_FIVE then
      self:on_operand ("5")
      return true
   end

   if cmd_id == ID_SIX then
      self:on_operand ("6")
      return true
   end

   if cmd_id == ID_SEVEN then
      self:on_operand ("7")
      return true
   end

   if cmd_id == ID_EIGHT then
      self:on_operand ("8")
      return true
   end

   if cmd_id == ID_NINE then
      self:on_operand ("9")
      return true
   end

   if cmd_id == ID_DOT then
      self:on_operand (".")
      return true
   end

   if cmd_id == ID_CLEAR then
      self:on_clear ()
      return true
   end

   if cmd_id == ID_BACK then
      self:on_backspace ()
      return true
   end

   if cmd_id == ID_PLUS then
      self:on_operator (ID_PLUS)
      return true
   end

   if cmd_id == ID_MINUS then
      self:on_operator (ID_MINUS)
      return true
   end

   if cmd_id == ID_MULTIPLY then
      self:on_operator (ID_MULTIPLY)
      return true
   end

   if cmd_id == ID_DIVIDE then
      self:on_operator (ID_DIVIDE)
      return true
   end

   if cmd_id == ID_EQUALS then
      self:on_operator (ID_EQUALS)
      return true
   end

   if cmd_id == ID_ROOT then
      self:on_operator (ID_ROOT)
      return true
   end

   if cmd_id == IDM_QUITAPP then
      self:quit_app ()
      return true
   end

   return false
end



function appFrame:on_create ()
   -- add title bar and close btn, close btn will have focus
   -- frame wnd text displays in running app list
   self:dress (APP_TITLE)

   -- create menu button on title bar
   local menuBtn = win.buttonWindow:new (self, ID_MENUBTN, 0, 0, "Menu")
   -- customise to blend into title bar
   menuBtn:set_colors (menuBtn:get_colors ().frame_text,
							  menuBtn:get_colors ().title_back,
                       menuBtn:get_colors ().frame_back)
   -- move on top of title bar text so wont get obscured
   menuBtn:move (nil, nil, nil, nil, win.WND_TOP)

   self.app_mode = MODE_OPERAND
   self.app_operand = 0
   self.app_operator = ID_EQUALS

   -- create controls, usually setting focus to first
   self.app_display = win.labelWindow:new (self, ID_DISPLAY, 1, 2, "                  0")
   self.app_display:set_bg_color (self:get_colors ().wnd_back)

   self.app_seven    = win.buttonWindow:new (self, ID_SEVEN,     1,  4, " 7 ")
   self.app_eight    = win.buttonWindow:new (self, ID_EIGHT,     5,  4, " 8 ")
   self.app_nine     = win.buttonWindow:new (self, ID_NINE,      9,  4, " 9 ")
   self.app_back     = win.buttonWindow:new (self, ID_BACK,     13,  4, " < ")
   self.app_clear    = win.buttonWindow:new (self, ID_CLEAR,    17,  4, " C ")
   self.app_four     = win.buttonWindow:new (self, ID_FOUR,      1,  6, " 4 ")
   self.app_five     = win.buttonWindow:new (self, ID_FIVE,      5,  6, " 5 ")
   self.app_six      = win.buttonWindow:new (self, ID_SIX,       9,  6, " 6 ")
   self.app_plus     = win.buttonWindow:new (self, ID_PLUS,     13,  6, " + ")
   self.app_minus    = win.buttonWindow:new (self, ID_MINUS,    17,  6, " - ")
   self.app_one      = win.buttonWindow:new (self, ID_ONE,       1,  8, " 1 ")
   self.app_two      = win.buttonWindow:new (self, ID_TWO,       5,  8, " 2 ")
   self.app_three    = win.buttonWindow:new (self, ID_THREE,     9,  8, " 3 ")
   self.app_multiply = win.buttonWindow:new (self, ID_MULTIPLY, 13,  8, " * ")
   self.app_divide   = win.buttonWindow:new (self, ID_DIVIDE,   17,  8, " / ")
   self.app_zero     = win.buttonWindow:new (self, ID_ZERO,      1, 10, "   0   ")
   self.app_dot      = win.buttonWindow:new (self, ID_DOT,       9, 10, " . ")
   self.app_root     = win.buttonWindow:new (self, ID_ROOT,     13, 10, " R ")
   self.app_equals   = win.buttonWindow:new (self, ID_EQUALS,   17, 10, " = ")


   -- app is constructed, bring it to top
   self:set_active_top_frame ()

   return true
end



function appFrame:set_display (value)
   local display = tostring (value or "")

   if display:len () < self.app_display.width then
      display = string.rep (" ", self.app_display.width - display:len ())..display
   end

   self.app_display:set_text (display)
end



function appFrame:get_display ()
   return string.trim (self.app_display:get_text (), " ")
end



function appFrame:on_clear ()
   if self.app_mode == MODE_OPERAND then
      self.app_mode = MODE_OPERAND
      self.app_operand = 0
      self.app_operator = ID_EQUALS
   end

   self:set_display ("0")
end



function appFrame:on_backspace ()
   local display = self:get_display ()

   if display:len () > 1 then
      display = display:sub (1, display:len() - 1)..""
   else
      display = "0"
   end

   self:set_display (display)
end



function appFrame:on_operator (operator)
   if self.app_mode == MODE_OPERAND then
      if operator == ID_ROOT then
         self:set_display (math.sqrt (tonumber (self:get_display ()) or 0))
         return
      end

      if self.app_operator == ID_PLUS then
         self.app_operand = self.app_operand + (tonumber (self:get_display ()) or 0)
      end

      if self.app_operator == ID_MINUS then
         self.app_operand = self.app_operand - (tonumber (self:get_display ()) or 0)
      end

      if self.app_operator == ID_MULTIPLY then
         self.app_operand = self.app_operand * (tonumber (self:get_display ()) or 0)
      end

      if self.app_operator == ID_DIVIDE then
         self.app_operand = ((tonumber (self:get_display ()) or 0) == 0 and 0) or
									 (self.app_operand / tonumber (self:get_display ()))
      end

      if self.app_operator == ID_EQUALS then
         self.app_operand = (tonumber (self:get_display ()) or 0)
      end

      self.app_mode = MODE_OPERATOR
      self:set_display (self.app_operand)
   elseif operator == ID_ROOT and self.app_operator == ID_EQUALS then
      self:set_display (math.sqrt (tonumber (self:get_display ()) or 0))
      return
   end

   self.app_operator = operator
end



function appFrame:on_operand (operand)
   if self.app_mode == MODE_OPERATOR then
      if self.app_operator == ID_EQUALS then
         self.app_operand = 0
      end

      self:set_display ("0")
      self.app_mode = MODE_OPERAND
   end

   local display = self:get_display ()

   if operand == "0" or operand == "1" or operand == "2" or operand == "3" or operand == "4" or
      operand == "5" or operand == "6" or operand == "7" or operand == "8" or operand == "9" then

      if display == "0" then
         display = operand
      else
         display = display..operand
      end
   elseif  operand == "." then
      if (display:find (".", 1, true)) == nil then
         display = display..operand
      end
   else
      display = tostring (tonumber (operand) or 0)
   end

   self:set_display (display)
end



function appFrame:on_copy ()
   self:set_clipboard (self:get_display (), win.CB_TEXT)
end



function appFrame:on_paste ()
   local cbType, data = self:get_clipboard ()

   if cbType == win.CB_TEXT then
      self:on_operand (data)
   end
end



-- run application loop after methods are defined
appFrame:run_app ()
