--[[
ArenaLog3
Cirno of <Reprise> Sargeras-Alliance
Zeta0978@yahoo.com

Maintains a database of played arena games.

-- Bugs
- Doesn't seem to find enemy team
- Adding data twice for some reason.


--]]
local strsplit = strsplit


BINDING_HEADER_ARENALOG = "ArenaLog";
BINDING_NAME_TOGGLE_ARENALOG = "Toggle ArenaLog Browser";

ArenaLog = { };
ArenaLog_Data = { };
ArenaLog_Options = { 
    ["announceRatio"] = 1,
};

ArenaLog.classTable = {
	["WARRIOR"] = 1,
	["ROGUE"] = 2,
	["HUNTER"] = 3,
	["PALADIN"] = 4,
	["SHAMAN"] = 5,
	["DRUID"] = 6,
	["WARLOCK"] = 7,
	["MAGE"] = 8,
	["PRIEST"] = 9,
	["DEATHKNIGHT"] = 10,
};

ArenaLog.announced = false;
ArenaLog.matchInProgress = false;

function ArenaLog:ResetData(bracket)
    local b = {
        ["2v2"] = 2,
        ["3v3"] = 3,
        ["5v5"] = 5,
    }
    
    if bracket == '' then
        bracket = 'All';
        ArenaLog_Data = { };        --Set table back to nothing.
    else
        local t = ArenaLog:GetTeamFromBracket(b[bracket]);
        ArenaLog_Data[t] = nil;
    end
    
    
        
    ArenaLog:UpdatePlayerTeams();
    DEFAULT_CHAT_FRAME:AddMessage(ARENALOG_PREFIX.." - "..ARENALOG_RESET.." ["..bracket.."]"); --Inform user
end

SlashCmdList["ARENALOG"] = function(msg)
    local args = {strsplit(" ",string.lower(msg))};
    
    -- Reset
    if (args[1] == "reset") then
        local bracket = '';
        if args[2] ~= nil then
            bracket = args[2];
        end
        ArenaLog:ResetData(bracket);
        ArenaLog.Browser:ResetCache();
    -- Recent game history
    elseif (args[1] == "history") then
        ArenaLog:UpdatePlayerTeams();
        
        local bracket, count;
        if (args[2] == "2v2") then 
            bracket = 2;
        elseif (args[2] == "3v3") then 
            bracket = 3;
        elseif (args[2] == "5v5") then 
            bracket = 5;
        else
            DEFAULT_CHAT_FRAME:AddMessage("|c00ff0000ArenaLog|r - Usage: /al history [bracket] [# of games]");
            DEFAULT_CHAT_FRAME:AddMessage(" - "..ARENALOG_BRACKET_VALUES..": 2v2, 3v3, 5v5");            
        end
        
        if (args[3] == nil) then
            args[3] = 10;
        end
        args[3] = args[3] + 0; --Convert to numeric.
        if (args[3] < 0) then args[3] = 0 end; --No negatives
        if (args[3] > 50) then args[3] = 50 end; --Constrain to 50
        
        count = args[3];
        
        if (bracket ~= nil) then
            ArenaLog:PrintGameHistory(bracket, count);
        end
    elseif (args[1] == "browser") then
        ArenaLog:ShowBrowser();
    else
        DEFAULT_CHAT_FRAME:AddMessage(ARENALOG_PREFIX.." - "..ARENALOG_COMMANDS..":");
        DEFAULT_CHAT_FRAME:AddMessage("    |c00ff0000--|r reset [bracket] (None = clear all brackets)");
        DEFAULT_CHAT_FRAME:AddMessage("    |c00ff0000--|r history [bracket] [#]");
        DEFAULT_CHAT_FRAME:AddMessage("    |c00ff0000--|r browser");
    end
end



SLASH_ARENALOG1 = "/al";
SLASH_ARENALOG2 = "/arenalog";

function ArenaLog:OnEvent(event, arg1, arg2, arg3)
    if (event == "UPDATE_BATTLEFIELD_STATUS") then
        local winner = GetBattlefieldWinner();
        if (winner ~= nil and ArenaLog.matchInProgress == true) then
            --There has been a concluded game
            ArenaLog:AddMatch();
            ArenaLog.announced = false;
            ArenaLog.matchInProgress = false;
            ArenaLog.Browser:ResetCache(); --Force browser to update on next show
        end
    elseif (event == "UNIT_TARGET" and strsub(arg1, 1, 5) == "arena" and ArenaLog.announced == false and ArenaLog_Options.announceRatio and ArenaLog.matchInProgress) then
        ArenaLog:AnnounceRatio(arg1);
    elseif (event == "ZONE_CHANGED_NEW_AREA") then
        local zonetype = select(2, IsInInstance());            
        if (zonetype == "arena") then
            ArenaLog:UpdatePlayerTeams();
            ArenaLog.matchInProgress = true;
        end
    end
end

local frame = CreateFrame("Frame", "ArenaLog_Frame");
frame:SetScript("OnEvent", ArenaLog.OnEvent);
frame:RegisterEvent("UPDATE_BATTLEFIELD_STATUS");
frame:RegisterEvent("UPDATE_BATTLEFIELD_SCORE");
--frame:RegisterEvent("ARENA_TEAM_UPDATE");
frame:RegisterEvent("ZONE_CHANGED_NEW_AREA");
frame:RegisterEvent("UNIT_TARGET");

function ArenaLog:AddMatch()    
    winner = GetBattlefieldWinner();
    IsArena, IsRegisteredGame = IsActiveBattlefieldArena();
    if(IsArena == 1 and IsRegisteredGame == 1) then --This was an arena game. 
        --Get team information for each team
        local teams = { };
        local teamName, oldTeamRating, newTeamRating, teamSkill;
        for i = 0, 1 do
            teamName, oldTeamRating, newTeamRating, teamSkill = GetBattlefieldTeamInfo(i); --0 or 1
            teams[i] = { ["teamName"] = teamName, ["oldTeamRating"] = oldTeamRating, ["newTeamRating"] = newTeamRating, ["composition"] = { }, ["teamSkill"] = teamSkill, ["players"] = { }, ["realm"] = "" };
        end
        
        if (teams[0]["teamName"] == "" or teams[1]["teamName"] == "") then return end;

        local playerTeamIndex; -- Keeps track of which team is the player's
        
        --Gather score information for game
        local scoredata = { }; --Contains a score table entry for each player
        local numplayers = GetNumBattlefieldScores();
        
        local name, realm, killingBlows, honorKills, deaths, honorGained, faction, rank, race, class, filename, damageDone, healingDone;
        for i=1, numplayers do
            local player_entry = { };
            name, killingBlows, honorKills, deaths, honorGained, faction, rank, race, class, filename, damageDone, healingDone = GetBattlefieldScore(i);

            -- will need to parse server from name field
            local oTemp = {strsplit("-",name)};                
            if(oTemp~=nil) then
                if(table.getn(oTemp) == 2) then
                    name, realm = oTemp[1], oTemp[2];	
                else
                    realm = GetRealmName();
                end                
            else
                realm = GetRealmName();
            end
            
            teams[faction]["realm"] = realm;           

            if(name == UnitName("player")) then playerTeamIndex = faction; end
            
            --Build table
            player_entry["name"] = name;
            player_entry["realm"] = realm;
            player_entry["killingBlows"] = killingBlows;
            player_entry["honorKills"] = honorKills;
            player_entry["honorGained"] = honorGained;
            player_entry["faction"] = faction;
            player_entry["rank"] = rank;
            player_entry["race"] = race;
            player_entry["class"] = class;
            player_entry["filename"] = filename;
            player_entry["damageDone"] = damageDone;
            player_entry["healingDone"] = healingDone;
            player_entry["teamName"] = teams[faction]["teamName"];
            
            table.insert(teams[faction]["composition"], filename);

            --Add new players to team info
            teams[faction]["players"][name] = {
                ["race"] = race, 
                ["class"] = class, 
                ["filename"] = filename
            };
            
            --Insert 
            table.insert(scoredata, player_entry);
        end
        
        -- Get match details
        local match = { };
        match["winner"] = (winner == playerTeamIndex);
        match["date"] = date("%Y-%m-%d %X");
        match["timestamp"] = time();
        match["duration"] = floor(GetBattlefieldInstanceRunTime() / 1000); --Returns running time of game in seconds
        match["zone"] = GetZoneText();        
        match["scores"] = scoredata;
        match["playerTeamRatingOld"] = teams[playerTeamIndex]["oldTeamRating"];
        match["playerTeamRatingNew"] = teams[playerTeamIndex]["newTeamRating"];
        match["enemyTeamRatingOld"] = teams[1 - playerTeamIndex]["oldTeamRating"];
        match["enemyTeamRatingNew"] = teams[1 - playerTeamIndex]["newTeamRating"];
        match["enemyTeamName"] = teams[1 - playerTeamIndex]["teamName"];
        match["enemyTeamRealm"] = teams[1 - playerTeamIndex]["realm"];
        match["enemyTeamSkill"] = teams[1 - playerTeamIndex]["teamSkill"];
        match["playerTeamSkill"] = teams[playerTeamIndex]["teamSkill"];
        
        -- We have all data.  Prepare tables for manipulation.
        local enemyTeamNameFull = teams[1 - playerTeamIndex]["teamName"].." - "..teams[1 - playerTeamIndex]["realm"];
        local compString = ArenaLog.GetCompositionString(teams[1 - playerTeamIndex]["composition"]);
        ArenaLog.InitPlayerTeam(teams[playerTeamIndex]["teamName"]);
        ArenaLog.InitEnemyTeam(teams[playerTeamIndex]["teamName"], enemyTeamNameFull);
        ArenaLog.InitComposition(teams[playerTeamIndex]["teamName"], compString);
        local playerTeamData = ArenaLog_Data[teams[playerTeamIndex]["teamName"]];
        local enemyTeamData = playerTeamData["enemyTeams"][enemyTeamNameFull];        
        local compData = playerTeamData["enemyComps"][compString];
        
        --Modify win ratios
        if (match["winner"] == true) then
            playerTeamData["wins"] = playerTeamData["wins"] + 1;
            enemyTeamData["wins"] = enemyTeamData["wins"] + 1;
            compData["wins"] = compData["wins"] + 1;
        else
            playerTeamData["losses"] = playerTeamData["losses"] + 1;
            enemyTeamData["losses"] = enemyTeamData["losses"] + 1;
            compData["losses"] = compData["losses"] + 1;
        end
        
        --Modify rating gain
        enemyTeamData["ratingChange"] = enemyTeamData["ratingChange"] + ( match["playerTeamRatingNew"] - match["playerTeamRatingOld"] );
        compData["ratingChange"] = compData["ratingChange"] + ( match["playerTeamRatingNew"] - match["playerTeamRatingOld"] );
        
        --Modify last played date
        enemyTeamData["lastPlayed"] = match["timestamp"];
        
        --Update enemy team player info
        for k, v in pairs(teams[1 - playerTeamIndex]["players"]) do
            enemyTeamData["players"][k] = v;
        end
        
        --Add match info
        table.insert(playerTeamData["games"], match);        
        
        ---- Reinsert tables into DB
        playerTeamData["enemyTeams"][enemyTeamNameFull] = enemyTeamData;
        playerTeamData["enemyComps"][compString] = compData;
        ArenaLog_Data[teams[playerTeamIndex]["teamName"]] = playerTeamData;
        
        DEFAULT_CHAT_FRAME:AddMessage(ARENALOG_PREFIX.." - "..ARENALOG_DATA_ADDED); --Inform user
    end    
end

function ArenaLog.GetCompositionString(comp)
    table.sort(comp);
    local s = "";
    for i, v in pairs(comp) do
        s = s .. ArenaLog.classTable[v] .. " ";
    end
    return strtrim(s);
end

function ArenaLog.InitPlayerTeam(teamName)
    if (ArenaLog_Data[teamName] == nil) then
        ArenaLog_Data[teamName] = {
            ["wins"] = 0,
            ["losses"] = 0,
            ["enemyTeams"] = { },
            ["enemyComps"] = { },
            ["games"] = { },
        };
    end
end

function ArenaLog.InitEnemyTeam(playerTeam, enemyTeam)
    if (ArenaLog_Data[playerTeam]["enemyTeams"][enemyTeam] == nil) then
        ArenaLog_Data[playerTeam]["enemyTeams"][enemyTeam] = {
            ["wins"] = 0,
            ["losses"] = 0,
            ["players"] = { },
            ["ratingChange"] = 0,
        };
    end
end

function ArenaLog.InitComposition(playerTeam, compString)
    if (ArenaLog_Data[playerTeam]["enemyComps"][compString] == nil) then
        ArenaLog_Data[playerTeam]["enemyComps"][compString] = {
            ["wins"] = 0,
            ["losses"] = 0,
            ["ratingChange"] = 0,
        };
    end
end

--This scans for and deletes any teams the player is not a part of, and initializes any teams the player is now on.
function ArenaLog:UpdatePlayerTeams()
    local teamName;
    local registeredTeams = { };
    for i = 1, 3, 1 do
        teamName = GetArenaTeam(i);
        if (teamName ~= nil) then
            ArenaLog.InitPlayerTeam(teamName);
            registeredTeams[teamName] = teamName;
        end
    end
    
    for k, v in pairs(ArenaLog_Data) do
        if (registeredTeams[k] == nil) then --There's a team in the db that is no longer registered.  Delete it.
            ArenaLog_Data[k] = nil;
        end
    end
    return registeredTeams;
end

function ArenaLog:GetRecentGames(teamName, count)
    if (count == nil) then 
        count = 10;
    end
    local games = ArenaLog_Data[teamName]["games"];
    local gameHistory = { };
    table.sort(games, function(a, b) return (a["timestamp"]>b["timestamp"]) end);
    local i = 1;
    for _, v in pairs(games) do
        gameHistory[i] = v;
        i = i + 1;
        if (i > count) then
            break;
        end
    end
    return gameHistory;
end

function ArenaLog:AnnounceRatio(unit)
    -- find out the current player's team
    local teamIndex, teamSize, status;
	for i=1, MAX_BATTLEFIELD_QUEUES do
		status, _, _, _, _, teamSize = GetBattlefieldStatus(i);
		if (status == "active" and teamSize > 0) then
			break;
		end
	end
    
    local teamName = ArenaLog:GetTeamFromBracket(teamSize);
    
    -- Find enemy team name / realm
    local name, realm = UnitName(unit);
    local enemyTeam = ArenaLog.PlayerSearch(teamName, name, realm);

    -- Found enemy team, or not.
    if (enemyTeam ~= nil) then
        local wins = ArenaLog_Data[teamName]["enemyTeams"][enemyTeam]["wins"];
        local losses = ArenaLog_Data[teamName]["enemyTeams"][enemyTeam]["losses"];
        SendChatMessage(ARENALOG_ENEMY_TEAM..": "..enemyTeam.." ("..wins.."/"..losses..")", "PARTY");
    else
        SendChatMessage(ARENALOG_NEW_ENEMY_TEAM, "PARTY");
    end
    ArenaLog.announced = true;
end

-- Finds the team that a given player is on.  Returns "TeamName - Realm"
function ArenaLog.PlayerSearch(playerTeam, enemyName, realm)
    if realm == nil then
        realm = GetRealmName();
    end
    --DEFAULT_CHAT_FRAME:AddMessage("Searching for "..enemyName.." from realm: "..realm.." ["..playerTeam.."]");
    local enemyTeam;
    for etn, v in pairs(ArenaLog_Data[playerTeam]["enemyTeams"]) do
        for epn,_ in pairs(v["players"]) do            
            if (epn == enemyName) then                
                -- Check for realm.  Chances are staggeringly low for a collision but check anyway.
                local nTemp = { strsplit("-",etn) };                
                if (strtrim(nTemp[2]) == realm) then -- We found the match for the correct server.
                    --DEFAULT_CHAT_FRAME:AddMessage("Found match: "..etn);
                    enemyTeam = etn;
                    break;
                end
            end
        end
        if (enemyTeam ~= nil) then
            break;
        end
    end
    return enemyTeam;
end

function ArenaLog:GetTeamFromBracket(bracket)
    local teamName, teamSize;
    for i=1, 3, 1 do
        teamName, teamSize = GetArenaTeam(i);
        if (teamSize == bracket) then
            break;
        end
    end
    return teamName;
end

-- Gets an array representing the classes of an enemy team
function ArenaLog.GetTeamCompArray(playerTeam, enemyTeam, enemyRealm)
    local enemyTeamFull = enemyTeam.." - "..enemyRealm;
    local teamInfo = ArenaLog_Data[playerTeam]["enemyTeams"][enemyTeamFull]["players"];
    local results = {};
    for k, v in pairs(teamInfo) do
        table.insert(results, v["filename"]);
    end
    sort(results);
    return results;
end

----------------------------
-- Blizzard Options Panel --
----------------------------
function ArenaLog_Options_Panel_OnLoad(panel)
    panel.name = "ArenaLog3";
    panel.okay = function (self) ArenaLog_Options_Panel_Save(); end;
    panel.default = function (self) ArenaLog_Options_Panel_OnShow();  end;

    ArenaLog_Options_Form();

    InterfaceOptions_AddCategory(panel);
end

function ArenaLog_Options_Panel_OnShow()
    ArenaLog_Options_PanelAnnounceRatioCB:SetChecked(ArenaLog_Options.announceRatio);
end

function ArenaLog_Options_Panel_Save()
    if (ArenaLog_Options_PanelAnnounceRatioCB:GetChecked()) then 
        ArenaLog_Options.announceRatio = 1;
    else 
        ArenaLog_Options.announceRatio = 0;
    end    
end

function ArenaLog_Options_Form()
    local title = ArenaLog_Options_Panel:CreateFontString(nil, 'ARTWORK', 'GameFontNormalLarge');
	title:SetPoint('TOPLEFT', 16, -16)
    title:SetText("ArenaLog");

    ArenaLog_Options_PanelAnnounceRatioCB = CreateFrame("CheckButton", "ArenaLog_Options_PanelAnnounceRatioCB", ArenaLog_Options_Panel, "OptionsCheckButtonTemplate");
    ArenaLog_Options_PanelAnnounceRatioCB:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -16);
    ArenaLog_Options_PanelAnnounceRatioCBText:SetText(ARENALOG_ANNOUNCE_RATIO);
    ArenaLog_Options_PanelAnnounceRatioCBText:SetFontObject("GameFontWhite");
end       

---------------------
-- Slash responses --
---------------------
function ArenaLog:PrintGameHistory(bracket, count)
    local teamName = ArenaLog:GetTeamFromBracket(bracket);
    local gameHistory = ArenaLog:GetRecentGames(teamName, count);
    if (gameHistory == nil or next(gameHistory) == nil) then
        DEFAULT_CHAT_FRAME:AddMessage(ARENALOG_PREFIX.." - |c0000ffff"..teamName.."|r : "..ARENALOG_NO_GAMES_FOUND);
    else
        DEFAULT_CHAT_FRAME:AddMessage(ARENALOG_PREFIX.." - |c0000ffff"..teamName.."|r : "..format(ARENALOG_SHOW_LAST_GAMES, count));

        for i, v in ipairs(gameHistory) do 
            local ratingChange, ratingColor = v["playerTeamRatingNew"] - v["playerTeamRatingOld"];
            if (ratingChange >= 0) then
                ratingColor = "|c0000ff00";
            else
                ratingColor = "|c00ff0000";
            end
            local win, winColor;
            if (v["winner"] == true) then
                win = "win ";
                winColor = "|c0000ff00";            
            else
                win = "loss";
                winColor = "|c00ff0000";
            end
            
            -- Line up double digits
            if (string.len(i) == 1) then
                i = " "..i;
            end
            
            local str = i..": "..winColor..win.."|r - "..v["enemyTeamName"].." ["..ratingColor..ratingChange.."|r]";
            DEFAULT_CHAT_FRAME:AddMessage(str);
        end
    end
end
    

