-- 
-- Please see the readme.txt file included with this distribution for 
-- attribution and copyright information.
--

function onInit()
	ChatManager.registerControl(self);
	
	if User.isHost() then
		Module.onActivationRequested = moduleActivationRequested;
	end

	Module.onUnloadedReference = moduleUnloadedReference;

	deliverLaunchMessage()
end

function deliverLaunchMessage()
    local msg = {sender = "", font = "narratorfont"};
    msg.text = "d20_JPG v3.0.5 ruleset for SmiteWorks' Fantasy Grounds II."
    addMessage(msg);
    msg.text = "Artwork derived from the 'd20' ruleset is copyright of SmiteWorks Ltd., and is used with permission.";
    addMessage(msg);
end

function onClose()
	ChatManager.registerControl(nil);
end

function onDiceLanded(draginfo)
	if draginfo.isType("fullattack") then
		ModifierStack.setLockCount(draginfo.getSlotCount());
		for i = 1, draginfo.getSlotCount() do
			draginfo.setSlot(i);
			processDiceLanded(draginfo);
		end
		return true;
	elseif draginfo.isType("attack") or draginfo.isType("critconfirm") or draginfo.isType("damage") or draginfo.isType("init") or 
			draginfo.isType("save") or draginfo.isType("dice") then
		processDiceLanded(draginfo);
		return true;
	end
end

function onReceiveMessage(msg)
	-- Special handling for client-host behind the scenes communication
	if ChatManager.processSpecialMessage(msg) then
		return true;
	end
	
	-- Otherwise, let FG know to do standard processing
	return false;
end

function onDrop(x, y, draginfo)
	if draginfo.getType() == "number" then
		ModifierStack.applyToRoll(draginfo);
	end
end

function moduleActivationRequested(module)
	local msg = {};
	msg.text = "Players have requested permission to load '" .. module .. "'";
	msg.font = "systemfont";
	msg.icon = "indicator_moduleloaded";
	addMessage(msg);
end

function moduleUnloadedReference(module)
	local msg = {};
	msg.text = "Could not open sheet with data from unloaded module '" .. module .. "'";
	msg.font = "systemfont";
	addMessage(msg);
end

function processDiceLanded(draginfo)
	-- Figure out what type of roll we're handling
	local dragtype = draginfo.getType();
	
	-- Get any custom data about this roll
	local custom = draginfo.getCustomData();
	if not custom then
		local class, nodename = draginfo.getShortcutData();
		if nodename then
			local node = DB.findNode(nodename);
			if class == "charsheet" then
				custom = {pc = node};
			else
				custom = {npc = node};
			end
		else
			custom = {};
		end
	end

	-- Apply any modifiers
	ModifierStack.applyToRoll(draginfo);

	-- Remember the base description
	local base_desc = "";
	if dragtype == "fullattack" then
		base_desc = draginfo.getStringData();
		if not base_desc then
			base_desc = draginfo.getDescription();
		end
	else
		base_desc = draginfo.getDescription();
	end

	-- Build the basic message to deliver
	local entry = ChatManager.createBaseMessage(custom);
	entry.text = entry.text .. base_desc;
	entry.dice = draginfo.getDieList();
	entry.diemodifier = draginfo.getNumberData();
	
	-- If this is a roll from TheBox, then it should always be secret
	if OptionsManager.isOption("TBOX", "on") then
		local boxindex = string.find(draginfo.getDescription(), "[BOX]", 1, true);
		if boxindex then
			entry.dicesecret = true;
		end
	end
	
	-- If the CTRL key is held down, then reverse the number
	-- Assumption: CTRL key negates diemodifier and provides CTRL hot key bar access
	-- It doesn't make sense that CTRL should negate diemodifier on dice rolls
	if Input.isControlPressed() then
		entry.diemodifier = 0 - draginfo.getNumberData();
	end

	-- Calculate the total
	local total = 0;
	local first_die = 0;
	for i,d in ipairs(entry.dice) do
		total = total + d.result;
		if first_die == 0 then
			first_die = d.result;
		end
	end
	total = total + entry.diemodifier;

	-- Special handling for attack rolls
	local add_total = true;
	local result_str = "";
	if dragtype == "attack" or dragtype == "fullattack" or dragtype == "critconfirm" then
		local defenseval = nil;
		if User.isHost() or OptionsManager.isOption("PATK", "on") then
			if custom["targetpc"] then
				local node = DB.findNode(custom["targetpc"] .. ".ac.totals.general");
				if node then
					defenseval = node.getValue();
				end
			elseif custom["targetct"] then
				local node = DB.findNode(custom["targetct"] .. ".ac");
				local ac_str = string.match(node.getValue(), "%d+");
				if ac_str then
					defenseval = tonumber(ac_str);
				end
			end
		end

		-- State variables
		local isSpecialHit = false;
		local isCritical = false;
		local isMiss = false;
		
		-- Check for hit/miss
		if first_die == 20 then
			isSpecialHit = true;
			result_str = " [AUTOMATIC HIT]";
		elseif first_die == 1 then
			isSpecialHit = true;
			isMiss = true;
			result_str = " [AUTOMATIC MISS]";
		elseif defenseval then
			if total >= defenseval then
				result_str = " [HIT]";
			else
				result_str = " [MISS]";
				isMiss = true;
			end
		end
		
		if not isMiss and (dragtype == "attack" or dragtype == "fullattack") then
			-- Determine the crit range
			local crit_threshold = 20;
			local crit_text = string.match(base_desc, "%[CRIT (%d+)%]");
			if crit_text then
				crit_threshold = tonumber(crit_text);
				if not crit_threshold then
					crit_threshold = 20;
				end
			end

			-- Check for critical threat
			if first_die >= crit_threshold then
				isSpecialHit = true;
				isCritical = true;
				result_str = result_str .. " [CRITICAL THREAT]";
			end

			-- Handle critical threat rolls
			if isCritical then
				ChatManager.d20Check("critconfirm", draginfo.getNumberData(), base_desc .. " [CONFIRM]", custom);
			end
		end
		
		-- Hide the bonuses on special rolls, if GM rolls are hidden
		if isSpecialHit then
			if not custom["pc"] and OptionsManager.isOption("REVL", "off") then
				if isCritical then
					ChatManager.Message("[GM] Original attack = " .. first_die .. "+" .. entry.diemodifier .. "=" .. total, false);
				end
				entry.diemodifier = 0;
				add_total = false;
			end
		end
	end

	-- Special handling for saving throws
	if dragtype == "save" then
		if first_die == 20 then
			result_str = " [AUTOMATIC SUCCESS]";
		elseif first_die == 1 then
			result_str = " [AUTOMATIC FAILURE]";
		end
	end

	-- If we have a target, then add their name and portrait to the message (if available)
	if custom["targetpc"] then
		-- If so, add their name portrait to the message
		if dragtype == "attack" or dragtype == "fullattack" or dragtype == "critconfirm" then
			local node = DB.findNode(custom["targetpc"] .. ".name");
			if node then
				entry.text = entry.text .. " [at " .. node.getValue() .. "]";
			end
		elseif dragtype == "damage" then
			local node = DB.findNode(custom["targetpc"] .. ".name");
			if node then
				entry.text = entry.text .. " [to " .. node.getValue() .. "]";
			end
		end
		local char_id = string.match(custom["targetpc"], "charsheet.(.+)");
		if char_id then
			entry.icon = "portrait_" .. char_id .. "_targetportrait";
		end
	elseif custom["targetct"] then
		if dragtype == "attack" or dragtype == "fullattack" or dragtype == "critconfirm" then
			local node = DB.findNode(custom["targetct"] .. ".name");
			if node then
				entry.text = entry.text .. " [at " .. node.getValue() .. "]";
			end
		elseif dragtype == "damage" then
			local node = DB.findNode(custom["targetct"] .. ".name");
			if node then
				entry.text = entry.text .. " [to " .. node.getValue() .. "]";
			end
		end
	end

	-- Add the total, if the auto-total option is on
	if OptionsManager.isOption("TOTL", "on") and add_total then
		entry.text = entry.text .. " [" .. total .. "]";
	end

	-- Add any special results
	if result_str ~= "" then
		entry.text = entry.text .. result_str;
	end

	-- Deliver the chat entry
	deliverMessage(entry);

	-- Special handling for damage and initiative rolls
	if dragtype == "damage" then
		-- Apply damage to the PC or combattracker entry referenced
		if User.isHost() or OptionsManager.isOption("PDMG", "on") then
			if custom["targetpc"] then
				ChatManager.sendSpecialMessage(ChatManager.SPECIAL_MSGTYPE_APPLYDMG, {total, "pc", custom["targetpc"]});
			elseif custom["targetct"] then
				ChatManager.sendSpecialMessage(ChatManager.SPECIAL_MSGTYPE_APPLYDMG, {total, "ct", custom["targetct"]});
			end
		end
	
	elseif dragtype == "init" then
		-- Set the initiative for this creature in the combat tracker
		local ct_node = DB.findNode("combattracker");
		if ct_node then
			local custom_nodename = nil;
			if custom then
				if custom["pc"] then
					custom_nodename = custom["pc"].getNodeName()
				end
				if custom["npc"] then
					custom_nodename = custom["npc"].getNodeName()
				end
			end
			if custom_nodename then
				-- Check for exact CT match
				for k, v in pairs(ct_node.getChildren()) do
					if v.getNodeName() == custom_nodename then
						v.getChild("initresult").setValue(total);
					end
				end
				-- Otherwise, check for link match
				for k, v in pairs(ct_node.getChildren()) do
					local node_class, node_ref = v.getChild("link").getValue();
					if node_ref == custom_nodename then
						v.getChild("initresult").setValue(total);
					end
				end
			end
		end
	end
end
