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

local APP_TITLE         = "admin"

local ID_OK             = 1

local ID_MENUBTN        = 101
local ID_DOMAIN         = 102
local ID_NETWORK        = 103
local ID_PORT           = 104
local ID_TIMEOUT        = 105
local ID_WIRELESS       = 106
local ID_PASSWORD       = 107
local ID_PROGRESS       = 108
local ID_ACCOUNT        = 109
local ID_SRCPATH        = 110
local ID_SRCBROWSE      = 111
local ID_DESTPATH       = 112
local ID_LISTING        = 113

local IDM_CONNECTION    = 1001
local IDM_UP            = 1002
local IDM_OPEN          = 1003
local IDM_NEWDIR        = 1004
local IDM_UPLOAD        = 1005
local IDM_DOWNLOAD      = 1006
local IDM_DELETE        = 1007
local IDM_NEWACC        = 1008
local IDM_RESETACC      = 1009
local IDM_DELACC        = 1010
local IDM_QUITAPP       = 1011

local TYPE_UP     = 0
local TYPE_DIR    = 1
local TYPE_FILE   = 2



local function path_folder (path)
	path = tostring (path or "")

	if path:len () > 1 then
		if path:sub (-1, -1) == "/" then
			path = path:sub (1, -2)
		end

		local last_name = fs.path_name (path)
		path = path:sub (1, -(last_name:len () + 1))
	else
		path = "/"
	end

	return path
end



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

function connectDlg:on_create (domain, password, network, timeout)
	self:dress ("Connection")

	self.con_domain =
		win.inputWindow:new (self, ID_DOMAIN, 1, 2, 17,
									tostring (domain or ""), "Domain")
	self.con_password =
		win.inputWindow:new (self, ID_PASSWORD, 1, 4, 17,
									tostring (password or ""), "Password")
	self.con_password:set_mask_char ("*")
	self.con_network =
		win.inputWindow:new (self, ID_NETWORK, 1, 6, 17,
									tostring (network or "wide_area_network"), "Network")
	self.con_timeout =
		win.inputWindow:new (self, ID_TIMEOUT, 1, 8, 7,
								  tostring (timeout or "5"), "Timeout")
	win.buttonWindow:new (self, ID_OK, 14, 10, " Ok ")
	self.con_domain:set_focus ()

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

	return true
end


function connectDlg:on_event (event, p1, p2, p3, p4, p5, ...)
	if event == "btn_click" then
		if p1:get_id () == ID_OK then
			if self.con_domain:get_text ():len () < 1 then
				self.con_domain:set_focus ()
				self.con_domain:set_error (true)
			elseif self.con_password:get_text ():len () < 1 then
				self.con_password:set_focus ()
				self.con_password:set_error (true)
			else
				self:close (ID_OK)
			end

			return true
		end
	end

	return false
end
-- end connectDlg class ------------------------------------------------

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

function accountDlg:on_create (title)
	self:dress (title)

	self.acc_account = win.inputWindow:new (self, ID_ACCOUNT, 1, 2, 17,
														 nil, "Account")
	self.acc_password = win.inputWindow:new (self, ID_PASSWORD, 1, 4, 17,
														  nil, "Password")
	win.buttonWindow:new (self, ID_OK, 14, 6, " Ok ")
	self.acc_password:set_mask_char ("*")
	self.acc_account:set_focus ()

	self:move (nil, nil, 19, 8)

	return true
end


function accountDlg:on_event (event, p1, p2, p3, p4, p5, ...)
	if event == "btn_click" then
		if p1:get_id () == ID_OK then
			if self.acc_account:get_text ():len () < 1 then
				self.acc_account:set_focus ()
			elseif self.acc_password:get_text ():len () < 1 then
				self.acc_password:set_focus ()
			else
				self:close (ID_OK)
			end

			return true
		end
	end

	return false
end
-- end accountDlg class ------------------------------------------------



function appFrame:on_receive (msg)
	if (msg.recipient_id or msg.recipient_name) and type (msg.data) == "table" then
		if msg.context == "listing_response" and type (msg.data.files) == "table" and
			type( msg.data.folders) == "table" and msg.data.path then

			self.app_list:set_cur_sel (0)
			self.app_list:reset_content ()
			self.app_cur_path = msg.data.path

			if msg.data.path ~= "/" then
				self.app_list:add_string ("..", { name = "", type = TYPE_UP })
			end

			table.sort (msg.data.folders)
			table.sort (msg.data.files)

			for i = 1, #msg.data.folders, 1 do
				self.app_list:add_string ("/"..msg.data.folders[i],
												  { name = msg.data.folders[i], type = TYPE_DIR })
			end

			for i = 1, #msg.data.files, 1 do
				self.app_list:add_string (msg.data.files[i],
												  { name = msg.data.files[i], type = TYPE_FILE })
			end

			self:update_title ()
			self:start_timer (0.1)

			return true
		elseif msg.context == "ftp_response" and
					msg.data.path and msg.data.content then
			local path = cmndlg.save_file (self, fs.path_name (msg.data.path))

			if path then
				self.app_last_file = path

				local file = io.open (path, "w")
				if file then
					file:write (msg.data.content)
					file:close ()
				else
					self:msgbox ("File Error", "Could not write file "..path, term.colors.red)
				end
			end

			self:start_timer (0.1)

			return true
		end
	end

	return false
end


function appFrame:on_sent (msg, success)
	if success then
		if msg.context == "file_delete" or msg.context == "file_upload" or
				msg.context == "directory_create" then
			self:close_connection ()
			self:on_file_list ()
		elseif msg.context == "create_account" then
			self:close_connection ()
			self:msgbox ("Success", "Account created.")
		elseif msg.context == "delete_account" then
			self:close_connection ()
			self:msgbox ("Success", "Account deleted.")
		elseif msg.context == "reset_password" then
			self:close_connection ()
			self:msgbox ("Success", "Account reset.")
		end
	else
		self:close_connection ()
		self:msgbox ("Fail", "Action failed!", term.colors.red)
	end

	return true
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
		end

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

	elseif event == "list_double_click" then
		if p1:get_id () == ID_LISTING then
			self:do_default_action ()

			return true
		end
	end

	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

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

		if key == keys.KEY_UP then
			return self:on_command (IDM_UP)
		end

		if key == keys.KEY_O then
			return self:on_command (IDM_OPEN)
		end

		if key == keys.KEY_N then
			return self:on_command (IDM_NEWDIR)
		end

		if key == keys.KEY_U then
			return self:on_command (IDM_UPLOAD)
		end

		if key == keys.KEY_D then
			return self:on_command (IDM_DOWNLOAD)
		end

		if key == keys.KEY_A then
			return self:on_command (IDM_NEWACC)
		end

		if key == keys.KEY_R then
			return self:on_command (IDM_RESETACC)
		end

		if key == keys.KEY_K then
			return self:on_command (IDM_DELACC)
		end
	elseif not ctrl and not alt and not shift then
		if key == keys.KEY_DELETE then
			return self:on_command (IDM_DELETE)
		end
	end

	return false
end


function appFrame:on_frame_activate (active)
end


function appFrame:on_timer (id)
	self:close_connection ()

	return false
end


function appFrame:on_quit ()
	self:close_connection ()

	return false
end


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

	x = x or 0
	y = y or 1

	menu:add_string ("Connection  Ctrl+C", IDM_CONNECTION)
	if self:can_connect () then
		menu:add_string ("------------------")
		if self:can_up () then
			menu:add_string ("Up          Ctrl+^", IDM_UP)
		end
		if self:can_open () then
			menu:add_string ("Open        Ctrl+O", IDM_OPEN)
		end
		menu:add_string ("New Folder  Ctrl+N", IDM_NEWDIR)
		menu:add_string ("Upload      Ctrl+U", IDM_UPLOAD)
		if self:can_download_file () then
			menu:add_string ("Download    Ctrl+D", IDM_DOWNLOAD)
		end
		if self:can_delete_item () then
			menu:add_string ("Delete         Del", IDM_DELETE)
		end
		menu:add_string ("------------------")
		menu:add_string ("New Account Ctrl+A", IDM_NEWACC)
		menu:add_string ("Reset Acc   Ctrl+R", IDM_RESETACC)
		menu:add_string ("Delete Acc  Ctrl+K", IDM_DELACC)
	end
	menu:add_string ("------------------")
	menu:add_string ("Quit App",             IDM_QUITAPP)

	menu:track (x, y)
end


function appFrame:on_command (cmdId)
	if cmdId == IDM_CONNECTION then
		self:on_connection ()
		return true
	end

	if cmdId == IDM_UP then
		self:on_up ()
		return true
	end

	if cmdId == IDM_OPEN then
		self:on_open ()
		return true
	end

	if cmdId == IDM_NEWDIR then
		self:on_new_folder ()
		return true
	end

	if cmdId == IDM_UPLOAD then
		self:on_upload_file ()
		return true
	end

	if cmdId == IDM_DOWNLOAD then
		self:on_download_file ()
		return true
	end

	if cmdId == IDM_DELETE then
		self:on_delete_item ()
		return true
	end

	if cmdId == IDM_NEWACC then
		self:on_create_account ()
		return true
	end

	if cmdId == IDM_RESETACC then
		self:on_reset_account ()
		return true
	end

	if cmdId == IDM_DELACC then
		self:on_delete_account ()
		return true
	end

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

	return false
end


function appFrame:on_move ()
	self.app_list:move (1, 1, self.width - 2, self.height - 2)
	self.app_progress:move (1, self.height - 1)

	return false
end


function appFrame:on_child_right_click (child, x, y, count)
	if child == self.app_list then
		self.app_list:set_focus ()
		self:on_menu (self:screen_to_wnd (x, y))

		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)

	self.app_domain = ""
	self.app_password = ""
	self.app_network = "wide_area_network"
	self.app_timeout = 5

	self.app_cur_path = "/"
	self.app_last_file = nil
	self.app_app_id = "sadmin"..tostring (math.random (1, 65535))

	self.app_list = win.listWindow:new (self, ID_LISTING, 1, 1,
													self.width - 2, self.height - 2)
	self.app_list:set_colors (self:get_colors ().wnd_text,
									  self:get_colors ().wnd_back,
									  self:get_colors ().wnd_back,
									  self:get_colors ().selected_text,
									  self:get_colors ().selected_back)
	self.app_list:set_focus ()

	self.app_progress =
		win.labelWindow:new (self, ID_PROGRESS, 1, self.height - 1, "processing ...")
	self.app_progress:show (false)

	if #app_args > 0 then
		local ini = fs.load_ini (app_args[1])
		if ini then
			self.app_network = tostring (ini:find("network") or "wide_area_network")
			self.app_timeout = tonumber (ini:find ("timeout")) or 5
			self.app_domain = tostring (ini:find ("domain") or "")
			self.app_password = tostring (ini:find ("password") or "")
		end
	end

	self.app_conn_name = nil

	self:set_active_top_frame ()

	if self:can_connect () then
		self:on_file_list ()
	end

	return true
end


function appFrame:update_title ()
	if self:can_connect () then
		self:set_title (string.trim_right (self.app_domain..self.app_cur_path, "/")..":"..APP_TITLE)
	else
		self:set_title (APP_TITLE)
	end
end


function appFrame:get_selected_path ()
	if self.app_list:get_data () then
		if self.app_list:get_data ().type ~= TYPE_UP then
			return (self.app_cur_path..self.app_list:get_data ().name)
		end
	end

	return nil
end


function appFrame:get_network ()
	return (self.app_network:len () < 1 and "wide_area_network") or self.app_network
end


function appFrame:can_connect ()
	return self.app_domain:len () > 0 and self.app_password:len () > 0
end


function appFrame:open_connection ()
	if self.app_conn_name then
		self:msgbox ("Connection", "Action in progress!", term.colors.red)
		return false
	end

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

	self.app_conn_name = con:get_name ()
	self:want_messages (self:get_network (), self.app_conn_name)
	self:want_messages (self.app_app_id, self.app_conn_name)
	self.app_progress:show (true)

	return true
end


function appFrame:close_connection ()
	if self.app_conn_name then
		self:comm_close (self.app_conn_name)
		self.app_conn_name = nil
		self.app_progress:show (false)
	end
end


function appFrame:on_connection ()
	local dlg = connectDlg:new (self)

	if dlg:do_modal (self.app_domain, self.app_password,
						  self:get_network (), self.app_timeout) == ID_OK then
		self.app_domain = dlg.con_domain:get_text ()
		self.app_password = dlg.con_password:get_text ()
		self.app_network = dlg.con_network:get_text ()
		self.app_timeout = tonumber (dlg.con_timeout:get_text ()) or 5

		self.app_cur_path = "/"
		self:on_file_list ()
	end
end


function appFrame:on_file_list ()
	if self:open_connection () then
		self:send_message (self.app_domain, self:get_network (),
								 "listing_request",
								 {
									 password = self.app_password,
									 path = self.app_cur_path,
									 application = self.app_app_id
								 }, self.app_conn_name)
	end
end


function appFrame:can_download_file ()
	return self.app_list:get_data () and
			 self.app_list:get_data ().type == TYPE_FILE
end


function appFrame:on_download_file ()
	if self:can_download_file () then
		if self:open_connection () then
			self:send_message (self.app_domain, self:get_network (),
									 "ftp_request",
									 {
										 path = self:get_selected_path (),
										 application = self.app_app_id
									 }, self.app_conn_name)
		end
	end
end


function appFrame:on_upload_file ()
	if self:can_connect () then
		local path = cmndlg.open_file (self, self.app_last_file)

		if path then
			local name = fs.path_name (path)
			self.app_last_file = path

			for i = 1, self.app_list:count(), 1 do
				if self.app_list:get_data (i) then
					if self.app_list:get_data (i).name == name then
						if cmndlg.confirm (self, "Overwrite",
												 "Overwrite "..name.."?",
												 false, term.colors.orange) then
							break
						else
							return
						end
					end
				end
			end

			local file = io.open (path, "r")
			if not file then
				self:msgbox ("Error", "Could not read source "..path, term.colors.red)
				return
			end

			local content = file:read ("*a")
			file:close ()

			if self:open_connection () then
				self:send_message (self.app_domain, self:get_network (),
										 "file_upload",
										 {
											 password = self.app_password,
											 path = self.app_cur_path..name,
											 content = content
										 }, self.app_conn_name)
			end
		end
	end
end


function appFrame:can_delete_item ()
	return self.app_list:get_data () and
			 (self.app_list:get_data ().type == TYPE_FILE or
			 self.app_list:get_data ().type == TYPE_DIR)
end


function appFrame:on_delete_item ()
	if self:can_delete_item () then
		local title, message;

		if self.app_list:get_data ().type == TYPE_FILE then
			title = "Delete File"
			message = "Delete file "..self:get_selected_path ()
		elseif self.app_list:get_data ().type == TYPE_DIR then
			title = "Delete Folder"
			message = "Delete folder "..self:get_selected_path ()..
																" and all its contents"
		end

		if cmndlg.confirm (self, title, message, false, term.colors.orange) then
			if self:open_connection () then
				self:send_message (self.app_domain, self:get_network (),
										 "file_delete",
										 {
											 password = self.app_password,
											 path = self:get_selected_path ()
										 }, self.app_conn_name)
			end
		end
	end
end


local function validate_new_folder (name)
	return (not name:find ("/", 1)) and
			 (not name:find (" ", 1))
end


function appFrame:on_new_folder ()
	if self:can_connect () then
		local folder = cmndlg.input (self, "New Folder", "Enter new folder name.",
											  "new", "Name", nil, validate_new_folder)
		if folder then
			if self:open_connection () then
				self:send_message (self.app_domain, self:get_network (),
										 "directory_create",
										 {
											 password = self.app_password,
											 path = self.app_cur_path..folder,
										 }, self.app_conn_name)
			end
		end
	end
end


function appFrame:can_up ()
	return self:can_connect () and
			 self.app_cur_path:len () > 1
end


function appFrame:should_up ()
	return self:can_up () and
			 self.app_list:get_data () and
			 self.app_list:get_data ().type == TYPE_UP
end


function appFrame:on_up ()
	if self:can_up () then
		self.app_cur_path = path_folder (self.app_cur_path)
		self:on_file_list ()
	end
end


function appFrame:can_open ()
	return self.app_list:get_data () and
			self.app_list:get_data ().type == TYPE_DIR
end


function appFrame:on_open ()
	if self:can_open () then
		self.app_cur_path = self:get_selected_path ().."/"
		self:on_file_list ()
	end
end


function appFrame:do_default_action ()
	if self:should_up () then
		self:on_up ()
	elseif self:can_open () then
		self:on_open ()
	elseif self:can_download_file () then
		self:on_download_file ()
	end
end


function appFrame:on_create_account ()
	if self:can_connect () then
		local dlg = accountDlg:new (self)

		if dlg:do_modal ("New Account") == ID_OK then
			if self:open_connection () then
				self:send_message (self.app_domain, self:get_network (),
										 "create_account",
										 {
											 password = self.app_password,
											 account = dlg.acc_account:get_text (),
											 client_password = dlg.acc_password:get_text ()
										 }, self.app_conn_name)
			end
		end
	end
end


function appFrame:on_delete_account ()
	if self:can_connect () then
		local account = cmndlg.input (self, "Delete Account",
												"Account to delete.", nil, "Account")
		if account then
			if self:open_connection () then
				self:send_message (self.app_domain, self:get_network (),
										 "delete_account",
										 {
											 password = self.app_password,
											 account = account
										 }, self.app_conn_name)
			end
		end
	end
end


function appFrame:on_reset_account ()
	if self:can_connect () then
		local dlg = accountDlg:new (self)

		if dlg:do_modal ("Reset Account") == ID_OK then
			if self:open_connection () then
				self:send_message (self.app_domain, self:get_network (),
										 "reset_password",
										 {
											 password = self.app_password,
											 account = dlg.acc_account:get_text (),
											 client_password = dlg.acc_password:get_text ()
										 }, self.app_conn_name)
			end
		end
	end
end


appFrame:run_app ()
