--[[
    This file is part of Decursive.
    
    Decursive (v 2.5.1-12-gb39554a) add-on for World of Warcraft UI
    Copyright (C) 2006-2007-2008-2009 John Wellesz (archarodim AT teaser.fr) ( http://www.2072productions.com/to/decursive.php )

    Starting from 2009-10-31 and until said otherwise by its author, Decursive
    is no longer free software, all rights are reserved to its author (John Wellesz).

    The only official and allowed distribution means are www.2072productions.com, www.wowace.com and curse.com.
    To distribute Decursive through other means a special authorization is required.
    

    Decursive is inspired from the original "Decursive v1.9.4" by Quu.
    The original "Decursive 1.9.4" is in public domain ( www.quutar.com )

    Decursive is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY.
--]]
-------------------------------------------------------------------------------

local addonName, T = ...;
-- big ugly scary fatal error message display function {{{
if not T._FatalError then
-- the beautiful error popup : {{{ -
StaticPopupDialogs["DECURSIVE_ERROR_FRAME"] = {
    text = "|cFFFF0000Decursive Error:|r\n%s",
    button1 = "OK",
    OnAccept = function()
        return false;
    end,
    timeout = 0,
    whileDead = 1,
    hideOnEscape = 1,
    showAlert = 1,
    }; -- }}}
T._FatalError = function (TheError) StaticPopup_Show ("DECURSIVE_ERROR_FRAME", TheError); end
end
-- }}}
if not T._LoadedFiles or not T._LoadedFiles["Dcr_Raid.lua"] then
    if not DecursiveInstallCorrupted then T._FatalError("Decursive installation is corrupted! (Dcr_Raid.lua not loaded)"); end;
    DecursiveInstallCorrupted = true;
    return;
end

local D = Dcr;
--D:SetDateAndRevision("$Date: 2008-09-16 00:25:13 +0200 (mar., 16 sept. 2008) $", "$Revision: 81755 $");

local L = D.L;
local LC = D.LC;
local DC = DcrC;
local DS = DC.DS;
-------------------------------------------------------------------------------

local pairs             = _G.pairs;
local ipairs            = _G.ipairs;
local type              = _G.type;
local table             = _G.table;
local t_sort            = _G.table.sort;
local PlaySoundFile     = _G.PlaySoundFile;
local UnitName          = _G.UnitName;
local UnitDebuff        = _G.UnitDebuff;
local UnitBuff          = _G.UnitBuff;
local UnitIsCharmed     = _G.UnitIsCharmed;
local UnitCanAttack     = _G.UnitCanAttack;
local UnitClass         = _G.UnitClass;
local UnitExists        = _G.UnitExists;
local GetNetStats       = _G.GetNetStats;
local _;

-------------------------------------------------------------------------------
-- The UI functions {{{
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-- The printing functions {{{
-------------------------------------------------------------------------------

function D:Show_Cure_Order() --{{{
    self:Println("printing cure order:");
    for index, unit in ipairs(self.Status.Unit_Array) do
        self:Println( unit, " - ", self:MakePlayerName((self:UnitName(unit))) , " Index: ", index);
    end
end --}}}

-- }}}
-------------------------------------------------------------------------------

-- Show Hide FUNCTIONS -- {{{

function D:ShowHideLiveList(hide) --{{{

    if not D.DcrFullyInitialized then
        return;
    end

    -- if hide is requested or if hide is not set and the live-list is shown
    if (hide==1 or (not hide and DcrLiveList:IsVisible())) then
        D.profile.Hide_LiveList = true;
        DcrLiveList:Hide();
        D:CancelDelayedCall("Dcr_LLupdate");
    else
        D.profile.Hide_LiveList = false;
        DcrLiveList:ClearAllPoints();
        DcrLiveList:SetPoint("TOPLEFT", "DecursiveMainBar", "BOTTOMLEFT");
        DcrLiveList:Show();

        D:ScheduleRepeatedCall("Dcr_LLupdate", D.LiveList.Update_Display, D.profile.ScanTime, D.LiveList);
    end

end --}}}

-- This functions hides or shows the "Decursive" bar depending on its current
-- state, it's also able hide/show the live-list if the "tie live-list" option is active
function D:HideBar(hide) --{{{

    if not D.DcrFullyInitialized then
        return;
    end

    if (hide==1 or (not hide and DecursiveMainBar:IsVisible())) then
        if (D.profile.LiveListTied) then
            D:ShowHideLiveList(1);
        end
        D.profile.Hidden = true;
        DecursiveMainBar:Hide();
    else
        if (D.profile.LiveListTied) then
            D:ShowHideLiveList(0);
        end
        D.profile.Hidden = false;
        DecursiveMainBar:Show();
    end

    if DecursiveMainBar:IsVisible() and DcrLiveList:IsVisible() then
        DcrLiveList:ClearAllPoints();
        DcrLiveList:SetPoint("TOPLEFT", "DecursiveMainBar", "BOTTOMLEFT");
    else
        D:ColorPrint(0.3, 0.5, 1, L["SHOW_MSG"]);
    end

    LibStub("AceConfigRegistry-3.0"):NotifyChange(D.name);
end --}}}

function D:ShowHidePriorityListUI() --{{{

    if not D.DcrFullyInitialized then
        return;
    end

    if (DecursivePriorityListFrame:IsVisible()) then
        DecursivePriorityListFrame:Hide();
    else
        DecursivePriorityListFrame:Show();
    end
end --}}}

function D:ShowHideSkipListUI() --{{{
    
    if not D.DcrFullyInitialized then
        return;
    end

    if (DecursiveSkipListFrame:IsVisible()) then
        DecursiveSkipListFrame:Hide();
    else
        DecursiveSkipListFrame:Show();
    end
end --}}}

-- This shows/hides the buttons near the "Decursive" bar
function D:ShowHideButtons(UseCurrentValue) --{{{

    if not D.DcrFullyInitialized then
        return;
    end

    if not D.profile then
        return;
    end


    local DcrFrame = "DecursiveMainBar";
    local buttons = {
        DcrFrame .. "Priority",
        DcrFrame .. "Skip",
        DcrFrame .. "Hide",
    }

    local DCRframeObject = _G[DcrFrame];

    if (not UseCurrentValue) then
        D.profile.HideButtons = (not D.profile.HideButtons);
    end

    for _, ButtonName in pairs(buttons) do
        local Button = _G[ButtonName];

        if (D.profile.HideButtons) then
            Button:Hide();
            DCRframeObject.isLocked = 1;
        else
            Button:Show();
            DCRframeObject.isLocked = 0;
        end

    end

end --}}}

-- }}}


-- this resets the location of the windows
function D:ResetWindow() --{{{

    DecursiveMainBar:ClearAllPoints();
    DecursiveMainBar:SetPoint("CENTER", UIParent);
    DecursiveMainBar:Show();

    DcrLiveList:ClearAllPoints();
    DcrLiveList:SetPoint("TOPLEFT", DecursiveMainBar, "BOTTOMLEFT");
    DcrLiveList:Show();

    DecursivePriorityListFrame:ClearAllPoints();
    DecursivePriorityListFrame:SetPoint("CENTER", UIParent);

    DecursiveSkipListFrame:ClearAllPoints();
    DecursiveSkipListFrame:SetPoint("CENTER", UIParent);

    DecursivePopulateListFrame:ClearAllPoints();
    DecursivePopulateListFrame:SetPoint("CENTER", UIParent);

    D.MFContainer:ClearAllPoints();
    D.MFContainer:SetPoint("CENTER", UIParent, "CENTER", 0, 0);

    DecursiveAnchor:ClearAllPoints();
    DecursiveAnchor:SetPoint("TOP", UIErrorsFrame, "BOTTOM", 0, 0);

end --}}}


function D:PlaySound (UnitID, Caller) --{{{
    if self.profile.PlaySound and not self.Status.SoundPlayed then
        local Debuffs = self:UnitCurableDebuffs(UnitID, true);
        if Debuffs and Debuffs[1] and Debuffs[1].Type then

            -- good sounds: Sound\\Doodad\\BellTollTribal.wav
            --          Sound\\interface\\AuctionWindowOpen.wav
            --          Sound\\interface\\AlarmClockWarning3.wav
            PlaySoundFile(self.profile.SoundFile);
            self:Debug("Sound Played! by %s", Caller);
            self.Status.SoundPlayed = true;
        else
            self.UnitDebuffed[UnitID] = false;
        end
    end
end --}}}

-- LIVE-LIST DISPLAY functions {{{



-- Those set the scalling of the LIVELIST container
-- SACALING FUNCTIONS {{{
-- Place the LIVELIST container according to its scale
function D:PlaceLL () -- {{{
    local UIScale       = UIParent:GetEffectiveScale()
    local FrameScale    = DecursiveMainBar:GetEffectiveScale();
    local x, y = D.profile.MainBarX, D.profile.MainBarY;

    -- check if the coordinates are correct
    if x and y and (x + 10 > UIParent:GetWidth() * UIScale or x < 0 or (-1 * y + 10) > UIParent:GetHeight() * UIScale or y > 0) then
        x = false; -- reset to default position
        T._FatalError("Decursive's bar position reset to default");
    end

    -- Executed for the very first time, then put it in the top right corner of the screen
    if (not x or not y) then
        x =    (UIParent:GetWidth()  * UIScale) / 2;
        y =  - (UIParent:GetHeight() * UIScale) / 8;

        D.profile.MainBarX = x;
        D.profile.MainBarY = y;
    end

    -- set to the scaled position
    DecursiveMainBar:ClearAllPoints();
    DecursiveMainBar:SetPoint("TOPLEFT", UIParent, "TOPLEFT", x/FrameScale , y/FrameScale);
    DcrLiveList:ClearAllPoints();
    DcrLiveList:SetPoint("TOPLEFT", DecursiveMainBar, "BOTTOMLEFT");
end -- }}}

-- Save the position of the frame without its scale
function D:SaveLLPos () -- {{{
    if self.profile and DecursiveMainBar:IsVisible() then
        -- We save the unscalled position (no problem if the sacale is changed behind our back)
        self.profile.MainBarX = DecursiveMainBar:GetEffectiveScale() * DecursiveMainBar:GetLeft();
        self.profile.MainBarY = DecursiveMainBar:GetEffectiveScale() * DecursiveMainBar:GetTop() - UIParent:GetHeight() * UIParent:GetEffectiveScale();

        
        if self.profile.MainBarX < 0 then
            self.profile.MainBarX = 0;
        end

        if self.profile.MainBarY > 0 then
            self.profile.MainBarY = 0;
        end

    end
end -- }}}

-- set the scaling of the LIVELIST container according to the user settings
function D:SetLLScale (NewScale) -- {{{
    
    -- save the current position without any scaling
    D:SaveLLPos ();
    -- Set the new scale
    DecursiveMainBar:SetScale(NewScale);
    DcrLiveList:SetScale(NewScale);
    -- Place the frame adapting its position to the news cale
    D:PlaceLL ();
    
end -- }}}
-- }}}


-- }}}

-- // }}}
-------------------------------------------------------------------------------

do
   local iterator = 1;
   local DebuffHistHashTable = {};

   function D:Debuff_History_Add( DebuffName, DebuffType )

       if not DebuffHistHashTable[DebuffName] then

           -- reset iterator if out of boundaries
           if iterator > DC.DebuffHistoryLength then
               iterator = 1;
           end

           -- clean hastable if necessary before adding a new entry
           if DebuffHistHashTable[D.DebuffHistory[iterator]] then
               DebuffHistHashTable[D.DebuffHistory[iterator]] = nil;
           end

           -- Register the name in the HashTable using the debuff type
           DebuffHistHashTable[DebuffName] = (DebuffType and DC.NameToTypes[DebuffType] or DC.NOTYPE);
           --D:Debug(DebuffName, DebuffHistHashTable[DebuffName]);

           -- Put this debuff in our history
           D.DebuffHistory[iterator] = DebuffName;

           -- This is a useless comment
           iterator = iterator + 1;
       end

   end

   function D:Debuff_History_Get (Index, Colored)

       local HumanIndex = iterator - Index;

       if HumanIndex < 1 then
           HumanIndex = HumanIndex + DC.DebuffHistoryLength;
       end

       if not D.DebuffHistory[HumanIndex] then
           return "|cFF777777Empty|r", false;
       end

       if Colored then
           --D:Debug(D.DebuffHistory[HumanIndex], DebuffHistHashTable[D.DebuffHistory[HumanIndex]]);
           return D:ColorText(D.DebuffHistory[HumanIndex], "FF" .. DC.TypeColors[DebuffHistHashTable[D.DebuffHistory[HumanIndex]]]), true;
       else
           return D.DebuffHistory[HumanIndex], true;
       end
   end

end

-- Scanning functionalities {{{
-------------------------------------------------------------------------------

do

    local Name, rank, Texture, Applications, TypeName, Duration, expirationTime;
    local D = _G.Dcr;
    local UnitAura = _G.UnitAura;

    -- This function only returns interesting values of UnitDebuff()
    local function GetUnitDebuff  (Unit, i) --{{{

        if D.LiveList.TestItemDisplayed and i == 1 and Unit ~= "target" and Unit ~= "mouseover" and UnitExists(Unit) then
            D:Debug("|cFFFF0000Setting test debuff for %s (debuff %d)|r", Unit, i);
            return "Test item", DC.TypeNames[D.Status.ReversedCureOrder[1]], 2, "Interface\\AddOns\\Decursive\\iconON.tga", D.LiveList.TestItemDisplayed + 70;
        end

        --D:Debug("|cFFFF0000Getting debuffs for %s , id = %d|r", Unit, i);


        --    Name, rank, Texture, Applications, TypeName, duration, expirationTime, unitCaster, isStealable = UnitAura("unit", index or ["name", "rank"][, "filter"])

        local Name, rank, Texture, Applications, TypeName, Duration, expirationTime = UnitAura(Unit, i, "HARMFUL");

        --local Name, rank, Texture, Applications, TypeName, Duration = UnitDebuff(Unit, i);

        if Name then
            return Name, TypeName, Applications, Texture, expirationTime;
        else
            return false, false, false, false, false;
        end
    end --}}}

    -- there is a known maximum number of unit and a known maximum debuffs per unit so lets allocate the memory needed only once. Memory will be allocated when needed and re-used...
    local DebuffUnitCache = {};

    -- Variables are declared outside so that Lua doesn't initialize them at each call
    local Name, Type, i, StoredDebuffIndex, CharmFound, IsCharmed;

    local DcrC = DcrC; -- for faster access

    local UnitIsCharmed = _G.UnitIsCharmed;
    local UnitCanAttack = _G.UnitCanAttack;
    local GetTime       = _G.GetTime;
    local IsSpellInRange    = _G.IsSpellInRange;

    -- This is the core debuff scanning function of Decursive
    -- This function does more than just reporting Debuffs. it also detects charmed units

    function D:GetUnitDebuffAll (Unit) --{{{

        -- create a Debuff table for this unit if there is not already one
        if (not DebuffUnitCache[Unit]) then
            DebuffUnitCache[Unit] = {};
        end

        -- This is just a shortcut for easier readability
        local ThisUnitDebuffs = DebuffUnitCache[Unit];

        i = 1;                  -- => to index all debuffs
        StoredDebuffIndex = 1;  -- => this index only debuffs with a type
        CharmFound = false;     -- => avoid to find that the unit is charmed again and again...


        -- test if the unit is mind controlled once
        if (UnitIsCharmed(Unit) and (UnitCanAttack("player", Unit) or UnitIsCharmed("player"))) then
            IsCharmed = true;
        else
            IsCharmed = false;
        end

        -- iterate all available debuffs
        while (true) do
            Name, TypeName, Applications, Texture, expirationTime = GetUnitDebuff(Unit, i);

            if not Name then
                break;
            end

        
            -- test for a type (Magic Curse Disease or Poison)
            if (TypeName and TypeName ~= "") then
                Type = DC.NameToTypes[TypeName];
            else
                Type = false;
            end

            -- implement the test for DominateMind I HATE stupid exceptions like this one... so many hours lost because of this :/
            --[=[
            if Name == DS["YOGGG_DOMINATE_MIND"] and Type == DC.MAGIC then

                if DC.MyClass == "PALADIN" then
                    IsCharmed = false;
                    CharmFound = false;
                else
                    IsCharmed = true;
                end

                if DC.MyClass == "PALADIN" or DC.MyClass == "SHAMAN" then

                    YoggReport = true;

                    D:AddDebugText("|cFFFF9955Decursive Yoggy Debug (try 6):|r", Unit, TypeName, Applications,
                    "PC:",DC.MyClass,"IsOvering:", self.Status.MouseOveringMUF, "DN",i, "SDi",StoredDebuffIndex,
                    "UICp:",UnitIsCharmed( "player"), "UICu:",UnitIsCharmed(Unit),
                    "UCApu:",UnitCanAttack("player", Unit), "IsCharmed:", IsCharmed
                    -- "UCAup:",UnitCanAttack(Unit, "player"), "UCApu:",UnitCanAttack("player", Unit), "UIFpu:",UnitIsFriend( "player", Unit), "UIEpu:",UnitIsEnemy( "player", Unit)
                    );
                end

            end
            --]=]

            -- if the unit is charmed and we didn't took care of this information yet
            if IsCharmed and (not CharmFound or Type == DC.MAGIC) then
                -- If the unit has a magical debuff and we can cure it
                -- (note that the target is not friendly in that case)
                if (Type == DC.MAGIC and self.Status.CuringSpells[DC.ENEMYMAGIC]) then
                    Type = DC.ENEMYMAGIC;

                    -- NOTE: if a unit is charmed and has another magical debuff
                    -- this block will be executed...
                else -- the unit doesn't have a magical debuff or we can't remove magical debuffs
                    Type = DC.CHARMED; -- The player can't remove it anyway so just say the unit is afflicted by a charming effect
                    TypeName = DC.TypeNames[DC.CHARMED];
                end
                CharmFound = true;
            end

            --[=[
            if YoggReport then
                local IsInRange;
                if self.Status.CuringSpells[Type] then
                    IsInRange = IsSpellInRange(self.Status.CuringSpells[Type], Unit)
                end
                D:AddDebugText("CharmFound:", CharmFound, "TN:", DC.TypeNames[Type], "ISIR", IsInRange);
            end
            --]=]


            -- If we found a type, register the Debuff
            if (Type) then
                -- Create a Debuff index entry if necessary
                if (not ThisUnitDebuffs[StoredDebuffIndex]) then
                    ThisUnitDebuffs[StoredDebuffIndex] = {};
                end

                ThisUnitDebuffs[StoredDebuffIndex].expirationTime = expirationTime;
                ThisUnitDebuffs[StoredDebuffIndex].Texture        = Texture;
                ThisUnitDebuffs[StoredDebuffIndex].Applications   = Applications;
                ThisUnitDebuffs[StoredDebuffIndex].TypeName       = TypeName;
                ThisUnitDebuffs[StoredDebuffIndex].Type           = Type;
                ThisUnitDebuffs[StoredDebuffIndex].Name           = Name;
                ThisUnitDebuffs[StoredDebuffIndex].index          = i;

                -- we can't use i, else we wouldn't have contiguous indexes in the table
                StoredDebuffIndex = StoredDebuffIndex + 1;
            end

            i = i + 1;
        end

        -- erase remaining unused entries without freeing the memory (less garbage)
        while (ThisUnitDebuffs[StoredDebuffIndex]) do
            ThisUnitDebuffs[StoredDebuffIndex].Type = false;
            StoredDebuffIndex = StoredDebuffIndex + 1;
        end

        -- if no debuff on the unit then it can't be charmed... FUCKING LAG!!
        if i == 1 then
            IsCharmed = false;
        end

        return ThisUnitDebuffs, IsCharmed;
    end --}}}
end


do
    -- see the comment about DebuffUnitCache
    local ManagedDebuffUnitCache = D.ManagedDebuffUnitCache;


    local D = D;
    local _ = false;
    local CureOrder;
    local sorting = function (a, b)

        CureOrder = D.classprofile.CureOrder; -- LUA is too simple, lets do the access optimization...

        local cura = (a.Type and CureOrder[a.Type] and CureOrder[a.Type] > 0) and CureOrder[a.Type] or 1024;
        local curb = (b.Type and CureOrder[b.Type] and CureOrder[b.Type] > 0) and CureOrder[b.Type] or 1024;

        return cura < curb;
    end

    -- This function will return a table containing only the Debuffs we can cure excepts the one we have to ignore
    -- in different conditions.
    function D:UnitCurableDebuffs (Unit, JustOne) -- {{{

        if not Unit then
            self:Debug("No unit supplied to UnitCurableDebuffs()");
            return false;
        end

        if (not ManagedDebuffUnitCache[Unit]) then
            ManagedDebuffUnitCache[Unit] = {};
        end

        local AllUnitDebuffs, IsCharmed = self:GetUnitDebuffAll(Unit); -- always return a table, may be empty though

        if not (AllUnitDebuffs[1] and AllUnitDebuffs[1].Type ) then -- if there is no curable debuff (a debuff with a type)
            return false, IsCharmed;
        end

        local Spells    = self.Status.CuringSpells; -- shortcut to available spells by debuff type


        local ManagedDebuffs = ManagedDebuffUnitCache[Unit]; -- shortcut for readability

        --self:Debug("Debuffs were found");

        local DebuffNum = 1; -- number of found debuff (used for indexing)

        local continue_ = true; -- if we have to ignore a debuff, this will become false



        for _, Debuff in ipairs(AllUnitDebuffs) do

            if (not Debuff.Type) then -- useless test, only debuffs with a type are returned...
                break;
            end
            continue_ = true;

            -- test if we have to ignore this debuf  {{{ --
            
            if self.Status.PlayerOnlyTypes[Debuff.Type] and Unit ~= "player" then -- if this type is curable on the player only
                continue_ = false;
            end
            
            if (self.profile.DebuffsToIgnore[Debuff.Name]) then
                -- these are the BAD ones... the ones that make the target immune... abort this unit
                --D:Debug("UnitCurableDebuffs(): %s is ignored", Debuff.Name);
                break; -- exit here
            end

            if (self.profile.BuffDebuff[Debuff.Name]) then
                -- these are just ones you don't care about (sleepless deam etc...)
                continue_ = false;
                --D:Debug("UnitCurableDebuffs(): %s is not a real debuff", Debuff.Name);
            end

            if (self.Status.Combat or self.profile.DebuffAlwaysSkipList[Debuff.Name]) then
                local _, EnUClass = UnitClass(Unit);
                if (self.profile.skipByClass[EnUClass]) then
                    if (self.profile.skipByClass[EnUClass][Debuff.Name]) then
                        -- these are just ones you don't care about by class while in combat

                        -- This lead to a problem because once the fight is finished there are no event to trigger
                        -- a rescan of this unit, so the debuff does not appear...

                        -- solution to the above problem:

                        if not self.profile.DebuffAlwaysSkipList[Debuff.Name] then
                            self:AddDelayedFunctionCall("ReScan"..Unit, D.MicroUnitF.UpdateMUFUnit, D.MicroUnitF, Unit);
                        end
                        
                        D:Debug("UnitCurableDebuffs(): %s is configured to be skipped", Debuff.Name);
                        continue_ = false;
                    end
                end
            end

            -- }}}

            
            if continue_ then
                --      self:Debug("Debuffs matters");
                -- If we are still here it means that this Debuff is something not to be ignored...


                -- We have a match for this type and we decided (checked) to
                -- cure it NOTE: self.classprofile.CureOrder[DEBUFF_TYPE] is set
                -- to FALSE when the type is unchecked and to < 0 when there is
                -- no spell available for the type or when the spell is gone
                -- (it happens for warlocks or when using the same profile with
                -- several characters)
                --if (self.classprofile.CureOrder[Debuff.Type] and self.classprofile.CureOrder[Debuff.Type] > 0) then
                if (self:GetCureCheckBoxStatus(Debuff.Type)) then


                    -- self:Debug("we can cure it");

                    -- if we do have a spell to cure
                    if (Spells[Debuff.Type]) then
                        -- The user doesn't want to cure a unit afllicted by poison or disease if the unit
                        -- is beeing cured by an abolish spell

                        if (self.profile.Check_For_Abolish and (Debuff.Type == DC.POISON and self:CheckUnitForBuffs(Unit, DS["SPELL_ABOLISH_POISON"]) or Debuff.Type == DC.DISEASE and self:CheckUnitForBuffs(Unit, DS["SPELL_ABOLISH_DISEASE"]))) then
                            self:Debug("Abolish buff found, skipping");
                        else
                            -- self:Debug("It's managed");

                            -- create an entry for this debuff index if necessary
                            if (not ManagedDebuffs[DebuffNum]) then
                                ManagedDebuffs[DebuffNum] = {};
                            end

                            -- copy the debuff information to this table.
                            self:tcopy(ManagedDebuffs[DebuffNum], Debuff);

                            DebuffNum = DebuffNum + 1;

                            -- the live-list only reports the first debuf found and set JustOne to true
                            if (JustOne) then
                                break;
                            end
                        end
                    end
                end
            end
        end -- for END

        -- erase unused entries without freeing the memory (less garbage)
        if (not JustOne or DebuffNum == 1)  then -- if JustOne is set don't clear anything except if we found nothing
            while (ManagedDebuffs[DebuffNum]) do
                ManagedDebuffs[DebuffNum].Type = false;
                -- ManagedDebuffs[DebuffNum].TimeStamp = false;
                DebuffNum = DebuffNum + 1;
            end
        end

        -- sort the table only if it's not 'empty' and only if there is at least two debuffs
        if (ManagedDebuffs[1] and ManagedDebuffs[1].Type) then

            -- order Debuffs according to type priority order
            if (not JustOne and ManagedDebuffs[2] and ManagedDebuffs[2].Type) then
                t_sort(ManagedDebuffs, sorting); -- uses memory..
            end
            return ManagedDebuffs, IsCharmed;
        else
            return false, IsCharmed;
        end

    end -- // }}}

    local GetTime       = _G.GetTime;
    local Debuffs       = {}; local IsCharmed = false; local Unit; local MUF; local IsDebuffed= false; local CheckStealth = false;
    local NoScanStatuses        = false;
    function D:ScanEveryBody()

        if not NoScanStatuses then
            NoScanStatuses = {[DC.ABSENT] = true, [DC.FAR] = true, [DC.BLACKLISTED] = true};
        end

        local UnitArray = self.Status.Unit_Array; local i = 1;
        local CheckStealth = self.profile.Show_Stealthed_Status;

        --[===[@debug@
        local start = GetTime();
        --@end-debug@]===]

        while UnitArray[i] do
            Unit = UnitArray[i];
            MUF = self.MicroUnitF.UnitToMUF[Unit];

            if MUF and not NoScanStatuses[MUF.UnitStatus] then
                Debuffs, IsCharmed = self:UnitCurableDebuffs(Unit, true);

                if CheckStealth then
                    self.Stealthed_Units[Unit] = self:CheckUnitStealth(Unit); -- update stealth status
                end

                IsDebuffed = (Debuffs and true) or IsCharmed;
                -- If MUF disagrees
                if IsDebuffed ~= MUF.IsDebuffed and not D:DelayedCallExixts("Dcr_Update" .. Unit) then
                    --[===[@debug@
                    if IsDebuffed then
                        self:AddDebugText("delayed debuff found by scaneveryone, scheduling analysis in 1s");
                        --D:ScheduleDelayedCall("Dcr_lateanalysis" .. Unit, self.MicroUnitF.LateAnalysis, 1, self.MicroUnitF, "ScanEveryone", Debuffs, MUF, MUF.UnitStatus);
                    else
                        self:AddDebugText("delayed UNdebuff found by scaneveryone on", Unit);
                    end
                    --@end-debug@]===]

                    self.MicroUnitF:UpdateMUFUnit(Unit, true);

                    --[===[@debug@
                    D:Println("HAAAAAAA!!!!!");
                    --@end-debug@]===]
                end
            end

            i = i + 1;
        end
        --[===[@debug@
        --D:Debug("|cFF777777Scanning everybody...", i - 1, "units scanned in ", GetTime() - start, "seconds|r");
        --@end-debug@]===]
    end


    -- a little test... the ".." way wins (6x faster than the format solution) when both sides are strings
    function D:tests()

        local test = "test1";
        local start = GetTime();
        local strings = {"string1", "string2", "strring3"};
        local teststring = "unitraid5"
        for i =1, 1000000 do
            teststring = strings[i%3 + 1];
            test = "test_"..teststring;
        end
        D:Debug("pass (\"\".. completed in:", GetTime() - start, test);

        start = GetTime();
        for i =1, 1000000 do
            local t = strings[i%3 + 1];
            test = ("test_%s"):format(teststring);
        end
        D:Debug("pass format completed in:", GetTime() - start, test);

    end

end

local UnitBuffsCache    = {};

-- this function returns true if one of the debuff(s) passed to it is found on the specified unit
function D:CheckUnitForBuffs(Unit, BuffNamesToCheck) --{{{

    -- --[=[
    if (not UnitBuffsCache[Unit]) then
        UnitBuffsCache[Unit] = {};
    end

    local UnitBuffs     = UnitBuffsCache[Unit];
    local i             = 1;
    local buff_name     = "";

    -- Get all the unit's buffs
    while true do

        buff_name = UnitBuff(Unit, i)

        if not buff_name then
            break;
        end

        UnitBuffs[i] = buff_name;
        i = i + 1;
    end

    while UnitBuffs[i] do -- clean the rest of the cache
        UnitBuffs[i] = false;
        i = i + 1;
    end
    --]=]

    if type(BuffNamesToCheck) ~= "table" then

        if self:tcheckforval(UnitBuffs, BuffNamesToCheck) then
            return true;
        else
            return false;
        end
    else
        local Buff;
        for _, Buff in pairs(BuffNamesToCheck) do

            if self:tcheckforval(UnitBuffs, Buff) then
                return true;
            end

        end
    end

    return false;

end --}}}


D.Stealthed_Units = {};

do
    local Stealthed = {DS["Prowl"], DS["Stealth"], DS["Shadowmeld"],  DS["Invisibility"], DS["Lesser Invisibility"]}; --, DS["Ice Armor"],};

    DC.IsStealthBuff = D:tReverse(Stealthed);

    function D:CheckUnitStealth(Unit)
        if self:CheckUnitForBuffs(Unit, Stealthed) then
            --      self:Debug("Sealth found !");
            return true;
        end
        return false;
    end
end
-- }}}



T._LoadedFiles["Decursive.lua"] = "2.5.1-12-gb39554a";

-- Sin
