--[[########################################################
--## Name: CharacterProfiler
--## Author: calvin
--## Addon Details & License can be found in 'readme.txt'
--######################################################--]]

--[[########################################################
--## RPGOCP object data
--######################################################--]]
RPGOCP = {
	TITLE		= "CharacterProfiler";
	ABBR		= "CP";
	PROVIDER	= "rpgo";
	VERSION		= GetAddOnMetadata("CharacterProfiler", "Version");
	AUTHOR		= GetAddOnMetadata("CharacterProfiler", "Author");
	EMAIL		= GetAddOnMetadata("CharacterProfiler", "X-Email");
	URL			= GetAddOnMetadata("CharacterProfiler", "X-Website");
	DATE		= GetAddOnMetadata("CharacterProfiler", "X-Date");
	PROFILEDB	= "2.0.0";
	TOOLTIP		= "rpgoCPtooltip";
	FRAME		= CreateFrame("Frame","rpgoCPframe",CharacterNameFrame);
}
RPGOCP.prefs={
	enabled=true,tooltips=true,tooltipshtml=true,fixtooltip=true,fixquantity=true,fixicon=true,fixcolor=true,reagentfull=true,talentsfull=true,questsfull=false,lite=true,button=true,debug=false,ver=020000,
	scan={inventory=true,talents=true,honor=true,reputation=true,spells=true,pet=true,equipment=true,mail=true,professions=true,skills=true,quests=true,bank=true},
};
RPGOCP.events={"PLAYER_LEVEL_UP","TIME_PLAYED_MSG",
	"CRAFT_SHOW","CRAFT_UPDATE","TRADE_SKILL_SHOW","TRADE_SKILL_UPDATE",
	"BANKFRAME_OPENED","BANKFRAME_CLOSED","MAIL_CLOSED",
	"MERCHANT_CLOSED","QUEST_FINISHED","PET_STABLE_CLOSED",
	"ZONE_CHANGED","ZONE_CHANGED_INDOORS","PLAYER_CONTROL_LOST","PLAYER_CONTROL_GAINED",
};
RPGOCP.usage={
	{"/cp","-- usage/help"},
	{"/cp [on|off]","-- turns on|off"},
	{"/cp export","-- force export"},
	{"/cp show","-- show current session scan"},
	{"/cp lite [on|off]","-- turns on|off lite scanning"},
	{"/cp list","-- list current profiles"},
	{"/cp purge [all|server|char]","-- purge info"},
};
--[[## Events
--######################################################--]]
RPGOCP.event1={
	VARIABLES_LOADED =
		function()
			rpgoCP_InitState();
			rpgoCP_InitPref();
			RPGOCP:RegisterEvents();
			rpgoCPframe:UnregisterEvent("VARIABLES_LOADED");
			return true;
		end,
};
RPGOCP.event2={
	CRAFT_UPDATE =
		function(a1,a2)
			rpgoCP_TradeTimer(a1,a2);
			return true;
		end,
	TRADE_SKILL_UPDATE =
		function(a1,a2)
			rpgoCP_TradeTimer(a1,a2);
			return true;
		end,
	UNIT_INVENTORY_CHANGED =
		function(a1)
			rpgoCP_UpdateEqScan(a1);
			return true;
		end,
	BAG_UPDATE =
		function(a1)
			rpgoCP_UpdateBagScan(a1);
			return true;
		end,
	PLAYERBANKSLOTS_CHANGED =
		function()
			rpgoCP_UpdateBagScan(BANK_CONTAINER);
			return true;
		end,
	TIME_PLAYED_MSG =
		function(a1,a2)
			rpgoCP_UpdatePlayed(a1,a2);
			return true;
		end,
	ZONE_CHANGED =
		function()
			rpgoCP_UpdateZone();
			return true;
		end,
	ZONE_CHANGED_INDOORS =
		function()
			rpgoCP_UpdateZone();
			return true;
		end,
	PLAYER_CONTROL_LOST =
		function()
			rpgoCPframe:UnregisterEvent("ZONE_CHANGED");
			rpgoCPframe:UnregisterEvent("ZONE_CHANGED_INDOORS");
			return true;
		end,
	PLAYER_CONTROL_GAINED =
		function()
			rpgoCPframe:RegisterEvent("ZONE_CHANGED");
			rpgoCPframe:RegisterEvent("ZONE_CHANGED_INDOORS");
			rpgoCP_UpdateZone();
			return true;
		end,
};
RPGOCP.event3={
	RPGOCP_SCAN =
		function()
			rpgoCP_UpdateProfile();
		end,
	RPGOCP_EXPORT =
		function()
			rpgoCP_ForceExport();
		end,
	SPELLBOOK =
		function()
			rpgoCP_GetSpellBook();
			rpgoCP_GetPetSpellBook();
		end,
	BANKFRAME_OPENED =
		function()
			rpgoCPstate["_bankopen"]=true;
			rpgoCP_GetBank();
		end,
	BANKFRAME_CLOSED =
		function()
			rpgoCP_GetBank();
			rpgoCP_GetEquipment();
			rpgoCP_GetInventory();
			rpgoCPstate["_bankopen"]=nil;
		end,
	MAIL_CLOSED =
		function()
			rpgoCP_GetMail();
			rpgoCP_GetEquipment();
			rpgoCP_GetInventory();
		end,
	MERCHANT_CLOSED =
		function()
			rpgoCP_GetEquipment();
			rpgoCP_GetInventory();
		end,
	TRADE_SKILL_SHOW =
		function()
			rpgoCP_GetSkills();
			rpgoCP_GetTradeSkill();
		end,
	CRAFT_SHOW =
		function()
			rpgoCP_GetSkills();
			rpgoCP_GetCraft();
		end,
	PLAYER_LEVEL_UP =
		function()
			rpgoCP_LoadProfile();
		end,
	QUEST_FINISHED =
		function()
			rpgoCP_GetQuests(force);
		end,
	PET_STABLE_CLOSED =
		function()
			rpgoCP_ScanPetStable();
		end,
};
RPGOCP.funcs={
	fixicon =
		function(a1)
			if(a1) then
				rpgo.scanIcon=rpgo.scanIconFix;
			else
				rpgo.scanIcon=rpgo.scanIconRaw;
			end
		end,
	fixcolor =
		function(a1)
			if(a1) then
				rpgo.scanColor=rpgo.scanColorFix;
			else
				rpgo.scanColor=rpgo.scanColorRaw;
			end
		end,
	button =
		function()
			rpgoCP_ButtonHandle();
		end
};
--[[## ChatCommand
--######################################################--]]
RPGOCP.command={
	off =
		function()
			rpgoCP_Toggle(false);
		end,
	on =
		function()
			rpgoCP_Toggle(true);
		end,
	show =
		function()
			rpgoCP_Show();
		end,
	list =
		function()
			rpgoCP_ProfileList();
		end,
	export =
		function()
			rpgoCP_EventHandler('RPGOCP_EXPORT');
		end,
	purge =
		function(argv)
			rpgoCP_Purge(argv);
		end,
};

--##########################################################
local timePlayed=-1;
local timeLevelPlayed=-1;
local TradeSkillCode={optimal=4,medium=3,easy=2,trivial=1,header=0};
local UnitPower={"Rage","Focus","Energy","Happiness"};UnitPower[0]="Mana";
local UnitSlots={"Head","Neck","Shoulder","Shirt","Chest","Waist","Legs","Feet","Wrist","Hands","Finger0","Finger1","Trinket0","Trinket1","Back","MainHand","SecondaryHand","Ranged","Tabard"};UnitSlots[0]="Ammo";
local UnitStatName={"Strength","Agility","Stamina","Intellect","Spirit"};
local UnitSchoolName={"Physical","Holy","Fire","Nature","Frost","Shadow","Arcane"};
local UnitResistanceName={"Holy","Fire","Nature","Frost","Shadow","Arcane"};
local rpgoCPstruct=nil;
if(not rpgoCPstate) then rpgoCPstate={}; end
--[[########################################################
--## rpgoCP Core Functions
--######################################################--]]
--[OnLoad]
function rpgoCP_Init()
	rpgoCPframe:RegisterEvent("VARIABLES_LOADED");
	SLASH_RPGOCP1="/cp";
	SLASH_RPGOCP2="/rpgocp";
	SLASH_RPGOCP3="/profiler";
	SlashCmdList["RPGOCP"] = rpgoCP_ChatCommand
	rpgoCPframe:SetScript("OnHide", function() return rpgoCP_EventHandler('RPGOCP_SCAN') end );
	rpgoCPframe:SetScript("OnEvent", function() return rpgoCP_EventHandler(event,arg1,arg2) end );
	RPGOCP:PrintTitle("loaded.",true,true);
end

--[EventHandler]
function rpgoCP_EventHandler(event,arg1,arg2)
	if(not event) then return;
	elseif(rpgoDebugArg) then
		rpgoDebugArg(RPGOCP.ABBR,event,arg1,arg2);
	end

	if(RPGOCP.event1[event]) then
		local retVal=RPGOCP.event1[event](arg1,arg2);
		if(retVal~=nil) then return retVal; end
	end

	if( not rpgoCPpref or not rpgoCPpref["enabled"] ) then return; end
	if( RPGOCP:liteScan(event) ) then return; end
	if( RPGOCP.event2[event ] ) then
		if( rpgoCPstate["_loaded"] ) then
			local retVal=RPGOCP.event2[event](arg1,arg2);
			if(retVal~=nil) then return retVal; end
		else return false; end
	end

	--debugprofilestart();
	if( (not rpgoCPstate["_lock"]) ) then
		rpgoCPstate["_lock"]=true;
		if(not rpgoCPstate["_loaded"]) then
			rpgoCP_InitProfile();
			rpgoCP_LoadProfile();
		end
		if(RPGOCP.event3[event]) then
			RPGOCP.event3[event](arg1,arg2);
		end
		rpgoCPstate["_lock"]=nil;
	end
	--RPGOCP:PrintDebug("time: "..debugprofilestop().."ms");
end
--[ChatCommand]
function rpgoCP_ChatCommand(argline)
	RPGOCP:PrintDebug("ChatCommand ("..argline..") ");
	local argv=rpgo.Str2Ary(argline);
	if(argv and argv[1]) then
		local argcase = string.lower(argv[1]);
		table.remove(argv,1);
		if(RPGOCP.command[argcase]) then
			return RPGOCP.command[argcase](argv);
		elseif(RPGOCP.prefs[argcase]~=nil) then
			return rpgoCP_TogglePref(argcase,argv[1]);
		end
	end
	RPGOCP:PrintUsage();
	rpgoCP_TogglePref("enabled");
end
--[InitState]
function rpgoCP_InitState()
	RPGOCP:PrintDebug("InitState");
	local _,tmpClass=UnitClass("player");
	rpgoCPstate={
		_loaded=nil,_lock=nil,_bagevent=nil,_bankopen=nil,
		_server=GetRealmName(),_player=UnitName("player"),_class=tmpClass,
		_skills={},
		Equipment=0,
		Guild=nil, GuildNum=nil,
		Bag={},Inventory={},Bank={},
		Skills=0,
		Talents=0,TalentPts=0,
		SpellBook={},
		Professions={},
		Reputation=0,
		Quests=0, QuestsLog=0,
		Mail=nil,
		Honor=0,
		Stable={},
		Pets={}, PetSpell={},
	};
	RPGOCP.state = rpgoCPstate;
end
--[InitPref]
function rpgoCP_InitPref()
	RPGOCP:PrintDebug("InitPref");
	if(not RPGOCP.prefs) then return; end
	if(not rpgoCPpref) then rpgoCPpref={}; end
	rpgo.PrefTidy(rpgoCPpref,RPGOCP.prefs);
	rpgo.PrefInit(rpgoCPpref,RPGOCP.prefs);
	RPGOCP.pref = rpgoCPpref;
	rpgoCP_ButtonHandle();
	rpgoCP_FrameHookCreate();
	RPGOCP.funcs["fixcolor"](rpgoCPpref["fixcolor"]);
	RPGOCP.funcs["fixicon"](rpgoCPpref["fixicon"]);
	RPGOCP:PrintDebug("running in DEBUG MODE");
end
--[Toggle]
function rpgoCP_Toggle(val)
	if(rpgoCPpref["enabled"]~=val) then
		rpgoCP_TogglePref("enabled",val);
		RPGOCP:RegisterEvents();
		if(val) then
			rpgoCP_InitState();
			if(not rpgoCPstate["_loaded"]) then
				rpgoCP_InitProfile();
				rpgoCP_LoadProfile();
			end
		else
			rpgoCPstate=nil;
		end
	else
		rpgoCP_TogglePref("enabled",val);
	end
end
--[TogglePref]
function rpgoCP_TogglePref(pref,val)
	local msg="["..pref.."]";
	if(RPGOCP.prefs[pref]~=nil) then
		local retVal=rpgo.TogglePref(rpgoCPpref,pref,val);
		if(retVal) then msg=msg.." changed";
		else msg=msg.." currently"; end
	end
	if(RPGOCP.funcs[pref]) then
		RPGOCP.funcs[pref](val);
	end
	RPGOCP:PrintTitle(msg.." "..rpgo.PrefColorize(rpgoCPpref[pref]))
end
--[ButtonHandle]
function rpgoCP_ButtonHandle()
	if(RPGOCP.pref.button) then
		rpgoCPUISaveButton = CreateFrame("Button","rpgoCPUISaveButton",PaperDollFrame,"UIPanelButtonTemplate");
		rpgoCPUISaveButton:SetPoint("TOPLEFT",PaperDollFrame,"TOPLEFT",73,-35);
		rpgoCPUISaveButton:SetHeight(20);
		rpgoCPUISaveButton:SetWidth(40);
		rpgoCPUISaveButton:SetToplevel(true);
		rpgoCPUISaveButton:SetText(RPGOCP_SAVE_TEXT);
		rpgoCPUISaveButton:Show();
		rpgoCPUISaveButton:SetScript("OnClick", function() return rpgoCP_EventHandler('RPGOCP_EXPORT') end );
		rpgoCPUISaveButton:SetScript("OnEnter", function() return rpgo.SetTooltip(RPGOCP_SAVE_TOOLTIP) end );
		rpgoCPUISaveButton:SetScript("OnLeave", function() return GameTooltip:Hide() end );
	elseif(rpgoCPUISaveButton) then
		rpgoCPUISaveButton:Hide();
		rpgoCPUISaveButton=nil;
	end
end
--[FrameHookCreate]
function rpgoCP_FrameHookCreate()
	rpgoCPSpellBook = CreateFrame("Frame","rpgoCPSpellBook",SpellBookFrame);
	rpgoCPSpellBook:SetScript("OnShow", function() return rpgoCP_EventHandler('SPELLBOOK') end );

	function rpgoCPTradeSkillFrame_OnUpdate()
		if(TradeSkillFrame and TradeSkillFrame:IsVisible()) then
			rpgoCP_EventHandler('TRADE_SKILL_UPDATE','TRADE_SKILL_UPDATE',arg1);
		end
	end
	rpgoCPTradeSkillFrame = CreateFrame("Frame","rpgoCPTradeSkillFrame",TradeSkillFrame);
	rpgoCPTradeSkillFrame:SetScript("OnUpdate", rpgoCPTradeSkillFrame_OnUpdate );

	function rpgoCPCraftFrame_OnUpdate()
		if(CraftFrame and CraftFrame:IsVisible()) then
			rpgoCP_EventHandler('CRAFT_UPDATE','CRAFT_UPDATE',arg1);
		end
	end
	rpgoCPCraftFrame = CreateFrame("Frame","rpgoCPCraftFrame",CraftFrame);
	rpgoCPCraftFrame:SetScript("OnUpdate", rpgoCPCraftFrame_OnUpdate );

	rpgoCPtooltip = CreateFrame("GameTooltip","rpgoCPtooltip",UIParent,"GameTooltipTemplate");
	rpgoCPtooltip:SetOwner(UIParent,"ANCHOR_NONE");
end
--[InitProfile]
function rpgoCP_InitProfile()
	if ( not myProfile ) then
		myProfile={}; end
	if ( not myProfile[rpgoCPstate["_server"]] ) then
		myProfile[rpgoCPstate["_server"]]={}; end
	if ( not myProfile[rpgoCPstate["_server"]]["Character"] ) then
		myProfile[rpgoCPstate["_server"]]["Character"]={}; end
	if ( not myProfile[rpgoCPstate["_server"]]["Character"][rpgoCPstate["_player"]] ) then
		myProfile[rpgoCPstate["_server"]]["Character"][rpgoCPstate["_player"]]={}; end
	if ( myProfile[rpgoCPstate["_server"]][rpgoCPstate["_player"]] ) then
		myProfile[rpgoCPstate["_server"]][rpgoCPstate["_player"]]=nil; end
end
--[LoadProfile]
function rpgoCP_LoadProfile()
	if(not rpgoCPstruct) then
		rpgoCPstruct = myProfile[rpgoCPstate["_server"]]["Character"][rpgoCPstate["_player"]];
	end
	rpgoCPstruct["CPversion"]=RPGOCP.VERSION;
	rpgoCPstruct["CPprovider"]=RPGOCP.PROVIDER;
	rpgoCPstruct["DBversion"]=RPGOCP.PROFILEDB;
	rpgoCPstruct["Name"]=rpgoCPstate["_player"];
	rpgoCPstruct["Server"]=rpgoCPstate["_server"];
	rpgoCPstruct["Locale"]=GetLocale();
	rpgoCPstruct["Race"],rpgoCPstruct["RaceEn"]=UnitRace("player")
	rpgoCPstruct["Class"],rpgoCPstruct["ClassEn"]=UnitClass("player");
	rpgoCPstruct["Sex"],rpgoCPstruct["SexId"]=rpgo_UnitSexString("player");
	rpgoCPstruct["FactionEn"],rpgoCPstruct["Faction"]=UnitFactionGroup("player");
	rpgoCPstruct["Hearth"]=GetBindLocation();
	rpgoCPstruct["Zone"]=GetZoneText();
	rpgoCPstruct["SubZone"]=GetSubZoneText();
	rpgoCPstruct["TalentPoints"]=UnitCharacterPoints("player");
	rpgoCPstruct["TimePlayed"]=timePlayed;
	rpgoCPstruct["TimeLevelPlayed"]=timeLevelPlayed;
	rpgoCPstruct["HasRelicSlot"]=UnitHasRelicSlot("player")==1 or false;
	rpgo.updateDate(rpgoCPstruct);
	rpgoCPstate["_loaded"]=true;
end
--[UpdateProfile]
function rpgoCP_UpdateProfile()
	rpgoCP_GetGuild(force);
	rpgoCP_GetBuffs(rpgoCPstruct);
	rpgoCP_GetEquipment();
	rpgoCP_GetInventory();
	rpgoCP_GetTalents();
	rpgoCP_GetSkills();
	rpgoCP_GetSpellBook();
	rpgoCP_GetReputation();
	rpgoCP_GetQuests();
	rpgoCP_GetHonor();
	rpgoCP_UpdateZone();
	rpgoCP_ScanPetInfo();
	rpgoCP_UpdatePlayed();
	rpgo.updateDate(rpgoCPstruct);
end
--[ForceExport]
function rpgoCP_ForceExport()
	local tmpState=rpgoCPstate;
	rpgoCP_InitState();
	rpgoCPstate["Bank"]=tmpState["Bank"];
	rpgoCPstate["Professions"]=tmpState["Professions"];
	rpgoCPstate["Stable"]=tmpState["Stable"];
	rpgoCPstate["Pets"]=tmpState["Pets"];
	rpgoCPstate["PetSpell"]=tmpState["PetSpell"];
	rpgoCPstate["Mail"]=tmpState["Mail"];
	rpgoCPstate["_litemsg"]=tmpState["_litemsg"];
	rpgoCP_InitProfile();
	rpgoCP_LoadProfile();
	rpgoCP_UpdateProfile();
	rpgoCP_ScanPetInfo();
	rpgoCP_Show();
end
--[Purge]
function rpgoCP_Purge(argv)
	local tmpPurged,msg;
	if(argv and argv[1]) then
		msg = " ["..argv[1].."]";
		if(myProfile) then
			if(argv[1]=="all") then
				myProfile=nil;
				tmpPurged=true;
--				if(RPGOGP and RPGOGP.state) then
--					RPGOGP.state=nil;
--				end
			elseif(argv[1]=="char") then
				local charProfile=rpgoCPstate["_player"];
				if(argv[2]) then
					charProfile=argv[2]; end
				msg = msg.." '"..charProfile.."'";
				if(myProfile[rpgoCPstate["_server"]] and myProfile[rpgoCPstate["_server"]]["Character"] and myProfile[rpgoCPstate["_server"]]["Character"][charProfile]) then
					myProfile[rpgoCPstate["_server"]]["Character"][charProfile]=nil;
					tmpPurged=true;
				end
			elseif(argv[1]=="server") then
				local serverProfile=rpgoCPstate["_server"];
				if(argv[2]) then
					serverProfile=argv[2]; end
				msg = msg.." '"..serverProfile.."'";
				if(myProfile[serverProfile] and myProfile[serverProfile]["Character"]) then
					myProfile[serverProfile]["Character"]=nil;
					tmpPurged=true;
				end
			elseif(argv[1] and argv[2]) then
				msg = " '"..argv[2].."@"..argv[1].."'";
				if(myProfile[argv[1]] and myProfile[argv[1]]["Character"] and myProfile[argv[1]]["Character"][argv[2]]) then
					myProfile[argv[1]]["Character"][argv[2]]=nil;
					tmpPurged=true;
				elseif(myProfile[argv[2]] and myProfile[argv[2]]["Character"] and myProfile[argv[2]]["Character"][argv[1]]) then
					msg = " '"..argv[1].."@"..argv[2].."'";
					myProfile[argv[2]]["Character"][argv[1]]=nil;
					tmpPurged=true;
				end
			end
		end
	end
	if(not tmpPurged and not msg) then
		RPGOCP:PrintTitle("Usage:  /cp purge [all|server|char]");
	else
		if(tmpPurged) then
			rpgoCP_InitState();
			msg = msg.." was "..rpgo.StringColorize(rpgo.colorGreen,"purged|r");
		else
			msg = msg.." was "..rpgo.StringColorize(rpgo.colorRed,"not purged|r");
		end
		RPGOCP:PrintTitle(msg);
	end
end
--[ProfileList]
function rpgoCP_ProfileList()
	local server,guild,char;
	if(myProfile) then
		RPGOCP:PrintTitle("stored character profiles");
		for server in pairs(myProfile) do
			rpgo.PrintMsg("  Server: "..server);
			if(myProfile[server]["Guild"]) then
				for guild,_ in pairs(myProfile[server]["Guild"]) do
					rpgo.PrintMsg("    Guild: "..guild);
				end
			end
			if(myProfile[server]["Character"]) then
				for char,_ in pairs(myProfile[server]["Character"]) do
					rpgo.PrintMsg("    Char: "..char.." .. "..rpgoCP_GetProfileDate(server,char));
				end
			end
		end
	else
		RPGOCP:PrintTitle("no stored character profiles");
	end
end
--[Show]
function rpgoCP_Show()
	if(rpgoCPpref["enabled"]) then
		if(rpgoCPstate["_player"] and rpgoCPstate["_loaded"]) then
			local msg="";
			local item;
			local tsort={};
				msg="Profile for: " .. rpgoCPstate["_player"] .. " @" .. rpgoCPstate["_server"];
				if(rpgoCPstruct["Level"]) then
					msg=msg.." (lvl "..rpgoCPstruct["Level"]..")"
				end
			RPGOCP:PrintTitle(msg);

				msg="Guild: ";
				if(rpgoCPstate["Guild"]==0) then
					msg=msg.."not in a guild";
				elseif(rpgoCPstate["Guild"]) then
					if(rpgoCPstruct["Guild"]["Name"] and rpgoCPstruct["Guild"]["Title"]) then
						msg=msg.."Name:"..rpgoCPstruct["Guild"]["Name"].."  Title:"..rpgoCPstruct["Guild"]["Title"];
					else
						msg=msg..rpgo.StringColorize(rpgo.colorRed," not scanned");
					end
				else
					msg=msg..rpgo.StringColorize(rpgo.colorRed," not scanned");
				end
			rpgo.PrintMsg("  "..msg);
				msg="Zone: ";
				if(rpgoCPstruct["Zone"]) then
					msg=msg..rpgoCPstruct["Zone"];
					if(rpgoCPstruct["SubZone"] and rpgoCPstruct["SubZone"]~="") then
						msg=msg.."/"..rpgoCPstruct["SubZone"];
					end
				else
					msg=msg..rpgo.StringColorize(rpgo.colorRed," not scanned");
				end
			rpgo.PrintMsg("  "..msg);

				msg="";
				msg=msg .. "Equip:"..rpgoCPstate["Equipment"].."/"..table.getn(UnitSlots);
				msg=msg .. " Skill:" ..rpgoCPstate["Skills"];
				msg=msg .. " Talent:" ..rpgoCPstate["Talents"];
				msg=msg .. " Rep:" ..rpgoCPstate["Reputation"];
				msg=msg .. " Quest:" ..rpgoCPstate["Quests"];
				if(rpgoCPstate["Mail"]) then
					msg=msg .. " Mail:" ..rpgoCPstate["Mail"];
				end
				if(rpgoCPstate["Honor"]~=0 and rpgoCPstruct["Honor"]["RankName"]) then
					msg=msg .. " Honor:" ..rpgoCPstruct["Honor"]["RankName"];
				else
					msg=msg .. " Honor:"..NONE;
				end
			rpgo.PrintMsg("  " .. msg);

				msg="Professions:";
				tsort={};
				table.foreach(rpgoCPstate["Professions"], function (k, v) table.insert (tsort, k) end );
				table.sort(tsort);
				if(table.getn(tsort)==0) then
					msg=msg..rpgo.StringColorize(rpgo.colorRed," not scanned")..".   to scan: open each profession";
				else
					for _,item in pairs(tsort) do
						msg=msg .. " " .. item..":"..rpgoCPstate["Professions"][item];
					end
				end
			rpgo.PrintMsg("  " .. msg);

				msg="Spells:";
				tsort={};
				table.foreach(rpgoCPstate["SpellBook"], function (k, v) table.insert (tsort, k) end );
				table.sort(tsort);
				if(table.getn(tsort)==0) then
					msg=msg..rpgo.StringColorize(rpgo.colorRed," not scanned")..".   to scan: open your spellbook";
				else
					for _,item in pairs(tsort) do
						msg=msg .. " " .. item..":"..rpgoCPstate["SpellBook"][item];
					end
				end
			rpgo.PrintMsg("  " .. msg);

				msg="Inventory:";
				tsort={};
				table.foreach(rpgoCPstate["Inventory"], function (k, v) table.insert (tsort, k) end );
				table.sort(tsort);
				if(table.getn(tsort)==0) then
					msg=msg..rpgo.StringColorize(rpgo.colorRed," not scanned")..".   to scan: open your bank or 'character info'";
				else
					for _,item in pairs(tsort) do
						msg=msg .. " " .. item.."]"..rpgoCPstate["Inventory"][item]["inv"].."/"..rpgoCPstate["Inventory"][item]["slot"];
					end
				end
			rpgo.PrintMsg("  " .. msg);
				msg="Bank:";
				tsort={};
				table.foreach(rpgoCPstate["Bank"], function (k, v) table.insert (tsort, k) end );
				table.sort(tsort);
				if(table.getn(tsort)==0) then
					msg=msg..rpgo.StringColorize(rpgo.colorRed," not scanned")..".   to scan: open your bank";
				else
					for _,item in pairs(tsort) do
						msg=msg .. " " .. item .. "]".. rpgoCPstate["Bank"][item]["inv"].."/"..rpgoCPstate["Bank"][item]["slot"];
					end
				end
			rpgo.PrintMsg("  " .. msg);
			if( (rpgoCPstate["_class"]=="HUNTER" and UnitLevel("player")>9) or rpgoCPstate["_class"]=="WARLOCK") then
				msg="Pets: ";
				tsort={};
				table.foreach(rpgoCPstate["Pets"], function (k, v) table.insert (tsort, k) end );
				table.sort(tsort);
				if(table.getn(tsort)==0) then
					msg=msg..rpgo.StringColorize(rpgo.colorRed," not scanned");
				else
					for _,item in pairs(tsort) do
						msg=msg..item.." ";
						if(rpgoCPstate["PetSpell"][item]) then
							msg=msg.."(spells:"..rpgoCPstate["PetSpell"][item]..") ";
						end
					end
				end
				rpgo.PrintMsg("  " .. msg);
			end
		else
			RPGOCP:PrintTitle(rpgo.StringColorize(rpgo.colorRed,"no character scanned"));
			rpgo.PrintMsg("    to scan open your character frame ('C')");
			rpgo.PrintMsg("    or force the export with '/cp export'");
		end
	else
		rpgoCP_TogglePref("enabled");
	end
end

--[[########################################################
--## OverLoaded functions
--######################################################--]]
--[Quit]
rpgo_Quit_old=Quit;
function Quit()
	if(rpgoCPpref and rpgoCPpref["enabled"] and rpgoCPstate["_loaded"]) then
		rpgoCP_EventHandler('RPGOCP_SCAN');
		RequestTimePlayed();
	end
	return rpgo_Quit_old();
end

--[Logout]
rpgo_Logout_old=Logout;
function Logout()
	if(rpgoCPpref and rpgoCPpref["enabled"] and rpgoCPstate["_loaded"]) then
		rpgoCP_EventHandler('RPGOCP_SCAN');
		RequestTimePlayed();
	end
	return rpgo_Logout_old();
end

--[PetAbandon]
rpgo_PetAbandon_old=PetAbandon;
function PetAbandon()
	if(rpgoCPpref and rpgoCPpref["enabled"]) then
		petName=UnitName("pet");
		if (rpgoCPstate["Stable"][petName]) then
			rpgoCPstate["Stable"][petName]=nil; end
		if (rpgoCPstate["Pets"][petName]) then
			rpgoCPstate["Pets"][petName]=nil; end
		if (rpgoCPstate["PetSpell"][petName]) then
			rpgoCPstate["PetSpell"][petName]=nil; end
		if (rpgoCPstruct["Pets"] and rpgoCPstruct["Pets"][petName]) then
			rpgoCPstruct["Pets"][petName]=nil; end
	end
	return rpgo_PetAbandon_old();
end

--[[########################################################
--## rpgoCP Extract functions
--######################################################--]]
function rpgoCP_GetGuild(force)
	local isGuildMember=IsInGuild();
	if(not isGuildMember) then
		rpgoCPstate["Guild"]=0;
		rpgoCPstruct["Guild"]=nil;
	else
		local numGuildMembers=GetNumGuildMembers();
		if(force or not rpgoCPstate["Guild"] or rpgoCPstate["GuildNum"]~=numGuildMembers) then
			rpgoCPstruct["Guild"]={} ;
			local structGuild=rpgoCPstruct["Guild"];
			local guildName,guildRankName,guildRankIndex=GetGuildInfo("player");
			if(guildName) then
				structGuild["Name"]=guildName;
				structGuild["Title"]=guildRankName;
				structGuild["Rank"]=guildRankIndex;
				rpgoCPstate["Guild"]=1;
				rpgoCPstate["GuildNum"]=numGuildMembers;
			end
		end
	end
end

function rpgoCP_GetSkills()
	if(rpgoCPpref["scan"]["skills"]) then
		local count=GetNumSkillLines();
		rpgoCPstruct["Skills"]={};
		local structSkill=rpgoCPstruct["Skills"];
		local skillheader="";
		local order=1;
		rpgoCPstate["Skills"]=0;
		for i=1,GetNumSkillLines(),1 do
			local skillName,isHeader,isExpanded,skillRank,numTempPoints,skillModifier,skillMaxRank,isAbandonable,stepCost,rankCost,minLevel,skillCostType=GetSkillLineInfo(i);
			if(isHeader==1) then
				skillheader=skillName;
				structSkill[skillheader]={};
				structSkill[skillheader]["Order"]=order;
				order=order+1;
			else
				structSkill[skillheader][skillName]=strjoin(":", skillRank,skillMaxRank);
				if(skillMaxRank~=1) then
					rpgoCPstate["_skills"][skillName]=skillRank;
				end
			end
			rpgoCPstate["Skills"]=rpgoCPstate["Skills"]+1;
		end
	elseif(rpgoCPstruct) then
		rpgoCPstruct["Skills"]=nil;
	end
end

function rpgoCP_GetReputation()
	if(rpgoCPpref["scan"]["reputation"]) then
		local numFactions=GetNumFactions();
		local thisHeader,name,description,standingID,barMin,barMax,barValue,atWar,canToggle,isHeader,isCollapsed;
		rpgoCPstate["Reputation"]=0;
		rpgoCPstruct["Reputation"]={};
		local structRep=rpgoCPstruct["Reputation"];
		structRep["Count"]=numFactions;
		thisHeader=NONE;
		for index=1,numFactions do
			name,description,standingID,barMin,barMax,barValue,atWar,canToggle,isHeader,isCollapsed=GetFactionInfo(index);
			if(not atWar) then atWar=0; end
			if(isHeader) then
				thisHeader=name;
				structRep[thisHeader]={};
			elseif(standingID) then
				structRep[thisHeader][name]={};
				structRep[thisHeader][name]["Standing"]=getglobal("FACTION_STANDING_LABEL"..standingID);
				structRep[thisHeader][name]["AtWar"]=atWar;
				structRep[thisHeader][name]["Value"]=barValue-barMin..":"..barMax-barMin;
			end
			rpgoCPstate["Reputation"]=rpgoCPstate["Reputation"]+1;
		end
	elseif(rpgoCPstruct) then
		rpgoCPstruct["Reputation"]=nil;
	end
end

function rpgoCP_GetHonor()
	if(rpgoCPpref["scan"]["honor"]) then
		local lifetimeHK,lifetimeRank=GetPVPLifetimeStats();
		if(rpgoCPstate["Honor"]~=lifetimeHK) then
			local currRankIcon,currRankProgress;
			local lifetimeRankName,lifetimeRankNumber=GetPVPRankInfo(lifetimeRank);
			if ( not lifetimeRankName ) then
				lifetimeRankName=NONE; end
			local currRankName,currRankNumber=GetPVPRankInfo(UnitPVPRank("player"));
			if ( not currRankName ) then
				currRankName=NONE; end
			currRankIcon="";
			if ( currRankNumber > 0 ) then
				currRankIcon=format("%s%02d","PvPRank",currRankNumber);
				if(not rpgoCPpref["fixicon"]) then
					texture="Interface\\PvPRankBadges\\"..currRankIcon; end
			end
			currRankProgress=rpgo.round(GetPVPRankProgress()*100,2);
			rpgoCPstruct["Honor"]={};
			local structHonor=rpgoCPstruct["Honor"];
			structHonor["Lifetime"]=rpgo.Arg2Tab("Rank","Name","HK",lifetimeRankNumber,lifetimeRankName,lifetimeHK);
			structHonor["Current"]=rpgo.Arg2Tab("Rank","Name","Icon","Progress",currRankNumber,currRankName,currRankIcon,currRankProgress);
			structHonor["Current"]["HonorPoints"]=GetHonorCurrency();
			structHonor["Current"]["ArenaPoints"]=GetArenaCurrency();
			structHonor["Yesterday"]=rpgo.Arg2Tab("HK","CP",GetPVPYesterdayStats());
			structHonor["Session"]=rpgo.Arg2Tab("HK","CP",GetPVPSessionStats());
			rpgoCPstate["Honor"]=lifetimeHK;
		end
	elseif(rpgoCPstruct) then
		rpgoCPstruct["Honor"]=nil;
	end
end

function rpgoCP_GetTalents()
	if(rpgoCPpref["scan"]["talents"] and UnitLevel("player") > 9 ) then
		local numTabs=GetNumTalentTabs();
		local numPts=UnitCharacterPoints("player");
		if( (rpgoCPstate["Talents"]~=numTabs) or (rpgoCPstate["TalentPts"]~=numPts) ) then
			local numTalents,tabIndex,talentIndex
			local tabName,texture,points,fileName;
			local nameTalent,iconTexture,iconX,iconY,currentRank,maxRank;
			rpgoCPstruct["TalentPoints"]=numPts;
			rpgoCPstruct["Talents"]={};
			local structTalent=rpgoCPstruct["Talents"];
			rpgoCPstate["Talents"]=0;
			for tabIndex=1,numTabs do
				numTalents=GetNumTalents(tabIndex);
				tabName,texture,points,fileName=GetTalentTabInfo(tabIndex);
				structTalent[tabName]={};
				structTalent[tabName]["PointsSpent"]=points;
				if(not rpgoCPpref["fixicon"]) then
					fileName="Interface\\TalentFrame\\"..fileName; end
				structTalent[tabName]["Background"]=fileName;
				structTalent[tabName]["Order"]=tabIndex;
				for talentIndex=1,numTalents do
					nameTalent,iconTexture,iconX,iconY,currentRank,maxRank=GetTalentInfo(tabIndex,talentIndex);
					if(currentRank > 0 or rpgoCPpref["talentsfull"]) then
						structTalent[tabName][nameTalent]={};
						structTalent[tabName][nameTalent]["Rank"]=strjoin(":", currentRank,maxRank);
						structTalent[tabName][nameTalent]["Location"]=strjoin(":", iconX,iconY);
						structTalent[tabName][nameTalent]["Icon"]=rpgo.scanIcon(iconTexture);
						rpgoCPtooltip:SetTalent(tabIndex,talentIndex)
						structTalent[tabName][nameTalent]["Tooltip"]=rpgo.ScanTooltip("rpgoCPtooltip",rpgoCPpref["tooltipshtml"]);
					end
				end
				rpgoCPstate["Talents"]=rpgoCPstate["Talents"]+1;
			end
			rpgoCPstate["TalentPts"]=numPts;
		end
	elseif(rpgoCPstruct) then
		rpgoCPstruct["Talents"]=nil;
	end
end

function rpgoCP_GetQuests(force)
	if(rpgoCPpref["scan"]["quests"]) then
		local numEntries,numQuests=GetNumQuestLogEntries();
		if(force or rpgoCPstate["QuestsLog"]~=numEntries) then
			local header=UNKNOWN;
			rpgoCPstruct["Quests"]={};
			local structQuest=rpgoCPstruct["Quests"];
			local slot,index,num,j; slot=1;
			rpgoCPstate["Quests"]=0;
			for index=1,numEntries,1 do
				local questLogTitleText,level,questTag,suggestedGroup,isHeader,isCollapsed,isComplete;
				if(rpgo.isBC()) then
					questLogTitleText,level,questTag,suggestedGroup,isHeader,isCollapsed,isComplete = GetQuestLogTitle(index);
				else
					questLogTitleText,level,questTag,isHeader,isCollapsed,isComplete=GetQuestLogTitle(index);
				end
				if(isHeader) then
					header=questLogTitleText;
					structQuest[header]={}
				elseif(questLogTitleText) then
					structQuest[header][slot]={}
					structQuest[header][slot]["Title"]=questLogTitleText;
					structQuest[header][slot]["Level"]=level;
					structQuest[header][slot]["Complete"]=isComplete;
					if(questTag) then
						structQuest[header][slot]["Tag"]=questTag;
					end
					if(suggestedGroup~=nil and suggestedGroup>0) then
						structQuest[header][slot]["Group"]=suggestedGroup;
					end
					SelectQuestLogEntry(index);
					if(rpgoCPpref["questsfull"]) then
						structQuest[header][slot]["Description"],structQuest[header][slot]["Objective"]=GetQuestLogQuestText(index);
					elseif(structQuest[header][slot]["Description"]) then
						structQuest[header][slot]["Description"]=nil;
						structQuest[header][slot]["Objective"]=nil;
					end
					num=GetNumQuestLeaderBoards(index);
					if(num and num > 0) then
						structQuest[header][slot]["Tasks"]={};
						for j=1,num,1 do
							structQuest[header][slot]["Tasks"][j]=rpgo.Arg2Tab("Note","Type","Done",GetQuestLogLeaderBoard(j,index));
						end
					end
					num=GetQuestLogRewardMoney(index);
					if(num and num~=0) then
						structQuest[header][slot]["RewardMoney"]=num;
					end
					num=GetNumQuestLogRewards(index);
					if(num and num > 0) then
						structQuest[header][slot]["Rewards"]={};
						for j=1,num,1 do
							_,curItemTexture,itemCount,_,_=GetQuestLogRewardInfo(j);
							rpgoCPtooltip:SetQuestLogItem("reward",j);
							table.insert(structQuest[header][slot]["Rewards"],rpgoCP_ScanItemInfo(GetQuestLogItemLink("reward",j),curItemTexture,itemCount,"rpgoCPtooltip"));
						end
					end
					num=GetNumQuestLogChoices(index);
					if(num and num > 0) then
						structQuest[header][slot]["Choice"]={};
						for j=1,num,1 do
							_,curItemTexture,itemCount,_,_=GetQuestLogChoiceInfo(j);
							rpgoCPtooltip:SetQuestLogItem("choice",j);
							table.insert(structQuest[header][slot]["Choice"],rpgoCP_ScanItemInfo(GetQuestLogItemLink("choice",j),curItemTexture,itemCount,"rpgoCPtooltip"));
						end
					end
					slot=slot+1;
					rpgoCPstate["Quests"]=rpgoCPstate["Quests"]+1;
				end
				rpgoCPstate["QuestsLog"]=rpgoCPstate["QuestsLog"]+1;
			end
		end
	elseif ( rpgoCPstruct ) then
		rpgoCPstruct["Quests"]=nil;
	end
end

function rpgoCP_GetStats(structStats,unit)
	if(not unit) then unit="player"; end
	if( unit=="player" and (UnitIsDeadOrGhost("player") or rpgoCP_UnitHasResSickness("player")) ) then
		return
	end
	if(not structStats["Attributes"]) then structStats["Attributes"]={}; end
	structStats["Level"]=UnitLevel(unit);
	structStats["Health"]=UnitHealthMax(unit);
	structStats["Mana"]=UnitManaMax(unit);
	structStats["Power"]=UnitPower[UnitPowerType(unit)];
	structStats["Attributes"]["Stats"]={};
	for i=1,table.getn(UnitStatName),1 do
		local stat,effectiveStat,posBuff,negBuff=UnitStat(unit,i);
		structStats["Attributes"]["Stats"][UnitStatName[i]] = strjoin(":", (stat - posBuff - negBuff),posBuff,negBuff);
	end
	local base,posBuff,negBuff,modBuff,effBuff,stat;
	base,modBuff = UnitDefense(unit);
	posBuff,negBuff = 0,0;
	if ( modBuff > 0 ) then
		posBuff = modBuff;
	elseif ( modBuff < 0 ) then
		negBuff = modBuff;
	end
	structStats["Attributes"]["Defense"] = {};
	structStats["Attributes"]["Defense"]["Defense"] = strjoin(":", base,posBuff,negBuff);
	base,effBuff,stat,posBuff,negBuff=UnitArmor(unit);
	structStats["Attributes"]["Defense"]["Armor"] = strjoin(":", base,posBuff,negBuff);
	structStats["Attributes"]["Defense"]["ArmorReduction"] = PaperDollFrame_GetArmorReduction(effBuff, UnitLevel("player"));
	base,posBuff,negBuff = GetCombatRating(CR_DEFENSE_SKILL),GetCombatRatingBonus(CR_DEFENSE_SKILL),0;
	structStats["Attributes"]["Defense"]["DefenseRating"]=strjoin(":", base,posBuff,negBuff);
	structStats["Attributes"]["Defense"]["DefensePercent"]=GetDodgeBlockParryChanceFromDefense();
	base,posBuff,negBuff = GetCombatRating(CR_DODGE),GetCombatRatingBonus(CR_DODGE),0;
	structStats["Attributes"]["Defense"]["DodgeRating"]=strjoin(":", base,posBuff,negBuff);
	structStats["Attributes"]["Defense"]["DodgeChance"]=rpgo.round(GetDodgeChance(),2);
	base,posBuff,negBuff = GetCombatRating(CR_BLOCK),GetCombatRatingBonus(CR_BLOCK),0;
	structStats["Attributes"]["Defense"]["BlockRating"]=strjoin(":", base,posBuff,negBuff);
	structStats["Attributes"]["Defense"]["BlockChance"]=rpgo.round(GetBlockChance(),2);
	base,posBuff,negBuff = GetCombatRating(CR_PARRY),GetCombatRatingBonus(CR_PARRY),0;
	structStats["Attributes"]["Defense"]["ParryRating"]=strjoin(":", base,posBuff,negBuff);
	structStats["Attributes"]["Defense"]["ParryChance"]=rpgo.round(GetParryChance(),2);
	structStats["Attributes"]["Defense"]["Resilience"]={};
	structStats["Attributes"]["Defense"]["Resilience"]["Melee"]=GetCombatRating(CR_CRIT_TAKEN_MELEE);
	structStats["Attributes"]["Defense"]["Resilience"]["Ranged"]=GetCombatRating(CR_CRIT_TAKEN_RANGED);
	structStats["Attributes"]["Defense"]["Resilience"]["Spell"]=GetCombatRating(CR_CRIT_TAKEN_SPELL);

	structStats["Attributes"]["Resists"]={};
	for i=1,table.getn(UnitResistanceName),1 do
		local base,resistance,positive,negative=UnitResistance(unit,i);
		structStats["Attributes"]["Resists"][UnitResistanceName[i]] = strjoin(":", base,positive,negative);
	end
	if(unit=="player") then
		structStats["Money"]=rpgo.Arg2Tab("Gold","Silver","Copper",rpgo_GetMoney());
		structStats["IsResting"]=IsResting() == 1 or false;
		local XPrest=GetXPExhaustion();
		if(not XPrest) then XPrest=0; end
		structStats["Experience"]=strjoin(":", UnitXP("player"),UnitXPMax("player"),XPrest);
		rpgoCPstruct["timestamp"]["Attributes"]=time();
	end
	rpgoCP_GetAttackRating(structStats["Attributes"],unit);
end

function rpgoCP_CharacterDamageFrame(unit,prefix)
	if(not unit) then unit="player"; end
	if(not prefix) then prefix="PlayerStatFrameLeft"; end
	local damageFrame = getglobal(prefix.."2");
	rpgoCPtooltip:ClearLines();
	-- Main hand weapon
	rpgoCPtooltip:SetText(INVTYPE_WEAPONMAINHAND, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b);
	rpgoCPtooltip:AddDoubleLine(ATTACK_SPEED_COLON, format("%.2f", damageFrame.attackSpeed), NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b);
	rpgoCPtooltip:AddDoubleLine(DAMAGE_COLON, damageFrame.damage, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b);
	rpgoCPtooltip:AddDoubleLine(DAMAGE_PER_SECOND, format("%.1f", damageFrame.dps), NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b);
	-- Check for offhand weapon
	if ( damageFrame.offhandAttackSpeed ) then
		rpgoCPtooltip:AddLine("\n");
		rpgoCPtooltip:AddLine(INVTYPE_WEAPONOFFHAND, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b);
		rpgoCPtooltip:AddDoubleLine(ATTACK_SPEED_COLON, format("%.2f", damageFrame.offhandAttackSpeed), NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b);
		rpgoCPtooltip:AddDoubleLine(DAMAGE_COLON, damageFrame.offhandDamage, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b);
		rpgoCPtooltip:AddDoubleLine(DAMAGE_PER_SECOND, format("%.1f", damageFrame.offhandDps), NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b);
	end
end

function rpgoCP_CharacterRangedDamageFrame(unit,prefix)
	if(not unit) then unit="player"; end
	if(not prefix) then prefix="PlayerStatFrameLeft"; end
	local damageFrame = getglobal(prefix.."2");
	if (not damageFrame.damage) then return; end
	rpgoCPtooltip:ClearLines();
	rpgoCPtooltip:SetText(INVTYPE_RANGED, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b);
	rpgoCPtooltip:AddDoubleLine(ATTACK_SPEED_COLON, format("%.2f", damageFrame.attackSpeed), NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b);
	rpgoCPtooltip:AddDoubleLine(DAMAGE_COLON, damageFrame.damage, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b);
	rpgoCPtooltip:AddDoubleLine(DAMAGE_PER_SECOND, format("%.1f", damageFrame.dps), NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b);
end

function rpgoCP_GetAttackRating(structAttack,unit,prefix)
	if(not unit) then unit="player"; end
	if(not prefix) then prefix="PlayerStatFrameLeft"; end
	UpdatePaperdollStats(prefix, "PLAYERSTAT_MELEE_COMBAT");
	local stat2 = getglobal(prefix.."2");
	local stat2Text = getglobal(prefix.."2".."StatText");
	local mainHandAttackBase,mainHandAttackMod,offHandAttackBase,offHandAttackMod = UnitAttackBothHands(unit);
	local speed,offhandSpeed = UnitAttackSpeed(unit);
	structAttack["Melee"]={};
	structAttack["Melee"]["MainHand"]={};
	structAttack["Melee"]["MainHand"]["AttackSpeed"]=rpgo.round(speed,2);
	structAttack["Melee"]["MainHand"]["AttackDPS"]=rpgo.round(stat2.dps,1);
	structAttack["Melee"]["MainHand"]["AttackSkill"]=mainHandAttackBase+mainHandAttackMod;
	structAttack["Melee"]["MainHand"]["AttackRating"]=strjoin(":", mainHandAttackBase,mainHandAttackMod,0);

	local tt=stat2Text:GetText();
	tt=rpgo.stripColor(tt);
	structAttack["Melee"]["MainHand"]["DamageRange"]=string.gsub(tt,"^(%d+)%s?-%s?(%d+)$","%1:%2");
	rpgoCP_CharacterDamageFrame();
	local tt=rpgo.ScanTooltip("rpgoCPtooltip",rpgoCPpref["tooltipshtml"]);
	structAttack["Melee"]["DamageRangeTooltip"]=rpgo.stripColor(tt);

	if ( offhandSpeed ) then
		structAttack["Melee"]["OffHand"]={};
		structAttack["Melee"]["OffHand"]["AttackSpeed"]=rpgo.round(offhandSpeed,2);
		structAttack["Melee"]["OffHand"]["AttackDPS"]=rpgo.round(stat2.offhandDps,1);
		structAttack["Melee"]["OffHand"]["AttackSkill"]=offHandAttackBase+offHandAttackMod;
		structAttack["Melee"]["OffHand"]["AttackRating"]=strjoin(":", offHandAttackBase,offHandAttackMod,0);

		tt=stat2.offhandDamage;
		tt=rpgo.stripColor(tt);
		structAttack["Melee"]["OffHand"]["DamageRange"]=string.gsub(tt,"^(%d+)%s?-%s?(%d+)","%1:%2");
	else
		structAttack["Melee"]["OffHand"]=nil;
	end
	local stat4 = getglobal(prefix.."4");
	local base,posBuff,negBuff;
	base,posBuff,negBuff = UnitAttackPower(unit);
	structAttack["Melee"]["AttackPower"] = strjoin(":", base,posBuff,negBuff);
	structAttack["Melee"]["AttackPowerDPS"]=rpgo.round(max((base+posBuff+negBuff), 0)/ATTACK_POWER_MAGIC_NUMBER,1);
	structAttack["Melee"]["AttackPowerTooltip"]=stat4.tooltip2;

	base,posBuff,negBuff = GetCombatRating(CR_HIT_MELEE),GetCombatRatingBonus(CR_HIT_MELEE),0;
	structAttack["Melee"]["HitRating"]=strjoin(":", base,posBuff,negBuff);
	base,posBuff,negBuff = GetCombatRating(CR_CRIT_MELEE),GetCombatRatingBonus(CR_CRIT_MELEE),0;
	structAttack["Melee"]["CritRating"]=strjoin(":", base,posBuff,negBuff);
	base,posBuff,negBuff = GetCombatRating(CR_HASTE_MELEE),GetCombatRatingBonus(CR_HASTE_MELEE),0;
	structAttack["Melee"]["HasteRating"]=strjoin(":", base,posBuff,negBuff);

	structAttack["Melee"]["CritChance"]=rpgo.round(GetCritChance(),2);

	if(unit=="player") then
		local rangedTexture = GetInventoryItemTexture("player",18);
		if ( not rangedTexture ) then
			structAttack["Ranged"]=nil;
		else
			UpdatePaperdollStats(prefix, "PLAYERSTAT_RANGED_COMBAT");
			local damageFrame = getglobal(prefix.."2");
			local damageFrameText = getglobal(prefix.."2".."StatText");
			if(PaperDollFrame.noRanged) then
				structAttack["Ranged"]=nil;
			else
				local rangedAttackSpeed,minDamage,maxDamage,physicalBonusPos,physicalBonusNeg,percent = UnitRangedDamage(unit);
				structAttack["Ranged"]={};
				structAttack["Ranged"]["AttackSpeed"]=rpgo.round(rangedAttackSpeed,1);
				structAttack["Ranged"]["AttackDPS"]=rpgo.round(damageFrame.dps,1);
				structAttack["Ranged"]["AttackSkill"]=UnitRangedAttack(unit);
				local rangedAttackBase,rangedAttackMod = UnitRangedAttack(unit);
				structAttack["Ranged"]["AttackRating"]=strjoin(":", rangedAttackBase,rangedAttackMod,0);

				tt=damageFrameText:GetText();
				tt=rpgo.stripColor(tt);
				structAttack["Ranged"]["DamageRange"]=string.gsub(tt,"^(%d+)%s?-%s?(%d+)","%1:%2");

				base,posBuff,negBuff = GetCombatRating(CR_HIT_RANGED),GetCombatRatingBonus(CR_HIT_RANGED),0;
				structAttack["Ranged"]["HitRating"]=strjoin(":", base,posBuff,negBuff);
				base,posBuff,negBuff = GetCombatRating(CR_CRIT_RANGED),GetCombatRatingBonus(CR_CRIT_RANGED),0;
				structAttack["Ranged"]["CritRating"]=strjoin(":", base,posBuff,negBuff);
				base,posBuff,negBuff = GetCombatRating(CR_HASTE_RANGED),GetCombatRatingBonus(CR_HASTE_RANGED),0;
				structAttack["Ranged"]["HasteRating"]=strjoin(":", base,posBuff,negBuff);
				structAttack["Ranged"]["CritChance"]=rpgo.round(GetRangedCritChance(),2);

				rpgoCP_CharacterRangedDamageFrame();
				local tt=rpgo.ScanTooltip("rpgoCPtooltip",rpgoCPpref["tooltipshtml"]);
				tt=rpgo.stripColor(tt);
				structAttack["Ranged"]["DamageRangeTooltip"]=tt;
				if( HasWandEquipped() ) then
					structAttack["Ranged"]["AttackPower"]=nil;
					structAttack["Ranged"]["AttackPowerDPS"]=nil;
					structAttack["Ranged"]["AttackPowerTooltip"]=nil;
					structAttack["Ranged"]["HasWandEquipped"]=true;
				else
					local base,pos,neg=UnitRangedAttackPower(unit);
					apDPS=base/ATTACK_POWER_MAGIC_NUMBER;
					structAttack["Ranged"]["AttackPower"] = strjoin(":", base,pos,neg);
					structAttack["Ranged"]["AttackPowerDPS"]=rpgo.round(apDPS,1);
					structAttack["Ranged"]["AttackPowerTooltip"]=format(RANGED_ATTACK_POWER_TOOLTIP,apDPS);
					structAttack["Ranged"]["HasWandEquipped"]=false;
				end
			end
		end
		structAttack["Spell"] = {};
		structAttack["Spell"]["BonusHealing"] = GetSpellBonusHealing();
		local holySchool = 2;
		structAttack["Spell"]["CritChance"]=rpgo.round(GetSpellCritChance(holySchool),2);
		local minModifier = GetSpellBonusDamage(holySchool);
		structAttack["Spell"]["School"]={};
		for i=(holySchool+1), MAX_SPELL_SCHOOLS do
			bonusDamage = GetSpellBonusDamage(i);
			minModifier = min(minModifier,bonusDamage);
			structAttack["Spell"]["School"][UnitSchoolName[i]] = bonusDamage;
		end
		structAttack["Spell"]["BonusDamage"]=GetSpellBonusDamage(holySchool);
		base,posBuff,negBuff = GetCombatRating(CR_HIT_SPELL),GetCombatRatingBonus(CR_HIT_SPELL),0;
		structAttack["Spell"]["HitRating"]=strjoin(":", base,posBuff,negBuff);
		base,posBuff,negBuff = GetCombatRating(CR_CRIT_SPELL),GetCombatRatingBonus(CR_CRIT_SPELL),0;
		structAttack["Spell"]["CritRating"]=strjoin(":", base,posBuff,negBuff);
		base,posBuff,negBuff = GetCombatRating(CR_HASTE_SPELL),GetCombatRatingBonus(CR_HASTE_SPELL),0;
		structAttack["Spell"]["HasteRating"]=strjoin(":", base,posBuff,negBuff);
		structAttack["Spell"]["Penetration"] = GetSpellPenetration();
		if(rpgo.isBC()) then
			local base,casting = GetManaRegen();
			base = floor( (base * 5.0) + 0.5);
			casting = floor( (casting * 5.0) + 0.5);
			structAttack["Spell"]["ManaRegen"] = strjoin(":", base,casting);
		end
	end
	PaperDollFrame_UpdateStats();
end

function rpgoCP_GetBuffs(structBuffs,unit)
	if(not unit) then unit="player"; end
	local index=1;
	if(not structBuffs["Attributes"]) then structBuffs["Attributes"]={}; end
	if(UnitBuff(unit,index)) then
		structBuffs["Attributes"]["Buffs"]={};
		while(UnitBuff(unit,index)) do
			local name,rank,icon,count=UnitBuff(unit,index);
			rpgoCPtooltip:SetUnitBuff(unit,index);
			structBuffs["Attributes"]["Buffs"][index]={};
			structBuffs["Attributes"]["Buffs"][index]["Name"]=name;
			if(rank and rank~="") then
				structBuffs["Attributes"]["Buffs"][index]["Rank"]=rank;
			end
			if(count and count>1) then
				structBuffs["Attributes"]["Buffs"][index]["Count"]=count;
			end
			structBuffs["Attributes"]["Buffs"][index]["Icon"]=rpgo.scanIcon(icon);
			structBuffs["Attributes"]["Buffs"][index]["Tooltip"]=rpgo.ScanTooltip("rpgoCPtooltip",rpgoCPpref["tooltipshtml"]);
			index=index+1
		end
	else
		structBuffs["Attributes"]["Buffs"]=nil;
	end
	index=1;
	if(UnitDebuff(unit,index)) then
		structBuffs["Attributes"]["Debuffs"]={};
		while(UnitDebuff(unit,index)) do
			local name,rank,icon,count=UnitDebuff(unit,index);
			rpgoCPtooltip:SetUnitDebuff(unit,index);
			structBuffs["Attributes"]["Debuffs"][index]={};
			structBuffs["Attributes"]["Debuffs"][index]["Name"]=name;
			if(rank and rank~="") then
				structBuffs["Attributes"]["Debuffs"][index]["Rank"]=rank;
			end
			if(count and count>1) then
				structBuffs["Attributes"]["Debuffs"][index]["Count"]=count;
			end
			structBuffs["Attributes"]["Debuffs"][index]["Icon"]=rpgo.scanIcon(icon);
			structBuffs["Attributes"]["Debuffs"][index]["Tooltip"]=rpgo.ScanTooltip("rpgoCPtooltip",rpgoCPpref["tooltipshtml"]);
			index=index+1
		end
	else
		structBuffs["Attributes"]["Debuffs"]=nil;
	end
end

function rpgoCP_GetEquipment(force)
	if(rpgoCPpref["scan"]["equipment"]) then
		if( force or rpgoCPstate["Equipment"]==0 or not rpgoCPstate["eq"] ) then
			local index,slot;
			rpgoCPstate["Equipment"]=0;
			rpgoCPstruct["Equipment"]={};
			local structEquip=rpgoCPstruct["Equipment"];
			for index,slot in pairs(UnitSlots) do
				local curItemLink,itemCount;
				local curItemTexture=GetInventoryItemTexture("player",index);
				rpgoCPtooltip:SetInventoryItem("player",index);
				if(index==0) then
					if(curItemTexture) then
						local curItemName,_=rpgo.GetItemInfoTT();
						_,curItemLink,_,_=rpgo.GetItemInfo(curItemName);
					end
				else
					curItemLink=GetInventoryItemLink("player",index);
				end
				if(curItemLink) then
					itemCount = GetInventoryItemCount("player",index);
					if(itemCount == 1) then itemCount=nil; end
					structEquip[slot]=rpgoCP_ScanItemInfo(curItemLink,curItemTexture,itemCount,"rpgoCPtooltip");
					rpgoCPstate["Equipment"]=rpgoCPstate["Equipment"]+1;
					curItemLink=nil;
				end
			end
			rpgoCPstruct["timestamp"]["Equipment"]=time();
			rpgoCPstate["eq"]=1;
			rpgoCPframe:RegisterEvent("UNIT_INVENTORY_CHANGED");
		end
	elseif(rpgoCPstruct) then
		structEquip=nil;
	end
	rpgoCP_GetStats(rpgoCPstruct);
end

function rpgoCP_GetMail()
	if(rpgoCPpref["scan"]["mail"]) then
		numMessages=GetInboxNumItems();
		if( (not rpgoCPstate["Mail"]) or (rpgoCPstate["Mail"]~=numMessages and numMessages~=0) ) then
			rpgoCPstate["Mail"]=0;
			rpgoCPstruct["MailBox"]={};
			local structMail=rpgoCPstruct["MailBox"];
			for index=1,numMessages do
				local _,_,mailSender,mailSubject,mailCoin,_,mailDays=GetInboxHeaderInfo(index);
				local itemName,itemIcon,itemQty,itemQuality,_=GetInboxItem(index);
				if(not mailSender) then mailSender=UNKNOWN; end
				structMail[index]={};
				structMail[index]["Sender"]=mailSender;
				structMail[index]["Subject"]=mailSubject;
				structMail[index]["Coin"]=mailCoin;
				structMail[index]["CoinIcon"]=rpgo.scanIcon(GetCoinIcon(mailCoin));
				structMail[index]["Days"]=mailDays;
				if(itemName) then
					rpgoCPtooltip:SetInboxItem(index);
					structMail[index]["Item"]=rpgoCP_ScanItemInfo(itemName,itemIcon,itemQty,"rpgoCPtooltip");
				end
				rpgoCPstate["Mail"]=rpgoCPstate["Mail"]+1;
			end
			rpgoCPstruct["timestamp"]["MailBox"]=time();
		end
	elseif(rpgoCPstruct) then
		rpgoCPstruct["MailBox"]=nil;
	end
end

function rpgoCP_GetInventory()
	local bagNum=0;
	if(rpgoCPpref["scan"]["inventory"]) then
		if(not rpgoCPstruct["Inventory"]) then
			rpgoCPstruct["Inventory"]={};
			rpgoCPstate["Inventory"]={};
		end
		local structInventory=rpgoCPstruct["Inventory"];
		for bag=0,NUM_BAG_FRAMES,1 do
			bagName="Bag" .. bag;
			local bagname,link,texture,color,item,itemname;
			if(bag==0) then
				bagname=GetBagName(bag);
				baglink="|cffffffff|Hitem:0:0:0:0|h["..bagname.."]|h|r";
				texture="Button-Backpack-Up";
				if(not rpgoCPpref["fixicon"]) then
					texture="Interface\\Buttons\\"..texture; end
				rpgoCPtooltip:SetText(bagname);
				rpgoCPtooltip:AddLine(format(CONTAINER_SLOTS,rpgoCP_GetContainerNumSlots(bag),""));
			else
				baglink=GetInventoryItemLink("player",ContainerIDToInventoryID(bag));
				texture=GetInventoryItemTexture("player",ContainerIDToInventoryID(bag));
				rpgoCPtooltip:SetInventoryItem("player",ContainerIDToInventoryID(bag))
			end
			if(baglink) then
				if(not rpgoCPstate["Inventory"][bag] or not rpgoCPstate["Bag"][bag]) then
					structInventory[bagName]=rpgoCP_ScanBagInfo(bag,baglink,texture,"rpgoCPtooltip");
					structInventory[bagName]["Contents"]=rpgoCP_GetContainerItems("Inventory",bag,bag);
					rpgoCPstate["Bag"][bag]=1;
				end
			else
				structInventory[bagName]=nil;
			end
			bagNum=bag;
		end
		if(HasKey and HasKey()) then
			local bagname,link,texture,color,item,itemname;
			local bag,bagnum;
			bagname=KEYRING;
			bag=KEYRING_CONTAINER;
			bagnum=bagNum+1;
			bagName="Bag" .. bagnum;
			texture="UI-Button-KeyRing";
			if(not rpgoCPpref["fixicon"]) then
				texture="Interface\\Buttons\\"..texture; end
			baglink="|cffffffff|Hitem:0:0:0:0|h["..KEYRING.."]|h|r";
			rpgoCPtooltip:SetText(KEYRING);
			rpgoCPtooltip:AddLine(format(CONTAINER_SLOTS,rpgoCP_GetContainerNumSlots(bag),KEYRING));
			if(baglink) then
				if(not rpgoCPstate["Inventory"][bagnum] or not rpgoCPstate["Bag"][bag]) then
					structInventory[bagName]=rpgoCP_ScanBagInfo(bag,baglink,texture,"rpgoCPtooltip");
					structInventory[bagName]["Contents"]=rpgoCP_GetContainerItems("Inventory",bag,bagnum);
					rpgoCPstate["Bag"][bag]=1;
				end
			else
				structInventory[bagName]=nil;
			end
		end
		rpgoCPstruct["timestamp"]["Inventory"]=time();
	elseif(rpgoCPstruct) then
		rpgoCPstruct["Inventory"]=nil;
		rpgoCPstate["Inventory"]={};
	end
end

function rpgoCP_GetBank()
	if(rpgoCPpref["scan"]["bank"]) then
		if(not rpgoCPstate["_bankopen"]) then return end
		if(not rpgoCPstruct["Bank"]) then
			rpgoCPstruct["Bank"]={};
			rpgoCPstate["Bank"]={};
		end
		local structBank=rpgoCPstruct["Bank"];
		local bag,bagnum;
		bag=BANK_CONTAINER;
		bagnum=0;
		if(not rpgoCPstate["Bank"][bagnum] or not rpgoCPstate["Bag"][bag]) then
			bagName="Bag" .. bagnum;
			if(not structBank[bagName]) then structBank[bagName]={}; end
			if(structBank["Contents"]) then structBank["Contents"]=nil; end
			structBank[bagName]=rpgoCP_ScanBagInfo(bag,nil,nil,nil);
			structBank[bagName]["Contents"]=rpgoCP_GetContainerItems("Bank",bag,bagnum);
			rpgoCPstate["Bag"][bag]=1;
		end
		local bag,size,slot,link,numBankSlots;
		numBankSlots=GetNumBankSlots()
		for bagnum=1,NUM_BANKBAGSLOTS do
			bag=bagnum+NUM_BAG_SLOTS;
			bagName="Bag" .. bagnum;
			baglink=GetInventoryItemLink("player",ContainerIDToInventoryID(bag));
			texture=GetInventoryItemTexture("player",ContainerIDToInventoryID(bag));
			if(baglink) then
				if(not rpgoCPstate["Bank"][bagnum] or not rpgoCPstate["Bag"][bag]) then
					rpgoCPtooltip:SetInventoryItem("player",ContainerIDToInventoryID(bag));
					structBank[bagName]=rpgoCP_ScanBagInfo(bag,baglink,texture,"rpgoCPtooltip");
					structBank[bagName]["Contents"]=rpgoCP_GetContainerItems("Bank",bag,bagnum);
					rpgoCPstate["Bag"][bag]=1;
				end
			elseif(bagnum > numBankSlots) then
				rpgoCPstate["Bank"][bagnum]=nil;
				structBank[bagName]=nil;
			end
		end
		rpgoCPstruct["timestamp"]["Bank"]=time();
	elseif(rpgoCPstruct) then
		rpgoCPstruct["Bank"]=nil;
		rpgoCPstate["Bank"]={};
	end
end

function rpgoCP_ScanBagInfo(bagindex,baglink,bagtexture,bagtooltip)
	local itemColor,bagLink,itemID,itemName=rpgo.GetItemInfo(baglink);
	local bagBlock={};
	if(bagindex==BANK_CONTAINER) then
		bagBlock["Name"] = "Bank Contents";
	elseif(bagindex==KEYRING_CONTAINER) then
		bagBlock["Name"] = KEYRING;
	else
		bagBlock["Name"]=GetBagName(bagindex);
		bagBlock["Color"]=rpgo.scanColor(itemColor);
	end
		bagBlock["Slots"]=rpgoCP_GetContainerNumSlots(bagindex);
	bagBlock["Item"]=itemID;
	bagBlock["Icon"]=rpgo.scanIcon(bagtexture);
	if(bagtooltip) then
	bagBlock["Tooltip"]=rpgo.ScanTooltip(bagtooltip,rpgoCPpref["tooltipshtml"]);
	end
	return bagBlock;
end

function rpgoCP_GetContainerItems(container,bagID,bagnum)
	local bagContents={};
	local bagInv,bagSlot;
	bagInv=0;
	bagSlot=rpgoCP_GetContainerNumSlots(bagID);
	if(bagSlot==nil or bagSlot==0) then
		return nil;
	end
	rpgoCPstate[container][bagnum]={};
	for slot=1,bagSlot do
		local itemlink=GetContainerItemLink(bagID,slot);
		local texture,itemCount,locked,quality=GetContainerItemInfo(bagID,slot);
		if(itemlink) then
			if(bagID==BANK_CONTAINER) then
				rpgoCPtooltip:SetInventoryItem("player",BankButtonIDToInvSlotID(slot));
			else
				rpgoCPtooltip:SetBagItem(bagID,slot);
			end
			bagContents[slot]=rpgoCP_ScanItemInfo(itemlink,texture,itemCount,"rpgoCPtooltip");
			bagInv=bagInv+1;
		end
	end
	if(not rpgoCPstate["_bagevent"]) then
		rpgoCPframe:RegisterEvent("PLAYERBANKSLOTS_CHANGED");
		rpgoCPframe:RegisterEvent("BAG_UPDATE");
		rpgoCPstate["_bagevent"]=1;
	end
	rpgoCPstate[container][bagnum]["slot"]=bagSlot;
	rpgoCPstate[container][bagnum]["inv"]=bagInv;
	return bagContents;
end

function rpgoCP_GetContainerNumSlots(bagID)
	if(bagID==KEYRING_CONTAINER) then
		return GetKeyRingSize();
	else
		return GetContainerNumSlots(bagID);
	end
end

function rpgoCP_UnitHasResSickness(unit)
	local index=1;
	if(UnitDebuff(unit,index)) then
		while(UnitDebuff(unit,index)) do
			buffTexture=UnitDebuff(unit,index);
			if(buffTexture == "Interface\\Icons\\Spell_Shadow_DeathScream") then
				return true;
			end
			index=index+1;
		end
	end
	return nil;
end

function rpgoCP_GetTradeSkill()
	if(rpgoCPpref["scan"]["professions"]) then
		local reagentsUnknown=nil;
		local skillLineName,skillLineRank,skillLineMaxRank=GetTradeSkillLine();
		if(not skillLineName) then
			return;
		elseif ( (skillLineName=="") or (skillLineName==UNKNOWN) ) then
			return;
		end
		ExpandTradeSkillSubClass(0);
		if(not rpgoCPstate["Professions"][skillLineName]) then
			rpgoCPstate["Professions"][skillLineName]={};
		end
		if ( not rpgoCPstruct["Professions"] ) then
			rpgoCPstruct["Professions"]={};
		end
		if ( not rpgoCPstruct["timestamp"]["Professions"] ) then
			rpgoCPstruct["timestamp"]["Professions"]={};
		end
		local structProf=rpgoCPstruct["Professions"];

		local numTradeSkills=GetNumTradeSkills();
		local skillHeader=nil;
		if(numTradeSkills>0 and (not rpgoCPstate["Professions"][skillLineName] or numTradeSkills~=rpgoCPstate["Professions"][skillLineName]) ) then
			local TradeSkillTemp=nil;
			if(not structProf[skillLineName]) then
				structProf[skillLineName]={};
			elseif(structProf[skillLineName]) then
				TradeSkillTemp=structProf[skillLineName];
				structProf[skillLineName]={};
			end
			rpgoCPstate["Professions"][skillLineName]=0;
			for itemIndex=1,numTradeSkills,1 do
				local skillName,skillDifficulty,numAvailable,isExpanded=GetTradeSkillInfo(itemIndex);
				local cooldown,reagents;
				if(skillDifficulty=="header" and skillName~="" and isExpanded) then
					skillHeader=skillName;
					structProf[skillLineName][skillHeader]={};
					rpgoCPstate["Professions"][skillLineName]=rpgoCPstate["Professions"][skillLineName]+1;
				elseif(skillDifficulty=="header" and not isExpanded) then
					skillHeader=nil;
				elseif(skillHeader and skillName and skillName~="" ) then
					local skillIcon=GetTradeSkillIcon(itemIndex);
					if(not skillIcon) then skillIcon=""; end
					local Color,_,Link,_=rpgo.GetItemInfo(GetTradeSkillItemLink(itemIndex));
					structProf[skillLineName][skillHeader][skillName]={};
					structProf[skillLineName][skillHeader][skillName]["Icon"]=rpgo.scanIcon(skillIcon);
					structProf[skillLineName][skillHeader][skillName]["Difficulty"]=TradeSkillCode[skillDifficulty];
					structProf[skillLineName][skillHeader][skillName]["Color"]=rpgo.scanColor(Color);
					structProf[skillLineName][skillHeader][skillName]["Item"]=Link;
					if(not MarsProfessionOrganizer_SetTradeSkillItem) then
						rpgoCPtooltip:SetTradeSkillItem(itemIndex);
					end
					if(GetTradeSkillCooldown(itemIndex)) then
						structProf[skillLineName][skillHeader][skillName]["Cooldown"]=GetTradeSkillCooldown(itemIndex);
						structProf[skillLineName][skillHeader][skillName]["DateUTC"]=date("!%Y-%m-%d %H:%M:%S");
						structProf[skillLineName][skillHeader][skillName]["timestamp"]=time();
						rpgoCPtooltip:AddLine(COOLDOWN_REMAINING.." "..SecondsToTime(structProf[skillLineName][skillHeader][skillName]["Cooldown"]));
					elseif(structProf[skillLineName][skillHeader][skillName]["Cooldown"]) then
						structProf[skillLineName][skillHeader][skillName]["Cooldown"]=nil;
					end
					structProf[skillLineName][skillHeader][skillName]["Tooltip"]=rpgo.ScanTooltip("rpgoCPtooltip",rpgoCPpref["tooltipshtml"]);
					local numReagents=GetTradeSkillNumReagents(itemIndex);
					if(rpgoCPpref["reagentfull"]) then
						reagents={};
					else
						reagents="";
					end
					for reagentIndex=1,numReagents,1 do
						local reagentName,reagentTexture,reagentCount,playerReagentCount=GetTradeSkillReagentInfo(itemIndex,reagentIndex);
						if(not reagentTexture) then reagentTexture=""; end
						if(not reagentName) then reagentName=UNKNOWN; reagentsUnknown=1; end
						if(rpgoCPpref["reagentfull"]) then
							local _,itemID,_ = rpgo.GetItemID(GetTradeSkillReagentItemLink(itemIndex,reagentIndex));
							reagents[reagentIndex]={};
							reagents[reagentIndex]["Name"]=reagentName;
							reagents[reagentIndex]["Count"]=reagentCount;
							reagents[reagentIndex]["Item"]=itemID;
						else
							if(reagentIndex==numReagents) then
								reagents=reagents .. reagentName .. " x" .. reagentCount;
							else
								reagents=reagents .. reagentName .. " x" .. reagentCount .. "<br>";
							end
						end
					end
					structProf[skillLineName][skillHeader][skillName]["Reagents"]=reagents;
					rpgoCPstate["Professions"][skillLineName]=rpgoCPstate["Professions"][skillLineName]+1;
				end
			end
			if(rpgoCPstate["Professions"][skillLineName]==0) then
				_skillError=1;
				structProf[skillLineName]=TradeSkillTemp;
				RPGOCP:PrintTitle(skillLineName..rpgo.StringColorize(rpgo.colorRed," not scanned; CP will rescan"));
				rpgoCPTradeSkillFrame:Show();
			elseif(reagentsUnknown) then
				_skillError=1
				structProf[skillLineName]=TradeSkillTemp;
				rpgoCPstate["Professions"][skillLineName]=0;
				RPGOCP:PrintTitle(skillLineName..rpgo.StringColorize(rpgo.colorRed," reagents not scanned; CP will rescan"));
				rpgoCPTradeSkillFrame:Show();
			else
				if(_skillError) then
					RPGOCP:PrintTitle(skillLineName.." rescanned successfully");
					_skillError=nil;
				end
				rpgoCPTradeSkillFrame:Hide();
			end
			TradeSkillTemp=nil;
		end
		rpgoCP_TidyProfessions();
		rpgoCPstruct["timestamp"]["Professions"][skillLineName]=time();
	elseif(rpgoCPstruct) then
		rpgoCPstruct["Professions"]=nil;
		rpgoCPstate["Professions"]={};
	end
end

function rpgoCP_GetCraft()
	if(rpgoCPpref["scan"]["professions"]) then
		local reagentsUnknown=nil;
		local skillLineName,skillLineRank,skillLineMaxRank=GetCraftDisplaySkillLine();
		if(not skillLineName) then
			return;
		elseif ( (skillLineName=="") or (skillLineName==UNKNOWN) ) then
			return;
		end
		local numCrafts=GetNumCrafts();
		if ( not rpgoCPstruct["Professions"] ) then
			rpgoCPstruct["Professions"]={};
		end
		if ( not rpgoCPstruct["timestamp"]["Professions"] ) then
			rpgoCPstruct["timestamp"]["Professions"]={};
		end
		local structProf=rpgoCPstruct["Professions"];
		local skillHeader=nil;
		if(numCrafts>0 and (not rpgoCPstate["Professions"][skillLineName] or numCrafts~=rpgoCPstate["Professions"][skillLineName]) ) then
			local TradeSkillTemp=nil;
			if(not structProf[skillLineName]) then
				structProf[skillLineName]={};
			elseif(structProf[skillLineName]) then
				TradeSkillTemp=structProf[skillLineName];
				structProf[skillLineName]={};
			end
			rpgoCPstate["Professions"][skillLineName]=0;
			skillHeader=skillLineName;
			for itemIndex=1,numCrafts,1 do
				local skillName,craftSubSpellName,skillDifficulty,numAvailable,isExpanded=GetCraftInfo(itemIndex);
				if( skillDifficulty=="header" and skillName~="" ) then
					skillHeader=skillName;
					structProf[skillLineName][skillHeader]={};
					rpgoCPstate["Professions"][skillLineName]=rpgoCPstate["Professions"][skillLineName]+1;
				elseif( skillHeader and skillName and skillName~="" ) then
					if(not structProf[skillLineName][skillHeader]) then
						structProf[skillLineName][skillHeader]={};
					end
					local skillIcon=GetCraftIcon(itemIndex);
					if(not skillIcon) then skillIcon=""; end
					local Color,_,Link,_=rpgo.GetItemInfo(GetTradeSkillItemLink(itemIndex));
					local numReagents=GetCraftNumReagents(itemIndex);
					local reagents="";
					structProf[skillLineName][skillHeader][skillName]={};
					structProf[skillLineName][skillHeader][skillName]["Icon"]=rpgo.scanIcon(skillIcon);
					structProf[skillLineName][skillHeader][skillName]["Difficulty"]=TradeSkillCode[skillDifficulty];
					structProf[skillLineName][skillHeader][skillName]["Color"]=rpgo.scanColor(Color);
					structProf[skillLineName][skillHeader][skillName]["Item"]=Link;
					structProf[skillLineName][skillHeader][skillName]["Tooltip"]=GetCraftDescription(itemIndex);
					if(rpgoCPpref["reagentfull"]) then
						reagents={};
					else
						reagents="";
					end
					for reagentIndex=1,numReagents,1 do
						local reagentName,reagentTexture,reagentCount,playerReagentCount=GetCraftReagentInfo(itemIndex,reagentIndex);
						if(not reagentTexture) then reagentTexture=""; end
						if(not reagentName) then reagentName=UNKNOWN; reagentsUnknown=1; end
						if(rpgoCPpref["reagentfull"]) then
							local _,itemID,_ = rpgo.GetItemID(GetCraftReagentItemLink(itemIndex,reagentIndex));
							reagents[reagentIndex]={};
							reagents[reagentIndex]["Name"]=reagentName;
							reagents[reagentIndex]["Count"]=reagentCount;
							reagents[reagentIndex]["itemID"]=itemID;
						else
							if(reagentIndex==numReagents) then
								reagents=reagents .. reagentName .. " x" .. reagentCount;
							else
								reagents=reagents .. reagentName .. " x" .. reagentCount .. "<br>";
							end
						end
					end
					structProf[skillLineName][skillHeader][skillName]["Reagents"]=reagents;
					rpgoCPstate["Professions"][skillLineName]=rpgoCPstate["Professions"][skillLineName]+1;
				end
			end
			if(rpgoCPstate["Professions"][skillLineName]==0) then
				_skillError=1;
				structProf[skillLineName]=TradeSkillTemp;
				RPGOCP:PrintTitle(skillLineName..rpgo.StringColorize(rpgo.colorRed," not scanned; CP will rescan"));
				rpgoCPCraftFrame:Show();
			elseif(reagentsUnknown) then
				_skillError=1;
				structProf[skillLineName]=TradeSkillTemp;
				rpgoCPstate["Professions"][skillLineName]=0;
				RPGOCP:PrintTitle(skillLineName..rpgo.StringColorize(rpgo.colorRed," reagents not scanned; CP will rescan"));
				rpgoCPCraftFrame:Show();
			else
				if(_skillError) then
					RPGOCP:PrintTitle(skillLineName.." rescanned successfully");
					_skillError=nil;
				end
				rpgoCPCraftFrame:Hide();
			end
			TradeSkillTemp=nil;
		end
		rpgoCP_TidyProfessions();
		rpgoCPstruct["timestamp"]["Professions"][skillLineName]=time();
	elseif(rpgoCPstruct) then
		rpgoCPstruct["Professions"]=nil;
		rpgoCPstate["Professions"]={};
	end
end

function rpgoCP_TidyProfessions()
	if(rpgoCPstate["_loaded"]) then
		for skillName in pairs(rpgoCPstruct["Professions"]) do
			if(not rpgoCPstate["_skills"][skillName]) then
				rpgoCPstruct["Professions"][skillName]=nil;
			end
		end
	end
end

function rpgoCP_GetSpellBook()
	if(rpgoCPpref["scan"]["spells"]) then
		if ( not rpgoCPstruct["SpellBook"] ) then
			rpgoCPstruct["SpellBook"]={};
		end
		local structSpell=rpgoCPstruct["SpellBook"];
		for spelltab=1,GetNumSpellTabs(),1 do
			local spelltabname,spelltabtexture,offset,numSpells=GetSpellTabInfo(spelltab);
			if(not rpgoCPstate["SpellBook"][spelltabname] or rpgoCPstate["SpellBook"][spelltabname]~=numSpells) then

				if ( not structSpell[spelltabname] ) then
					structSpell[spelltabname]={};
				end
				structSpell[spelltabname]["Icon"]=rpgo.scanIcon(spelltabtexture);
				structSpell[spelltabname]["Spells"]={};
				rpgoCPstate["SpellBook"][spelltabname]=0;
				for spellId=offset + 1,numSpells + offset,1 do
					spellName,spellRank=GetSpellName( spellId,BOOKTYPE_SPELL );
					spellTexture=GetSpellTexture( spellId,spelltab );
					if ( not structSpell[spelltabname]["Spells"][spellName] ) then
						structSpell[spelltabname]["Spells"][spellName]={};
					end
					structSpell[spelltabname]["Spells"][spellName]["Rank"]=spellRank;
					structSpell[spelltabname]["Spells"][spellName]["Icon"]=rpgo.scanIcon(spellTexture);
					rpgoCPtooltip:SetSpell(spellId,BOOKTYPE_SPELL);
					structSpell[spelltabname]["Spells"][spellName]["Tooltip"]=rpgo.ScanTooltip("rpgoCPtooltip",rpgoCPpref["tooltipshtml"]);
					rpgoCPstate["SpellBook"][spelltabname]=rpgoCPstate["SpellBook"][spelltabname]+1;
				end
				structSpell[spelltabname]["Count"]=numSpells;

			end
			rpgoCPstruct["timestamp"]["SpellBook"]=time();
		end
	elseif(rpgoCPstruct) then
		rpgoCPstruct["SpellBook"]=nil;
		rpgoCPstate["SpellBook"]={};
	end
end

function rpgoCP_ScanPetInit(name)
	if(name) then
		if(not rpgoCPstruct["Pets"]) then
			rpgoCPstruct["Pets"]={};
		end
		if(not rpgoCPstruct["Pets"][name]) then
			rpgoCPstruct["Pets"][name]={};
		end
		if(not rpgoCPstruct["timestamp"]["Pets"]) then
			rpgoCPstruct["timestamp"]["Pets"]={};
		end
	end
end

function rpgoCP_ScanPetStable()
	if(rpgoCPpref["scan"]["pet"] and (rpgoCPstate["_class"]=="HUNTER" and UnitLevel("player")>9)) then
		local structPets;
		for petIndex=0,GetNumStableSlots(),1 do
			local petIcon,petName,petLevel,petType,petLoyalty=GetStablePetInfo(petIndex);
			if(petName) then
				rpgoCP_ScanPetInit(petName);
				structPets=rpgoCPstruct["Pets"];
				structPets[petName]["Slot"]=petIndex;
				structPets[petName]["Icon"]=rpgo.scanIcon(petIcon);
				structPets[petName]["Name"]=petName;
				structPets[petName]["Level"]=petLevel;
				structPets[petName]["Type"]=petType;
				structPets[petName]["Loyalty"]=petLoyalty;
				rpgoCPstruct["timestamp"]["Pets"][petName]=time();
			end
			rpgoCPstate["Stable"][petIndex]=petName;
		end
		rpgoCP_ScanPetInfo();
	elseif(rpgoCPstruct) then
		rpgoCPstruct["Pets"]=nil;
		rpgoCPstate["Pets"]={};
	end
end

function rpgoCP_ScanPetInfo()
	if(rpgoCPpref["scan"]["pet"]) then
		if(HasPetUI()) then
			petName=UnitName("pet");
			rpgoCP_ScanPetInit(petName);
			local structPet=rpgoCPstruct["Pets"][petName];
			structPet["Name"]=petName;
			structPet["Type"]=UnitCreatureFamily("pet");
			structPet["TalentPoints"],structPet["TalentPointsUsed"]=GetPetTrainingPoints();
			local currXP,nextXP=GetPetExperience();
			structPet["Experience"]=strjoin(":", currXP,nextXP);

			rpgoCP_GetStats(structPet,"pet");
			rpgoCP_GetBuffs(structPet,"pet");
			rpgoCP_GetPetSpellBook();
			rpgoCPstate["Pets"][petName]=1;
			rpgoCPstruct["timestamp"]["Pets"][petName]=time();
		end
	elseif(rpgoCPstruct) then
		rpgoCPstruct["Pets"]=nil;
		rpgoCPstate["Pets"]={};
	end
end

function rpgoCP_GetPetSpellBook()
	if(rpgoCPpref["scan"]["spells"]) then
		numSpells,_=HasPetSpells();
		petName=UnitName("pet");
		if(numSpells) then
			rpgoCP_ScanPetInit(petName);
			if (not rpgoCPstruct["Pets"][petName]["SpellBook"]) then
				rpgoCPstruct["Pets"][petName]["SpellBook"]={};
			end
			local structPetSpell=rpgoCPstruct["Pets"][petName]["SpellBook"];
			for petSpellId=1,numSpells,1 do
				local spellName,spellRank=GetSpellName(petSpellId,BOOKTYPE_PET);
				local spellTexture=GetSpellTexture(petSpellId,BOOKTYPE_PET);
				if (spellName==nil) then break; end
				if (not structPetSpell["Spells"]) then
					structPetSpell["Spells"]={};
				end
				structPetSpell["Spells"][spellName]={};
				structPetSpell["Spells"][spellName]["Rank"]=spellRank;
				structPetSpell["Spells"][spellName]["Icon"]=rpgo.scanIcon(spellTexture);
				structPetSpell["Count"]=petSpellId;
			end
			rpgoCPstate["PetSpell"][petName]=numSpells;
		end
	end
end

function rpgoCP_TradeTimer(event,arg1)
	local skill;
	if(not rpgoCPstate["ProfTimer"]) then rpgoCPstate["ProfTimer"]={}; end
	if(event=="CRAFT_UPDATE") then
		skill=GetCraftDisplaySkillLine();
	elseif(event=="TRADE_SKILL_UPDATE") then
		skill=GetTradeSkillLine();
	end
	if(skill and skill~=UNKNOWN) then
		if( (not arg1) or (not rpgoCPstate["ProfTimer"][skill]) ) then
			rpgoCPstate["ProfTimer"][skill]=0;
		elseif(tonumber(arg1)) then
			rpgoCPstate["ProfTimer"][skill]=rpgoCPstate["ProfTimer"][skill]+arg1;
		end
		if(rpgoCPstate["ProfTimer"][skill] > 1.5) then
			rpgoCPstate["ProfTimer"][skill]=nil;
			rpgoCP_EventHandler(string.gsub(event,'_UPDATE','_SHOW'),arg1);
		end
	else
		if(event=="CRAFT_UPDATE") then
			rpgoCPCraftFrame:Hide();
		elseif(event=="TRADE_SKILL_UPDATE") then
			rpgoCPTradeSkillFrame:Hide();
		end
	end
end

function rpgoCP_UpdatePlayed(arg1,arg2)
	if(arg1 and arg2) then timePlayed=arg1; timeLevelPlayed=arg2; end
	if(rpgoCPstate["_loaded"] and rpgoCPstruct) then
		rpgoCPstruct["TimePlayed"]=timePlayed;
		rpgoCPstruct["TimeLevelPlayed"]=timeLevelPlayed;
	end
end

function rpgoCP_UpdateZone()
	rpgoCPstruct["Zone"]=GetZoneText();
	rpgoCPstruct["SubZone"]=GetSubZoneText();
end

function rpgoCP_UpdateBagScan(bagID)
	if(bagID~=nil and rpgoCPstate["Bag"][bagID]) then
		rpgoCPstate["Bag"][bagID]=nil;
		if(bagID==BANK_CONTAINER) then
			rpgoCPframe:UnregisterEvent("PLAYERBANKSLOTS_CHANGED");
		elseif(table.maxn(rpgoCPstate["Bag"])==0) then
			rpgoCPstate["_bagevent"]=nil;
			rpgoCPframe:UnregisterEvent("BAG_UPDATE");
		end
	end
end

function rpgoCP_UpdateEqScan(unit)
	if(unit=="player" and rpgoCPstate["eq"]) then
		rpgoCPstate["eq"]=nil;
		rpgoCPframe:UnregisterEvent("UNIT_INVENTORY_CHANGED");
	end
end

function rpgoCP_GetProfileDate(server,char)
	local thisProfile,thisEpoch;
	if(myProfile and myProfile[server] and myProfile[server]["Character"] and myProfile[server]["Character"][char]) then
		thisProfile=myProfile[server]["Character"][char];
		if(thisProfile["timestamp"] and thisProfile["timestamp"]["init"] and thisProfile["timestamp"]["init"]["TimeStamp"]) then
			thisEpoch=thisProfile["timestamp"]["init"]["TimeStamp"];
		elseif(thisProfile["timestamp"] and thisProfile["timestamp"]["init"] and thisProfile["timestamp"]["init"]["Date"]) then
			thisEpoch=thisProfile["timestamp"]["init"]["Date"];
		end
		if(thisEpoch) then
			return date("%Y-%m-%d",thisEpoch);
		end
	end
	return "";
end

--[[## general rpgo functions: unit
--######################################################--]]
--[function] arg1:unit
function rpgo_UnitSexString(arg1)
	local UnitSexLabel={UNKNOWN,MALE,FEMALE};
	local unitSexID=UnitSex(arg1);
	return UnitSexLabel[unitSexID],mod(unitSexID,2);
end

--[function] rpgo_GetMoney()
function rpgo_GetMoney()
	local money=GetMoney();
	local gold,silver,copper;
	local CopperPerGold=COPPER_PER_SILVER * SILVER_PER_GOLD;
	gold=floor(money/CopperPerGold);
		money=mod(money,CopperPerGold);
	silver=floor(money/COPPER_PER_SILVER);
		money=mod(money,CopperPerGold);
	copper=mod(money,COPPER_PER_SILVER);
	return gold,silver,copper;
end

--[[## general rpgo functions: item
--######################################################--]]
--[function] itemlink,itemtexture,itemcount,itemtooltip
function rpgoCP_ScanItemInfo(itemlink,itemtexture,itemcount,itemtooltip)
	local itemColor,itemLink,itemID,itemName
	if(itemlink) then
		itemColor,itemLink,itemID,itemName=rpgo.GetItemInfo(itemlink);
		local itemBlock={};
		if(rpgoCPpref["fixquantity"] and itemcount and itemcount<2) then itemcount=nil end
		if(not itemName or not itemColor) then
			itemName,itemColor=rpgo.GetItemInfoTT(itemtooltip);
		end
		itemBlock["Name"]=itemName;
		itemBlock["Item"]=itemID;
		itemBlock["Color"]=rpgo.scanColor(itemColor);
		itemBlock["Quantity"]=itemcount;
		itemBlock["Icon"]=rpgo.scanIcon(itemtexture);
		itemBlock["Tooltip"]=rpgo.ScanTooltip(itemtooltip,rpgoCPpref["tooltipshtml"]);
		if(rpgoCPpref["fixtooltip"] and itemBlock["Name"]==itemBlock["Tooltip"]) then
			itemBlock["Tooltip"]=nil end
		return itemBlock;
	end
	return nil;
end

function rpgo.isBC()
	local vVersion,vMajor,vMinor=rpgo.version();
	if(vVersion==2 and vMinor>=2) then return true
	else return false end
end

--[[########################################################
--## object functions
--######################################################--]]
function RPGOCP:liteScan(event)
	if(event=="RPGOCP_EXPORT") then return false; end
	if(not self.state["_loaded"]) then return false; end
	return rpgo.liteScan(self);
end
function RPGOCP:PrintTitle(...)
	return rpgo.PrintTitle(self,...);
end
function RPGOCP:PrintUsage()
	return rpgo.PrintUsage(self);
end
function RPGOCP:PrintDebug(...)
	return rpgo.PrintDebug(self,...);
end

function RPGOCP:RegisterEvents(flagMode)
	if(not flagMode) then flagMode = (self.pref.enabled); end
	self:PrintDebug("RegisterEvents ("..rpgo.PrefColorize(flagMode)..") ");
	return rpgo.RegisterEvents(self,flagMode);
end
rpgoCP_Init();
