Quantcast
local GridStatus = Grid:GetModule("GridStatus");
local GridRoster = Grid:GetModule("GridRoster");
local GridFrame = Grid:GetModule("GridFrame");
--
local MapData = LibStub("LibMapData-1.0");

local APP = "GridStatusSmartHealing";
GridStatusSmartHealing = Grid:NewStatusModule(APP);
GridStatusSmartHealing.menuName = "Smart Healing - by Tirds";

-- Timer used to measure time between updates
local Timer = 0;
-- Blank frame used to run OnUpdate
local BlankFrame = CreateFrame("Frame");
local NumEnabled = 0;

-- TODO make language files
local function L(text)
    return text;
end

GridStatusSmartHealing.defaultDB =
{
    debug = false,
    cycle = 0.2,
}
GridStatusSmartHealing.extraOptions =
{
    ["frequency"] =
    {
        order = 101,
        type = "range",
        name = "Refresh time",
        desc = "Secounds between each refresh",
        width = "full",
        min = 0.01,
        max = 2,
        step = 0.01,
        get = function()
            return GridStatusSmartHealing.db.profile.cycle;
        end,
        set = function(info, v)
            GridStatusSmartHealing.db.profile.cycle = v;
        end
    },
    --[[
    ["test"] =
    {
        name = "test",
        order = 102,
        type = "toggle",
        width = "full",
        get = function()
            return false;
        end,
        set = function()
            print(GridStatusSmartHealing.statusModules);
            for key, value in pairs(GridStatusSmartHealing.statusModules) do
                print(key, " = ", value);
            end
        end
    }
    ]]
}
GridStatusSmartHealing.statusModules = {};

-- module stuff
GridStatusSmartHealing.modulePrototype = GridStatus.modulePrototype;
GridStatusSmartHealing:SetDefaultModulePrototype(GridStatusSmartHealing.modulePrototype);

function GridStatusSmartHealing:RegisterStatus(module, status, desc, options, inMainMenu, order)
    GridStatus:RegisterStatus(status, desc, module.moduleName or true);

    options["enabled"] =
    {
        name = L("Enable"),
        desc = string.format(L("Enable %s"), desc),
        order = 10,
        width = "full",
        type = "toggle",
        get = function()
            return module.db.profile.enable;
        end,
        set = function(info, v)
            module.db.profile.enable = v;
            if (v) then
                if (module["OnStatusEnable"]) then
                    module:OnStatusEnable(status);
                end
            else
                if (module["OnStatusDisable"]) then
                    module:OnStatusDisable(status);
                end
            end
        end
    };
    options["color"] =
    {
        name = L("Color"),
        desc = string.format(L("Color for %s"), desc),
        order = 20,
        type = "color",
        hasAlpha = true,
        get = function()
            local color = module.db.profile.color;
            return color.r, color.g, color.b, color.a;
        end,
        set = function(_, r, g, b, a)
            local color = module.db.profile.color;
            color.r = r;
            color.g = g;
            color.b = b;
            color.a = a or 1;
        end,
    };
    options["priority"] =
    {
        name = L("Priority"),
        desc = string.format(L("Priority for %s"), desc),
        order = 30,
        type = "range", max = 99, min = 0, step = 1,
        get = function()
            return module.db.profile.priority
        end,
        set = function(_, v)
            module.db.profile.priority = v
        end,
    };

    GridStatusSmartHealing.options.args[desc] =
    {
        type = "group",
        name = desc,
        desc = string.format(L("Options for %s"), desc),
        args = options,
    };
end

function GridStatusSmartHealing:PostInitialize()
    -- Do now so modulePrototype can register Options for modules
    self:InitializeOptions();
end

function GridStatusSmartHealing:PostEnable()
    for name, statusModule in pairs(self.statusModules) do
        if (self:Enabled(statusModule)) then
            statusModule:OnStatusEnable(nil);----------------
            NumEnabled = NumEnabled + 1;
        end
    end

    if (NumEnabled > 0) then
        self:DoEnable(true);
    end
end

function GridStatusSmartHealing:IsEnabled()
    return NumEnabled > 0;
end

function GridStatusSmartHealing:DoEnable(force)
    if (NumEnabled > 0 and not force) then
        NumEnabled = NumEnabled + 1;
        return;
    end
    self:Debug("Enabling", self.moduleName);
    --[[
    self:UpdateRoster();

    self:RegisterEvent("PLAYER_MEMBERS_CHANGED", "UpdateRoster");
    self:RegisterEvent("RAID_ROSTER_UPDATE", "UpdateRoster");
    ]]

    --GridRoster:RegisterMessage("Grid_RosterUpdated");

    BlankFrame:SetScript("OnUpdate", function(_, timer)
        return self:OnUpdate(timer);
    end);

    -- todo loop modules and check enabled
    if (not force) then
        NumEnabled = 1;
    end

    --
    self.state =
    {
        x = 0,
        y = 0,
        mapWidth = 0,
        mapHeight = 0,
        data = {},
    };
end

function GridStatusSmartHealing:DoDisable()
    if (NumEnabled == 0) then
        return;
    end

    NumEnabled = NumEnabled - 1;

    if (NumEnabled == 0) then
        self:Debug("Disabling", self.moduleName);
        BlankFrame:SetScript("OnUpdate", nil);
    end
end

function GridStatusSmartHealing:ClearAll()
    for name, statusModule in pairs(self.statusModules) do
        if (self:Enabled(statusModule) and statusModule['ClearAll']) then
            statusModule:ClearAll(statusModule);
        end
    end
end

function GridStatusSmartHealing:UpdateAll()
    for name, statusModule in pairs(self.statusModules) do
        if (self:Enabled(statusModule) and statusModule['Update']) then
            statusModule:Update();
        end
    end
end

function GridStatusSmartHealing:OnUpdate(elapsed)
    Timer = Timer + elapsed;

    if (NumEnabled == 0) then
        self:Debug("Nothing is Enabled to Update");
        return;
    end

    if (Timer >= self.db.profile.cycle) then
        Timer = 0;
        --self:UpdateRoster();
        if (self:UpdateMap()) then
            self:UpdateData();
            self:UpdateAll();
        else
            SetMapToCurrentZone();
            self:Debug("Update Map Failed");
            self:ClearAll();
        end
    end
end

function GridStatusSmartHealing:GetUnitInfo(guid)
    return self.state.data[guid];
end

function GridStatusSmartHealing:UpdateData()
    self.state.data = {};
    self.state.x = self.state.x * self.state.mapWidth;
    self.state.y = self.state.y * self.state.mapHeight;

    for _guid, _unitId in GridRoster:IterateRoster() do
        local unitX, unitY = GetPlayerMapPosition(_unitId);

        if (unitX ~= 0 or unitY ~= 0 and self:IsValidHealTarget(_unitId)) then
            local health = UnitHealth(_unitId);
            local maxHealth = UnitHealthMax(_unitId);

            self.state.data[_guid] =
            {
                    unitId = _unitId,
                    guid = _guid,
                    x = unitX * self.state.mapWidth,
                    y = unitY * self.state.mapHeight,
                    health = health,
                    maxHealth = maxHealth,
                    missingHealth = maxHealth - health,
                    percent = maxHealth / health,
            };
        end
    end
end

function GridStatusSmartHealing:UpdateMap()
    self.state.x, self.state.y = GetPlayerMapPosition('player');

    if (self.state.x <= 0 or self.state.y <= 0) then
        return false;
    end

    local fileName = GetMapInfo();
    local currentLevel = GetCurrentMapDungeonLevel();

    self.state.mapWidth, self.state.mapHeight = MapData:MapArea(fileName, currentLevel);

    if (self.state.mapWidth ~= 0 and self.state.mapHeight ~= 0) then
        return true;
    end

    return false;
end

--[[ -- Now using GridRoster
function GridStatusSmartHealing:Grid_RosterUpdated()
    self.roster = { [1] = {}, [2] = {}, [3] = {}, [4] = {}, [5] = {}, [6] = {}, [7] = {}, [8] = {} };

    local RaidMemberNum = GetNumGroupMembers();

    if (IsInRaid() == false) then
        tinsert(self.roster[1], "player");
        player_group = 1;
        for i = 1, 4 do
            local unitid = "party" .. i;
            if UnitExists(unitid) then
                tinsert(self.roster[1], unitid);
            end
        end
    else
        for i = 1, RaidMemberNum do
            local name, _, group = GetRaidRosterInfo(i);
            local unitid = "raid" .. i;
            if (name and UnitExists(unitid)) then
                tinsert(self.roster[group], unitid)
                if (UnitIsUnit("player", unitid)) then
                    player_group = group;
                end
            end
        end
    end
end
]]

function GridStatusSmartHealing:Enabled(statusModule)
    return statusModule.db.profile.enable;
end

function GridStatusSmartHealing:Distance(p1, p2)
    local x = p2.x - p1.x;
    local y = p2.y - p1.y;
    return x*x + y*y;
end

function GridStatusSmartHealing:HasGlyph(glyph_id)
    for i = 1, 9 do
        local _, _, _, id = GetGlyphSocketInfo(i);
        if (id and glyph_id == id) then
            return true;
        end
    end
    return false;
end

function GridStatusSmartHealing:IsValidHealTarget(unitId)
    return not UnitIsDeadOrGhost(unitId) and
        UnitIsConnected(unitId) and
        UnitIsVisible(unitId) and
        not (UnitIsCharmed(unitId) and UnitIsEnemy("player", unitId));
end

function GridStatusSmartHealing:NewModule(name)
    local statusModule = Grid:NewStatusModule(name);
    GridStatusSmartHealing.statusModules[statusModule.name] = statusModule;
    return statusModule;
end