--------------------------------------------------------------------------
-- YD编辑器的主配置和代码文件
--------------------------------------------------------------------------

-- 标志魔兽是否是1.24以及以后版本（有hashtable，无return bug）
new_warcraft = false

-- YDWE的菜单句柄
ydwe_menu_handle = nil

-- 本函数在编辑器启动时调用，可以在本函数中载入一些插件
function on_startup()
	log.debug("********************* on startup start *********************")
	
	-- 检测UI和Units目录
	check_conflicting_ui()
	
	-- 检查魔兽目录下是否有可能引起冲突的文件夹
	clear_potential_conflicting()
	
	-- 获取game.dll的路径
	local game_dll_path = sys.get_warcraft_dir() / "game.dll"
	
	-- 检测魔兽的版本
	local version_info = sys.get_module_version_info(game_dll_path)
	if version_info["FileVersionMS"] >= "1.24" then
		new_warcraft = true
		log.trace(string.format("New version warcraft %s detected", version_info["FileVersion"]))
	else
		log.trace(string.format("Old version warcraft %s detected", version_info["FileVersion"]))
	end
	
	-- 尝试载入TESH
	load_normal_shared_object(sys.get_editor_root_dir() / "tesh" / "TESH.dll")
	
	log.debug("********************* on startup end *********************")
end

-- 本函数在编辑器关闭时调用，可以在本函数中作一些清理工作
function on_exit()
	log.debug("********************* on exit start *********************")
	log.debug("********************* on exit end *********************")
end

-- 当载入主游戏文件(game.dll)时调用(对于WE，永远也不会调用)
function on_gameload()
end

-- 当保存地图时调用
-- 返回布尔值表示是否编译成功
function on_save()
	log.debug("********************* on save start *********************")
	
	-- 刷新配置数据
	renew_config()
	
	-- 获取当前保存的地图名
	local map_path = sys.get_trigger_map()
	local map_path_aux = nil
	if (new_warcraft) then
		map_path_aux = generate_aux_map_path_of_path(map_path, "returnbug")
	else
		map_path_aux = generate_aux_map_path_of_path(map_path, "hashtable")
	end

	log.debug("Saving " .. map_path:file_string())
	
	-- 是否需要插入代码
	local inject_basic = global_config:get_integer("CodeInjection.Basic", 1)
	local inject_script_system = global_config:get_integer("CodeInjection.ScriptSystem", 0)
	
	-- 获取保存类型
	local save_type = global_config:get_integer("SaveOption.Option", 0)
	
	log.trace(string.format("CodeInjection.Basic = %d, CodeInjection.ScriptSystem = %d, SaveOption.Option = %d", inject_basic, inject_script_system, save_type))
	
	-- 获取jasshelper目录
	local jasshelper_dir = sys.get_editor_root_dir() / "jasshelper"
	
	-- 从魔兽中解压缩两个重要文件到jasshelper目录（供语法检查用）
	local mpq_handle = ar.stormlib_create_archive(map_path, OPEN_EXISTING, 0)
	local blizzard_j_path = jasshelper_dir / "blizzard.j"
	local common_j_path = jasshelper_dir / "common.j"
	if ar.stormlib_has_file(mpq_handle, "scripts\\blizzard.j") then
		ar.stormlib_extract_file(mpq_handle, blizzard_j_path, "scripts\\blizzard.j")
	else
		ar.native_generic_extract_file(blizzard_j_path, "scripts\\blizzard.j")
	end
	if ar.stormlib_has_file(mpq_handle, "scripts\\common.j") then
		ar.stormlib_extract_file(mpq_handle, common_j_path, "scripts\\common.j")
	else
		ar.native_generic_extract_file(common_j_path, "scripts\\common.j")
	end
	ar.stormlib_close_archive(mpq_handle)
	
	-- 获取另一个版本的bj和cj
	local blizzard_j_path_aux = nil
	local common_j_path_aux = nil
	if new_warcraft then
		blizzard_j_path_aux = sys.get_editor_root_dir() / "jass" / "system" / "rb" / "blizzard.j"
		common_j_path_aux = sys.get_editor_root_dir() / "jass" / "system" / "rb" / "common.j"
	else
		blizzard_j_path_aux = sys.get_editor_root_dir() / "jass" / "system" / "ht" / "blizzard.j"
		common_j_path_aux = sys.get_editor_root_dir() / "jass" / "system" / "ht" / "common.j"
	end
	
	-- 如果选择不插入基本代码，那么电影系统也将忽略
	if inject_basic == 1 then
		-- 打开MPQ（地图）
		mpq_handle = ar.stormlib_create_archive(map_path, OPEN_EXISTING, 0)
		-- 确定war3map.j的解压目录
		local war3map_j_path = jasshelper_dir / "war3map.j"
		-- 将war3map.j解压进jasshelper目录
		ar.stormlib_extract_file(mpq_handle, war3map_j_path, "war3map.j")

		-- 假如选择保存双份时，第二份位置
		local war3map_j_path_aux = jasshelper_dir / "war3mapaux.j"
		
		-- 确定需要注入的代码列表
		local inject_code_path_list = {}
		local inject_code_path_list_aux = {}
		
		-- 判断需要注入的代码类型
		local inject_code_dir = sys.get_editor_root_dir() / "jass"
		if save_type == 0 then
			if new_warcraft then
				table.insert(inject_code_path_list, inject_code_dir / "inject_hashtable.j")
			else
				table.insert(inject_code_path_list, inject_code_dir / "inject_returnbug.j")
			end
		elseif save_type == 1 then
			table.insert(inject_code_path_list, inject_code_dir / "inject_returnbug.j")
		elseif save_type == 2 then
			table.insert(inject_code_path_list, inject_code_dir / "inject_hashtable.j")
		else
			if new_warcraft then
				table.insert(inject_code_path_list, inject_code_dir / "inject_hashtable.j")
				table.insert(inject_code_path_list_aux, inject_code_dir / "inject_returnbug.j")
			else
				table.insert(inject_code_path_list, inject_code_dir / "inject_returnbug.j")
				table.insert(inject_code_path_list_aux, inject_code_dir / "inject_hashtable.j")
			end
		end

		-- 是否需要注入Script System？
		if inject_script_system == 1 then
			table.insert(inject_code_path_list, inject_code_dir / "inject_scriptsystem.j")
			table.insert(inject_code_path_list_aux, inject_code_dir / "inject_scriptsystem.j")
		end
		
		-- ** 插入代码到war3map.j中 **
		-- 如果是双份，必须先拷贝
		if save_type == 3 then
			log.trace("Making copy of the original map.")
			
			-- true代表覆盖现有文件。false不会覆盖
			fs.copy_file(map_path, map_path_aux, true)
			fs.copy_file(war3map_j_path, war3map_j_path_aux, true)
		end
		
		-- 注入代码
		inject_code(war3map_j_path, inject_code_path_list)
		
		-- 把修改完的war3map.j写回地图
		ar.stormlib_add_or_replace_file(mpq_handle, war3map_j_path, "war3map.j", bit.bor(MPQ_FILE_REPLACEEXISTING, MPQ_FILE_COMPRESS))
		
		-- 关闭地图
		ar.stormlib_close_archive(mpq_handle)
		
		-- 第二份
		if save_type == 3 then
			-- 打开MPQ（地图）
			mpq_handle = ar.stormlib_create_archive(map_path_aux, OPEN_EXISTING, 0)
			-- 注入代码
			inject_code(war3map_j_path_aux, inject_code_path_list_aux)
			
			-- 把修改完的war3map.j写回地图
			ar.stormlib_add_or_replace_file(mpq_handle, war3map_j_path_aux, "war3map.j", bit.bor(MPQ_FILE_REPLACEEXISTING, MPQ_FILE_COMPRESS))
			
			-- 关闭地图
			ar.stormlib_close_archive(mpq_handle)
		end
	end
		
	-- ** 调用jasshelper进行语法检查 **
	
	-- 确定jasshelper的路径
	local jasshelper_path = jasshelper_dir / "jasshelper.exe"
	
	-- 调试模式？
	local jasshelper_debug_mode = global_config:get_integer("JassHelper.Debug", 0)
	local jasshelper_debug_command = nil
	if jasshelper_debug_mode == 1 then
		jasshelper_debug_command = " --debug --nooptimize "
	else
		jasshelper_debug_command = " "
	end
	
	-- 生成命令行
	-- jasshelper.exe <path_to_common.j> <path_to_blizzard.j> <path_to_map.w3x>
	local command_line = nil
	
	if (save_type == 1 and new_warcraft) or (save_type == 2 and not new_warcraft) then
		command_line = 
			decorate_path(jasshelper_path) .. jasshelper_debug_command ..
			decorate_path(common_j_path_aux) .. " " ..
			decorate_path(blizzard_j_path_aux) .. " " ..
			decorate_path(map_path)
	else
		command_line = 
			decorate_path(jasshelper_path) .. jasshelper_debug_command ..
			decorate_path(common_j_path) .. " " ..
			decorate_path(blizzard_j_path) .. " " ..
			decorate_path(map_path)
	end
	
	-- 执行并等待结果
	local result = sys.spawn_dir_wait(command_line, sys.get_editor_root_dir())
	if result then
		log.debug("Compilation succeeded")
	else
		log.debug("Compilation failed")
	end
	
	-- 处理第二份
	if result and save_type == 3 then
		-- 生成命令行
		-- jasshelper.exe <path_to_common.j> <path_to_blizzard.j> <path_to_map.w3x>
		command_line = 
			decorate_path(jasshelper_path) .. jasshelper_debug_command ..
			decorate_path(common_j_path_aux) .. " " ..
			decorate_path(blizzard_j_path_aux) .. " " ..
			decorate_path(map_path_aux)
		
		-- 执行并等待结果
		if sys.spawn_dir_wait(command_line, sys.get_editor_root_dir()) then
			log.debug("Second compilation succeeded")
		else
			log.debug("Second compilation failed")
		end
	end
	
	log.debug("********************* on save end *********************")
	
	return result
end

-- 本函数在WE/War3试图载入MPQ文件时调用，在本函数中载入需要的MPQ
function on_stormload()
	log.debug("********************* on stormload end *********************")
	
	-- 试图载入mpq目录下的yd.mpq，该文件名可以自己改
	load_mpq_in_mpq_dir("yd.mpq")
	-- 其他需要载入的MPQ写在下面...
	
	log.debug("********************* on stormload end *********************")
end

-- 本函数在测试地图时使用
function on_test()
	log.debug("********************* on test start *********************")
	-- 刷新配置数据
	renew_config()
	
	-- 获取当前测试的地图名
	local map_path = sys.get_trigger_map()
	log.debug("Testing " .. map_path:file_string())
	
	log.debug("********************* on test end *********************")
end

-- on_test之后调用，用于获取命令行参数
-- 必须返回字符串
function on_execution_warcraft(original_cmd_line)
	-- 附加命令行
	local command_aux = ""
	
	if global_config:get_integer("LaunchTest.OpenGL", 0) == 1 then
		command_aux = command_aux .. " -opengl"
	end
	
	if global_config:get_integer("LaunchTest.Windowed", 0) == 1 then
		command_aux = command_aux .. " -window"
	end
	
	return original_cmd_line .. command_aux
end

-- 基础菜单ID
YDWE_MENU_ID_BASE = 48886
-- 配置菜单ID
YDWE_MENU_ID_CONFIG = YDWE_MENU_ID_BASE
-- 编译信息菜单ID
YDWE_MENU_ID_COMPILE_INFO = YDWE_MENU_ID_BASE + 1
-- 显示JassHelper版本菜单ID
YDWE_MENU_ID_SHOW_JASSHELPER_VERSION = YDWE_MENU_ID_BASE + 2

-- 初始化菜单
function on_menuinit()
	log.debug("********************* on menuinit start *********************")

	-- 首先创建新菜单
	ydwe_menu_handle = gui.create_menu()
	
	-- 添加到菜单末尾
	gui.append_menu(gui.get_main_menu(), bit.bor(MF_STRING, MF_POPUP), ydwe_menu_handle, "&YDWE")
	
	-- 添加子菜单
	gui.append_menu(ydwe_menu_handle, MF_STRING, YDWE_MENU_ID_CONFIG, "YDWE配置(&C)")
	gui.append_menu(ydwe_menu_handle, MF_STRING, YDWE_MENU_ID_COMPILE_INFO, "上一次编译信息(&T)")
	gui.append_menu(ydwe_menu_handle, MF_STRING, YDWE_MENU_ID_SHOW_JASSHELPER_VERSION, "显示JassHelper版本(&A)")
	
	-- 刷新菜单
	gui.draw_menu_bar(gui.get_main_window())
	
	log.debug("********************* on menuinit end *********************")
end

-- 当WE的窗口过程收到消息时执行
function on_windowevent(window_handle, message, wparam, lparam)
	-- 只处理菜单信息
	if message == WM_COMMAND then
		-- 菜单ID（低16位）
		local menu_id = bit.band(wparam, 0xFFFF)
		
		-- 根据ID判断操作
		if menu_id == YDWE_MENU_ID_CONFIG then
			launch_config()
		elseif menu_id == YDWE_MENU_ID_COMPILE_INFO then
			show_last_error()
		elseif menu_id == YDWE_MENU_ID_SHOW_JASSHELPER_VERSION then
			show_jasshelper_version()
		end
	end
end

-- 启动配置对话框
function launch_config()
	local config_program = sys.get_editor_root_dir() / "EverConfig.exe"
	local command_line = string.format('"%s"', config_program:file_string())
	sys.spawn_dir(command_line, sys.get_editor_root_dir())
end

-- 显示最近一次编译错误
function show_last_error()
	local jasshelper_path = sys.get_editor_root_dir() / "jasshelper" / "jasshelper.exe"
	local command_line = string.format('"%s" --showerrors', jasshelper_path:file_string())
	sys.spawn_dir(command_line, sys.get_editor_root_dir())
end

-- 显示JassHelper版本
function show_jasshelper_version()
	local jasshelper_path = sys.get_editor_root_dir() / "jasshelper" / "jasshelper.exe"
	local command_line = string.format('"%s" --about', jasshelper_path:file_string())
	sys.spawn_dir(command_line, sys.get_editor_root_dir())
end

-- 检查魔兽目录下是否有可能引起冲突的文件夹
function check_conflicting_ui()
	-- 获取魔兽路径
	local war_path = sys.get_warcraft_dir()
	-- UI目录
	local ui_dir = war_path / "UI"
	-- Units目录
	local units_dir = war_path / "Units"
	
	-- 如果发现UI目录，则删除
	if fs.exists(ui_dir) then
		if sys.question_box("检测到魔兽目录下有UI目录，可能影响本WE运行，是否删除？", "YDWE") then
			fs.remove_all(ui_dir)
		end
	end
	
	-- 同法处理Units
	if fs.exists(units_dir) then
		if sys.question_box("检测到魔兽目录下有Units目录，可能影响本WE运行，是否删除？", "YDWE") then
			fs.remove_all(units_dir)
		end
	end
end

-- 清除可能引起冲突的文件
function clear_potential_conflicting()
	-- 获取魔兽路径
	local war_path = sys.get_warcraft_dir()
	-- 需要清理的文件
	local file_list = {"MiscData.txt", "TriggerData.txt", "TriggerStrings.txt", "WorldEditData.txt", "WorldEditLayout.txt", "WorldEditStrings.txt"}
	
	for index, file in ipairs(file_list) do
		fs.remove(war_path / file)
	end
end

-- 注入代码到war3map.j中
-- 第一个参数是war3map.j的路径，第二个是一个table，包含所有需要注入的代码路径
-- 注：table必须是数组形式的，哈希表形式的不能处理
function inject_code(map_script_path, inject_code_path_list)
	log.trace("Writing code to " .. map_script_path:filename())
	
	-- 打开war3map.j供写入
	local war3map_j_file = io.open(map_script_path:file_string(), "ab+")
	
	-- 循环处理每个需要注入的文件
	for index, inject_code_path in ipairs(inject_code_path_list) do
		if fs.exists(inject_code_path) then
			-- 打开代码文件
			local f = io.open(inject_code_path:file_string(), "rb")
			-- 读取所有内容
			local code_content = f:read("*all")
			-- 关闭文件
			f:close()
			
			-- 插入代码到原文件最后
			war3map_j_file:write(code_content)
			-- 写上一个换行符
			war3map_j_file:write("\n")
		end
	end
	
	-- 关闭文件
	war3map_j_file:close()
end

--------------------------------------------------------------------------

-- 辅助函数，到主程序目录下载入MPQ
function load_mpq_in_mpq_dir(mpqname)
	-- 获取主程序所在的目录
	local editor_dir = sys.get_editor_root_dir()
	-- 定位到主程序目录下mpq目录内的MPQ文件
	local mpq = editor_dir / "mpq" /mpqname
	
	-- 文件存在否？
	if fs.exists(mpq) then
		if (ar.native_open_archive(mpq, 15) ~= 0) then
			log.debug("Loaded " .. mpq:filename())
		else
			log.debug("Cannot load " .. mpq:filename())
		end
	else
		log.debug("Cannot find " .. mpq:filename())
	end
end

-- 辅助函数，载入普通DLL
function load_normal_shared_object(sopath)
	if fs.exists(sopath) then
		if sys.load_library_by_path(sopath) then
			log.trace(string.format("%s loaded", sopath:filename()))
		else
			log.trace(string.format("Cannot load %s", sopath:filename()))
		end
	else
		log.trace(string.format("Cannot find %s", sopath:filename()))
	end
end

-- 辅助函数，给路径加上一对引号
function decorate_path_string(str)
	if string.sub(str, 1, 1) == "\"" or string.sub(str, 1, 1) == "'" then
		return str
	else
		return "\"" .. str .. "\""
	end
end

-- 辅助函数(2)，给路径加上一对引号
function decorate_path(path)
	return decorate_path_string(path:file_string())
end

-- 刷新配置数据
function renew_config()
	if fs.exists(global_config_file) then
		global_config:reload(global_config_file)
	end
end

-- 给地图名末尾，扩展名前添加内容
-- 参数string、string，返回string
function generate_aux_map_path_string_of_string(map_path_string, some_string)
	local map_path_string_without_suffix = string.sub(map_path_string, 1, string.len(map_path_string) - 4)
	local map_path_string_suffix = string.sub(map_path_string, -4)
	
	local result = map_path_string_without_suffix .. some_string .. map_path_string_suffix
	return result
end

-- 功能同上，但是第一个参数是fs.path
function generate_aux_map_path_string_of_path(map_path, some_string)
	return generate_aux_map_path_string_of_string(map_path:file_string(), some_string)
end

-- 功能同上，但是返回fs.path
function generate_aux_map_path_of_path(map_path, some_string)
	return fs.path(generate_aux_map_path_string_of_path(map_path, some_string))
end
----------------------------------------------------------------------
-- 初始化代码
function initialize()
	global_config_file = sys.get_editor_root_dir() / "EverConfig.cfg"
	global_config = sys.config_property(global_config_file)
end

initialize()
