local MogIt,mog = ...;
_G["MogIt"] = mog;
local L = mog.L;


mog.frame = CreateFrame("Frame","MogItFrame",UIParent,"ButtonFrameTemplate");
mog.list = {};

function mog:Error(msg)
	DEFAULT_CHAT_FRAME:AddMessage("MogIt: "..msg,0.9,0.5,0.9);
end

function mog.IsDropdownShown(dd)
	return DropDownList1 and DropDownList1:IsShown() and UIDropDownMenu_GetCurrentDropDown() == dd;
end


--// Slash Commands
function mog:ToggleFrame()
	if mog.frame:IsShown() then
		HideUIPanel(mog.frame);
	else
		ShowUIPanel(mog.frame);
	end
end

function mog:TogglePreview()
	if mog.view:IsShown() then
		HideUIPanel(mog.view);
	else
		ShowUIPanel(mog.view);
	end
end
--//


--// Bindings
SLASH_MOGIT1 = "/mog";
SLASH_MOGIT2 = "/mogit";
SlashCmdList["MOGIT"] = mog.ToggleFrame;

BINDING_HEADER_MogIt = "MogIt";
BINDING_NAME_MogIt = L["Toggle Mogit"];
BINDING_NAME_MogItPreview = L["Toggle Preview"];
--//


--// LibDataBroker
mog.LDBI = LibStub("LibDBIcon-1.0");
mog.mmb = LibStub("LibDataBroker-1.1"):NewDataObject("MogIt",{
	type = "launcher",
	icon = "Interface\\Icons\\INV_Enchant_EssenceCosmicGreater",
	OnClick = function(self,btn)
		if btn == "RightButton" then
			mog:TogglePreview();
		else
			mog:ToggleFrame();
		end
	end,
	OnTooltipShow = function(self)
		if not self or not self.AddLine then return end
		self:AddLine("MogIt");
		self:AddLine(L["Left click to toggle MogIt"],1,1,1);
		self:AddLine(L["Right click to toggle the preview"],1,1,1);
	end,
});
--//


--// Module API
mog.moduleVersion = 1;
mog.modules = {};
mog.moduleList = {};

function mog:GetModule(name)
	return mog.modules[name];
end

function mog:GetActiveModule()
	return mog.active;
end

function mog:RegisterModule(name,version,data)
	if mog.modules[name] then
		--mog:Error(L["The \124cFFFFFFFF%s\124r module is already loaded."]:format(name));
		return mog.modules[name];
	elseif type(version) ~= "number" or version < mog.moduleVersion then
		mog:Error(L["The \124cFFFFFFFF%s\124r module needs to be updated to work with this version of MogIt."]:format(name));
		return;
	elseif version > mog.moduleVersion then
		mog:Error(L["The \124cFFFFFFFF%s\124r module requires you to update MogIt for it to work."]:format(name));
		return;
	end
	data = data or {};
	data.name = name;
	mog.modules[name] = data;
	table.insert(mog.moduleList,data);
	if mog.menu.active == mog.menu.modules and mog.IsDropdownShown(mog.menu) then
		HideDropDownMenu(1);
		ToggleDropDownMenu(1,data,mog.menu);
	end
	return data;
end

function mog:SetModule(module,text)
	if mog.active and mog.active ~= module and mog.active.Unlist then
		mog.active:Unlist(module);
	end
	mog.active = module;
	mog:BuildList(true);
	mog:FilterUpdate();
	mog.frame.path:SetText(text or module.label or module.name or "");
end

function mog:BuildList(top,module)
	if (module and mog.active and mog.active.name ~= module) then return end;
	mog.list = mog.active and mog.active:BuildList() or {};
	mog:SortList(nil,true);
	mog.scroll:update(top and 1);
	mog.filt.models:SetText(#mog.list);
end
--//


--// Item Cache
local GetItemInfo = GetItemInfo;

mog.cacheFrame = CreateFrame("Frame");
local cacheQueue = {};
local cacheFuncs = {
	BuildList = mog.BuildList;
	ModelOnEnter = function()
		local owner = GameTooltip:GetOwner();
		if owner and GameTooltip[mog] then
			mog.ModelOnEnter(owner);
		end
	end,
	ItemMenu = function()
		if mog.IsDropdownShown(mog.Item_Menu) then
			HideDropDownMenu(1);
			ToggleDropDownMenu(nil,nil,mog.Item_Menu,"cursor",0,0,mog.Item_Menu.menuList);
		end
	end,
	SetMenu = function()
		if mog.IsDropdownShown(mog.Set_Menu) then
			HideDropDownMenu(1);
			ToggleDropDownMenu(nil,nil,mog.Set_Menu,"cursor",0,0,mog.Set_Menu.menuList);
		end
	end,
};
mog.cacheFuncs = cacheFuncs;

function mog:GetItemInfo(id,type)
	if GetItemInfo(id) then
		return GetItemInfo(id);
	elseif cacheFuncs[type] then
		cacheQueue[type] = true;
	end
end

function mog.ItemInfoReceived()
	for k in pairs(cacheQueue) do
		cacheFuncs[k]();
	end
	wipe(cacheQueue);
	mog.cacheFrame:SetScript("OnUpdate", nil);
end
--//

function mog:HasItem(itemID)
	return GetItemCount(itemID, true) > 0
end


--// Events
local defaults = {
	profile = {
		singlePreview = false,
		noAnim = false,
		minimap = {},
		url = "Battle.net",
		
		point = "CENTER",
		gridWidth = 600,
		gridHeight = 400,
		rows = 2;
		columns = 3,
		gridDress = "preview",
		sync = true,
		previewProps = {
			["*"] = {
				w = 335,
				h = 385,
				point = "CENTER",
			}
		},
		
		tooltip = true,
		tooltipWidth = 300,
		tooltipHeight = 300,
		tooltipMouse = false,
		tooltipDress = false,
		tooltipRotate = true,
		tooltipMog = true,
		tooltipMod = "None",
	}
}

function mog.LoadSettings()
	mog:UpdateGUI();
	
	if mog.db.profile.minimap.hide then
		mog.LDBI:Hide("MogIt");
	else
		mog.LDBI:Show("MogIt");
	end
	
	if mog.db.profile.tooltipRotate then
		mog.tooltip.rotate:Show();
	else
		mog.tooltip.rotate:Hide();
	end
	
	mog.scroll:update();
end

mog.frame:SetScript("OnEvent",function(self,event,arg1,...)
	if event == "PLAYER_LOGIN" then
		mog:UpdateGUI();
		self:SetScript("OnSizeChanged", function(self, width, height)
			mog.db.profile.gridWidth = width;
			mog.db.profile.gridHeight = height;
			mog:UpdateGUI(true);
		end)
	elseif event == "GET_ITEM_INFO_RECEIVED" then
		mog.cacheFrame:SetScript("OnUpdate", mog.ItemInfoReceived);
	elseif event == "PLAYER_EQUIPMENT_CHANGED" then
		local slot, hasItem = arg1, ...;
		-- don't do anything if the slot is not visible (necklace, ring, trinket)
		if mog.db.profile.gridDress == "equipped" then
			for i, frame in ipairs(mog.models) do
				local item = frame.data.item
				if item then
					if hasItem then
						if (slot ~= INVSLOT_HEAD or ShowingHelm()) and (slot ~= INVSLOT_BACK or ShowingCloak()) then
							frame.model:TryOn(mog.mogSlots[slot] and select(6, GetTransmogrifySlotInfo(slot)) or GetInventoryItemID("player", slot));
						end
					else
						frame.model:UndressSlot(slot);
					end
					frame.model:TryOn(item);
				end
			end
		end
	elseif event == "ADDON_LOADED" then
		if arg1 == MogIt then
			local AceDB = LibStub("AceDB-3.0")
			mog.db = AceDB:New("MogItDB", defaults, true)
			mog.db.RegisterCallback(mog, "OnProfileChanged", "LoadSettings")
			mog.db.RegisterCallback(mog, "OnProfileCopied", "LoadSettings")
			mog.db.RegisterCallback(mog, "OnProfileReset", "LoadSettings")

			if not mog.db.global.version then
				mog:Error(L["MogIt has loaded! Type \"/mog\" to open it."]);
			end
			mog.db.global.version = GetAddOnMetadata(MogIt,"Version");
			
			mog.LDBI:Register("MogIt",mog.mmb,mog.db.profile.minimap);
			
			mog.tooltip:SetSize(mog.db.profile.tooltipWidth,mog.db.profile.tooltipHeight);
			if mog.db.profile.tooltipRotate then
				mog.tooltip.rotate:Show();
			end
			
			for name,module in pairs(mog.moduleList) do
				if module.MogItLoaded then
					module:MogItLoaded()
				end
			end
		elseif mog.modules[arg1] then
			mog.modules[arg1].loaded = true;
			if mog.menu.active == mog.menu.modules and mog.IsDropdownShown(mog.menu) then
				HideDropDownMenu(1);
				ToggleDropDownMenu(1,mog.modules[arg1],mog.menu,mog.menu.modules,0,0);
			end
		end
	end
end);
mog.frame:RegisterEvent("PLAYER_LOGIN");
mog.frame:RegisterEvent("GET_ITEM_INFO_RECEIVED");
mog.frame:RegisterEvent("ADDON_LOADED");
mog.frame:RegisterEvent("PLAYER_EQUIPMENT_CHANGED");
--//


--// Data API
mog.data = {};

function mog:AddData(data,id,key,value)
	if not data and id and key then return end;
	if not mog.data[data] then
		mog.data[data] = {};
	end
	if not mog.data[data][key] then
		mog.data[data][key] = {};
	end
	mog.data[data][key][id] = value;
	return value;
end

function mog:DeleteData(data,id,key)
	if not mog.data[data] then return end;
	if id and key then
		mog.data[data][key][id] = nil;
	elseif id then
		for k,v in pairs(mog.data[data]) do
			v[id] = nil;
		end
	elseif key then
		mog.data[data][key] = nil;
	else
		mog.data[data] = nil;
	end
end

function mog:GetData(data,id,key)
	return mog.data[data] and mog.data[data][key] and mog.data[data][key][id];
end
--//


--// Slot Conversion
mog.slots = {
	"HeadSlot",
	"ShoulderSlot",
	"BackSlot",
	"ChestSlot",
	"ShirtSlot",
	"TabardSlot",
	"WristSlot",
	"HandsSlot",
	"WaistSlot",
	"LegsSlot",
	"FeetSlot",
	"MainHandSlot",
	"SecondaryHandSlot",
};

mog.slotsType = {
	INVTYPE_HEAD = "HeadSlot",
	INVTYPE_SHOULDER = "ShoulderSlot",
	INVTYPE_BODY = "ShirtSlot",
	INVTYPE_CLOAK = "BackSlot",
	INVTYPE_CHEST = "ChestSlot",
	INVTYPE_ROBE = "ChestSlot",
	INVTYPE_WAIST = "WaistSlot",
	INVTYPE_LEGS = "LegsSlot",
	INVTYPE_FEET = "FeetSlot",
	INVTYPE_WRIST = "WristSlot",
	INVTYPE_2HWEAPON = "MainHandSlot",
	INVTYPE_WEAPON = "MainHandSlot",
	INVTYPE_WEAPONMAINHAND = "MainHandSlot",
	INVTYPE_WEAPONOFFHAND = "SecondaryHandSlot",
	INVTYPE_RANGED = "SecondaryHandSlot",
	INVTYPE_RANGEDRIGHT = "MainHandSlot",
	INVTYPE_SHIELD = "SecondaryHandSlot",
	INVTYPE_HOLDABLE = "SecondaryHandSlot",
	INVTYPE_HAND = "HandsSlot",
	INVTYPE_TABARD = "TabardSlot",
};

-- all slot IDs that can be transmogrified
mog.mogSlots = {
	[INVSLOT_HEAD] = true,
	[INVSLOT_SHOULDER] = true,
	[INVSLOT_CHEST] = true,
	[INVSLOT_WAIST] = true,
	[INVSLOT_LEGS] = true,
	[INVSLOT_FEET] = true,
	[INVSLOT_WRIST] = true,
	[INVSLOT_HAND] = true,
	[INVSLOT_BACK] = true,
	[INVSLOT_MAINHAND] = true,
	[INVSLOT_OFFHAND] = true,
}

function mog:GetSlot(id)
	return mog.slots[id] or mog.slotsType[id];
end
--//