
local app_args = {...}
local appFrame = win.create_app_frame ()


local APP_TITLE         = "eShop"

local ID_MENUBTN        = 101
local ID_TALLY          = 102
local ID_ITEMS          = 103
local ID_MORE           = 104
local ID_LESS           = 105
local ID_NAME           = 106
local ID_ADDRESS        = 107
local ID_ACCOUNT        = 108
local ID_PIN            = 109
local ID_OK             = 110

local IDM_MORE          = 1001
local IDM_LESS          = 1002
local IDM_SEND          = 1003
local IDM_QUITAPP       = 1004
local IDM_CLEAR         = 1005

local STORE_APP_ID		= "estore_app_id"


local ALIGN_LEFT     = 0
local ALIGN_RIGHT    = 1
local ALIGN_CENTER   = 2



local function str_field (field, len, precision, align, fill)
	local str = tostring (field or "")
	fill = fill or " "
	align = align or ALIGN_LEFT
	len = tonumber (len) or 0

	if type (field) == "number" then
		if precision and precision >= 0 then
			precision = tonumber (precision)

			if precision > 0 then
				local whole, part = math.modf (field)

				part = math.modf ((part * math.pow (10, precision)) + 0.5)

				str = string.format ("%01d.%0"..tostring (precision).."d", whole, part)
			else
				str = string.format ("%01d", (math.modf (field + 0.5)))
			end
		end
	end

	if len > 0 then
		if string.len (str) > len then
			if align == ALIGN_RIGHT then
				local s = string.sub (str, string.len (str) - len)
				str = s..""
			else
				local s = string.sub (str, 1, len)
				str = s..""
			end
		elseif string.len (str) < len then
			if align == ALIGN_RIGHT then
				local s = string.rep (fill or " ", len - string.len (str))..str
				str = s..""
			elseif align == ALIGN_CENTER then
				local pre = math.modf ((len - string.len (str)) / 2)

				if pre > 0 then
					local s = string.rep (fill or " ", pre)..str..string.rep (fill or " ", len - string.len (str) - pre)
					str = s..""
				else
					local s = str..fill or " "
					str = s..""
				end
			else
				local s = str..string.rep (fill or " ", len - string.len (str))
				str = s..""
			end
		end
	end

	return str
end



local function item_line (item, price, qty, width)
	return str_field (item, width - 10, nil, ALIGN_LEFT)..
				str_field (price, 5, 2, ALIGN_RIGHT)..
				str_field (qty, 5, 0, ALIGN_RIGHT)
end



-- sendDlg class -------------------------------------------------------
local sendDlg = win.popupFrame:base ()



function sendDlg:on_create (name, address, account)
	self:dress ("Send Order")

	self.dlg_name = win.inputWindow:new (self, ID_NAME, 1, 2, 20, name, "Name")
	self.dlg_address = win.inputWindow:new (self, ID_ADDRESS, 1, 4, 20, address, "Address")
	self.dlg_account = win.inputWindow:new (self, ID_ACCOUNT, 1, 6, 20, account, "Account")
	self.dlg_pin = win.inputWindow:new (self, ID_PIN, 1, 8, 20, "", "Pin")

	self.dlg_pin:set_mask_char ("*")
	if string.len (tostring (name or "")) > 0 then
		self.dlg_pin:set_focus ()
	else
		self.dlg_name:set_sel (0, -1)
		self.dlg_name:set_focus ()
	end

	win.buttonWindow:new (self, ID_OK, 17, 10, " Ok ")

	self:move (nil, nil, 22, 12)

	return true
end



function sendDlg:on_event (event, p1, p2, p3, p4, p5, ...)
	if event == "btn_click" then
		if p1:get_id () == ID_OK then
			if string.len (self.dlg_name:get_text ()) < 1 then
				self.dlg_name:set_focus ()
				self.dlg_name:set_sel (0, -1, false)
				self.dlg_name:set_error (true)
				return true
			end

			if string.len (self.dlg_address:get_text ()) < 1 then
				self.dlg_address:set_focus ()
				self.dlg_address:set_sel (0, -1, false)
				self.dlg_address:set_error (true)
				return true
			end

			if string.len (self.dlg_account:get_text ()) < 1 then
				self.dlg_account:set_focus ()
				self.dlg_account:set_sel (0, -1, false)
				self.dlg_account:set_error (true)
				return true
			end

			if string.len (self.dlg_pin:get_text ()) < 1 then
				self.dlg_pin:set_focus ()
				self.dlg_pin:set_sel (0, -1, false)
				self.dlg_pin:set_error (true)
				return true
			end

			self:close (ID_OK)
			return true
		end
	end

	return false
end



function sendDlg:on_child_focus (child, blurred)
	if win.popupFrame.on_child_focus (self, child, blurred) then
		return true
	end

	if child.set_sel then
		child:set_sel (0, -1, false)
	end
end



function sendDlg:on_child_blur (child, focused)
	if win.popupFrame.on_child_blur (self, child, focused) then
		return true
	end

	if child.set_sel then
		child:set_sel (0, 0, false)
		child:set_error (false)
	end
end
-- end sendDlg class ---------------------------------------------------



function appFrame:on_receive (msg)
	if msg.application == self.app_app_id and type (msg.data) == "table" then
		if msg.context == "estore_catalog" then
			self:load_catalog (msg.data)
			return true
		elseif msg.context == "estore_ordered" then
			self:msgbox ("Success",
							 "Order "..msg.data.order..
								" successful for "..str_field (msg.data.total, nil, 2))
			return true
		elseif msg.context == "estore_declined" then
			self:msgbox ("Declined", "Order declined.\n"..msg.data.message, term.colors.red)
			return true
		end
	end

	return false
end



function appFrame:on_sent (msg, success)
	if not success then
		self:msgbox ("Error", "Failed to send "..msg.context, term.colors.red)
	end
end



function appFrame:on_event (event, p1, p2, p3, p4, p5, ...)
	if event == "btn_click" then
		if p1:get_id () == ID_MENUBTN then
			self:on_menu ()
			return true
		elseif p1:get_id () == ID_MORE then
			self:on_more ()
			return true
		elseif p1:get_id () == ID_LESS then
			self:on_less ()
			return true
		end

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

	return false
end



function appFrame:on_move ()
	local label = string.format ("Items%sPrice  Qty", string.rep (" ", self.width - 18))
	self.app_columns:set_text (label)
	self.app_columns:move (nil, nil, string.len (label))

	self.app_items:move (1, 3, self.width - 2, self.height - 5)
	self.app_total:move (nil, self.height - 2)
	self.app_tally:move (nil, self.height - 2)
	self:display_items ()

	return false
end



function appFrame:on_child_key (wnd, key, ctrl, alt, shift)
	if win.applicationFrame.on_child_key (self, wnd, key, ctrl, alt, shift) then
		return true
	end

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

		if key == keys.KEY_L then
			return self:on_command (IDM_LESS)
		end
	elseif ctrl and not alt and not shift then
		if key == keys.KEY_S then
			return self:on_command (IDM_SEND)
		end

		if key == keys.KEY_C then
			return self:on_command (IDM_CLEAR)
		end
	end

	return false
end



function appFrame:on_menu ()
	local menu = win.menuWindow:new (self)

	menu:add_string ("More       M",   IDM_MORE)
	menu:add_string ("Less       L",   IDM_LESS)
	if self.app_amount > 0 then
		menu:add_string ("------------")
		menu:add_string ("Send  Ctrl+S",   IDM_SEND)
	end
	menu:add_string ("------------")
	menu:add_string ("Clear Ctrl+C",   IDM_CLEAR)
	menu:add_string ("------------")
	menu:add_string ("Quit App",       IDM_QUITAPP)

	menu:track (0, 1)
end



function appFrame:on_command (cmd_id)
	if cmd_id == IDM_MORE then
		self:on_more ()
		return true
	end

	if cmd_id == IDM_LESS then
		self:on_less ()
		return true
	end

	if cmd_id == IDM_SEND then
		self:on_send ()
		return true
	end

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

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

	return false
end



function appFrame:on_create ()
	self:dress (APP_TITLE)

	local menuBtn = win.buttonWindow:new (self, ID_MENUBTN, 0, 0, "Menu")
	menuBtn:set_colors (menuBtn:get_colors ().frame_text,
							  menuBtn:get_colors ().title_back,
							  menuBtn:get_colors ().frame_back)
	menuBtn:move (nil, nil, nil, nil, win.WND_TOP)

	win.buttonWindow:new (self, ID_MORE, 1, 1, " More ")
	win.buttonWindow:new (self, ID_LESS, 8, 1, " Less ")
	self.app_message = win.labelWindow:new (self, 0, 15, 1, "               ")

	self.app_columns = win.labelWindow:new (self, 0, 1, 2,
		str_field ("Item", self.width - 13, nil, ALIGN_LEFT)..
		str_field ("Price", 5, 2, ALIGN_RIGHT)..
		str_field ("Qty", 5, 0, ALIGN_RIGHT))

	self.app_items = win.listWindow:new (self, ID_ITEMS, 1, 3, self.width - 2, self.height - 5)
	self.app_total = win.labelWindow:new (self, 0, 1, self.height - 2, "Total:")
	self.app_tally = win.labelWindow:new (self, ID_TALLY, 8, self.height - 2, "      0.00")
	self.app_amount = 0
	self.app_catalog_loaded = false

	self.app_items:set_focus ()

	if not self:load_init () then
		return false
	end

	local con = self:comm_open (nil, 5)
	if not con then
		self:msgbox ("Connection", "Could not connect to modem!", term.colors.red)
		return false
	end

	self.app_connection_name = con:get_name ()
	self.app_app_id = APP_TITLE..tostring (math.random (1, 65535))
	self:want_messages (self.app_app_id, self.app_connection_name)

	self:get_catalog ()

	self:set_active_top_frame ()

	return true
end



function appFrame:load_init ()
	local ini = fs.load_ini (self:get_app_path ()..".ini")
	if ini then
		self.app_retailer = ini:find ("retailer")
		self.app_estore = ini:find ("estorename")
		self.app_account = ini:find ("account")
		self.app_name = ini:find ("name")
		self.app_address = ini:find ("address")
	end

	if not self.app_estore then
		self:msgbox ("eStore", "No estore set in init file!", term.colors.red)
		return false
	end

	if self.app_retailer then
		self:set_title (APP_TITLE..":"..self.app_retailer)
	end

	return true
end



function appFrame:get_catalog ()
	self.app_catalog_loaded = false
	self.app_message:set_text ("Loading...")
	self:send_message (self.app_estore, STORE_APP_ID, "eshop_catalog",
							 {
								 application = self.app_app_id
							 },
							 self.app_connection_name)
end



function appFrame:load_catalog (catalog)
	if not self.app_catalog_loaded then
		self.app_catalog_loaded = true
		self.app_items:reset_content ()

		for i = 1, #catalog, 1 do
			self.app_items:add_string (item_line (catalog[i].item, catalog[i].price, 0, self.app_items.width - 1),
												{ item = catalog[i].item, price = catalog[i].price, qty = 0 })
		end

		self.app_message:set_text ("")
	end
end



function appFrame:display_items ()
	local i = self.app_items:get_cur_sel ()

	for i = 1, self.app_items:count (), 1 do
		local data = self.app_items:get_data (i)
		self.app_items:set_string (item_line (data.item, data.price, data.qty, self.app_items.width - 1) ,i)
	end
end



function appFrame:on_more ()
	local i = self.app_items:get_cur_sel ()

	if i > 0 then
		local data = self.app_items:get_data (i)

		data.qty = data.qty + 1
		self.app_items:set_data (data, i)
		self.app_items:set_string (item_line (data.item, data.price, data.qty, self.app_items.width - 1) ,i)

		self.app_amount = self.app_amount + data.price
		self.app_tally:set_text (str_field (self.app_amount, 10, 2, ALIGN_RIGHT))
	end
end



function appFrame:on_less ()
	local i = self.app_items:get_cur_sel ()

	if i > 0 then
		local data = self.app_items:get_data (i)

		if data.qty > 0 then
			data.qty = data.qty - 1
			self.app_items:set_data (data, i)
			self.app_items:set_string (item_line (data.item, data.price, data.qty, self.app_items.width - 1) ,i)

			self.app_amount = self.app_amount - data.price
			self.app_tally:set_text (str_field (self.app_amount, 10, 2, ALIGN_RIGHT))
		end
	end
end



function appFrame:on_send ()
	if self.app_amount > 0 then
		local dlg = sendDlg:new (self)

		if dlg:do_modal (self.app_name, self.app_address, self.app_account) == ID_OK then
			local data =
			{
				application = self.app_app_id,
				order =
				{
					name = dlg.dlg_name:get_text (),
					address = dlg.dlg_address:get_text (),
					account = dlg.dlg_account:get_text (),
					pin = dlg.dlg_pin:get_text (),
					items = { }
				}
			}

			for i = 1, self.app_items:count(), 1 do
				local item = self.app_items:get_data (i)

				if item.qty > 0 then
					data.order.items[#data.order.items + 1] = item
				end
			end

			self:send_message (self.app_estore, STORE_APP_ID, "eshop_order", data, self.app_connection_name)
		end
	end
end



function appFrame:on_clear ()
	self.app_items:reset_content ()
	self:get_catalog ()
	self.app_amount = 0
	self.app_tally:set_text (str_field (self.app_amount, 10, 2, ALIGN_RIGHT))
end



appFrame:run_app ()
