--[[
 Decursive (v 1.9.8.3) add-on for World of Warcraft UI
 Copyright (C) 2006 Archarodim ( http://www.2072productions.com/?to=decursive-continued.txt )
 This is the continued work of the original Decursive (v1.9.4) by Quu
 Decursive 1.9.4 is in public domain ( www.quutar.com )
 
 License:
	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.
 
	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
--]]
-------------------------------------------------------------------------------

-- this will spam... really only use it for testing
local Dcr_Print_Spell_Found = false;
-- how many seconds... can be fractional... needs to be more than 0.4... 1.0 is optimal
local Dcr_SpellCombatDelay = 1.0;
-- print out a fuckload of info
Dcr_Print_DEBUG = false;
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-- and any internal HARD settings for decursive
DCR_MAX_LIVE_SLOTS = 15;
local DCR_TEXT_LIFETIME = 4.0;
-------------------------------------------------------------------------------


-- this is something I use for remote debugging
DCR_REMOTE_DEBUG = { };

-------------------------------------------------------------------------------
-- variables {{{
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-- The stored variables {{{
-------------------------------------------------------------------------------
Dcr_Saved = {
    -- this is the items that are stored...

    Dcr_Print_DEBUG_bis = false;

    -- this is the priority list of people to cure
    PriorityList = { };

    -- this is the people to skip
    SkipList = { };

    -- this is wether or not to show the "live" list	
    Hide_LiveList = false;

    -- This will turn on and off the sending of messages to the default chat frame
    Print_ChatFrame = false;

    -- this will send the messages to a custom frame that is moveable	
    Print_CustomFrame = true;

    -- this will disable error messages
    Print_Error = true;

    -- check for abolish before curing poison or disease
    Check_For_Abolish = true;

    -- this is "fix" for the fact that rank 1 of dispell magic does not always remove
    -- the high level debuffs properly. This carrys over to other things.
    AlwaysUseBestSpell = true;

    -- should we do the orders randomly?
    Random_Order = false;

    -- should we scan pets
    Scan_Pets = true;

    -- should we ignore stealthed units
    Ingore_Stealthed = false;

    -- how many to show in the livelist
    Amount_Of_Afflicted = 5;

    -- how many seconds to "black list" someone with a failed spell
    CureBlacklist	= 5.0;

    -- how often to poll for afflictions in seconds
    ScanTime = 0.2;

    -- Are prio list members protected from blacklisting?
    DoNot_Blacklist_Prio_List = false;

    -- Play a sound when there is something to decurse
    PlaySound = true;

    -- Hide the buttons
    HideButtons = false;

    -- Cure magic if possible
    CureMagic	= true;
    -- Cure Poison if possible
    CurePoison	= true;
    -- Cure Disease if possible
    CureDisease	= true;
    -- Cure Curse if possible
    CureCurse	= true;

    -- Display text above in the custom frame
    CustomeFrameInsertBottom = false;

    -- Disable tooltips in affliction list
    AfflictionTooltips = true;

    -- Reverse LiveList Display
    ReverseLiveDisplay = false;

    -- Hide everything but the livelist
    Hidden = false;

    -- if true then the live list will show only if the main window is shown
    LiveListTied = false;
    
    -- allow to changes the default output window
    Dcr_OutputWindow = DEFAULT_CHAT_FRAME;

    -- cure order list
    CureOrderList = {
	[1] = DCR_MAGIC,
	[2] = DCR_CURSE,
	[3] = DCR_POISON,
	[4] = DCR_DISEASE
    }


}; -- // }}}
-------------------------------------------------------------------------------


-- This array avoid to test someone we've just blackisted twice.
local DCR_ThisCleanBlaclisted = { };

local Dcr_CuringAction_Icons = { };

-- the new spellbook (made it simpler due to localization problems)
local DCR_HAS_SPELLS		= false;

local DCR_SPELL_MAGIC_1		= {0,"", ""};
local DCR_SPELL_MAGIC_2		= {0,"", ""};
 DCR_CAN_CURE_MAGIC	= false;

local DCR_SPELL_ENEMY_MAGIC_1	= {0,"", ""};
local DCR_SPELL_ENEMY_MAGIC_2	= {0,"", ""};
 DCR_CAN_CURE_ENEMY_MAGIC	= false;

local DCR_SPELL_DISEASE_1	= {0,"", ""};
local DCR_SPELL_DISEASE_2	= {0,"", ""};
 DCR_CAN_CURE_DISEASE	= false;

local DCR_SPELL_POISON_1	= {0,"", ""};
local DCR_SPELL_POISON_2	= {0,"", ""};
 DCR_CAN_CURE_POISON	= false;

local DCR_SPELL_CURSE		= {0,"", ""};
 DCR_CAN_CURE_CURSE	= false;

local DCR_SPELL_COOLDOWN_CHECK	= {0,"", ""};

-- for the blacklist
local Dcr_Casting_Spell_On = nil;
local Dcr_Blacklist_Array = { };

local DEBUFF_CACHE_LIFE = 30.0;
local Dcr_Debuff_Texture_to_name_cache = {};
local Dcr_Debuff_Texture_to_name_cache_life = 0.0;

local Dcr_Buff_Texture_to_name_cache = {};
local Dcr_Buff_Texture_to_name_cache_life = 0.0;

local Dcr_CheckingPET = false;
local Dcr_DelayedReconf = false;

Dcr_Groups_datas_are_invalid = false; -- not local because changed from the xml file when clicking on random cure

local InternalPrioList	    = { };
local InternalSkipList	    = { };
local Dcr_Unit_Array	    = { };
local Dcr_Unit_ArrayByName  = { };
local target_added = false;

local SortingTable = {};

local Dcr_CheckPet_Delay	= 2;
local Dcr_DelayedReconf_delay	= 1;

local Dcr_DelayedReconf_timer	= 0;
local Dcr_CheckPet_Timer	= 0;
local Dcr_Delay_Timer		= 0;

local last_DemonType = nil;
local curr_DemonType = nil;

local Dcr_AlreadyCleanning = false;
local Dcr_RestoreTarget = true;

local Dcr_SoundPlayed = false;

local Dcr_CombatMode = false;

local Dcr_timeLeft = 0;

Dcr_PlayerClass = "";

-- The rotatibg cure table functions array
local Curing_functions = {};

Dcr_CureTypeCheckBoxes = {};

local RestorSelfAutoCastTimeOut = 1;
local RestorSelfAutoCast = false;

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

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

-------------------------------------------------------------------------------
-- The printing functions {{{
-------------------------------------------------------------------------------
function Dcr_debug( Message) --{{{
    if (Dcr_Print_DEBUG) then
	table.insert(DCR_REMOTE_DEBUG, Message);
	Dcr_Saved.Dcr_OutputWindow:AddMessage(Message, 0.1, 0.1, 1);
    end
    return true;
end --}}}

function Dcr_debug_bis( Message) --{{{
    if (Dcr_Saved.Dcr_Print_DEBUG_bis) then
	table.insert(DCR_REMOTE_DEBUG, Message);
	Dcr_Saved.Dcr_OutputWindow:AddMessage(Message, 0.1, 0.1, 1);
    end
    return true;
end --}}}

function Dcr_Toggle_debug_bis() --{{{
    if (Dcr_Saved.Dcr_Print_DEBUG_bis) then
	Dcr_debug_bis("Debug: Disabled");
	Dcr_Saved.Dcr_Print_DEBUG_bis = false;
	DCR_REMOTE_DEBUG = {};
    else
	Dcr_Saved.Dcr_Print_DEBUG_bis = true;
	Dcr_debug_bis("Debug: Enabled");
    end
end --}}}

function MakePlayerName (name) --{{{
    return "|cFFFFAA22|Hplayer:" .. name .. "|h" .. string.upper(name) .. "|h|r";
end --}}}

function MakeAfflictionName (name) --{{{
    if (name) then
	return "|cFFFF6622" .. DCR_LOC_AF_TYPE[name] .. "|r";
    else
	return "";
    end
end --}}}

function Dcr_println( Message) --{{{

    if (Dcr_Saved.Print_ChatFrame) then
	if (Dcr_Saved.Dcr_OutputWindow) then -- this FUCKING variable find the fucking way of being fucking NIL for some players.... *spitting on LUA language*
	    Dcr_Saved.Dcr_OutputWindow:AddMessage(Message, 1, 1, 1);
	else
	    DEFAULT_CHAT_FRAME:AddMessage(Message, 1, 1, 1);
	end
    end
    if (Dcr_Saved.Print_CustomFrame) then
	DecursiveTextFrame:AddMessage(Message, 1, 1, 1, 0.9);
    end
end --}}}

function Dcr_errln( Message) --{{{
    if (Dcr_Saved.Print_Error) then
	if (Dcr_Saved.Print_ChatFrame) then
	    if (Dcr_Saved.Dcr_OutputWindow) then -- this FUCKING variable find the fucking way of being fucking NIL for some players.... *spitting on LUA language*
		Dcr_Saved.Dcr_OutputWindow:AddMessage(Message, 1, 0.1, 0.1);
	    else
		DEFAULT_CHAT_FRAME:AddMessage(Message, 1, 0.1, 0.1);
	    end
	end
	if (Dcr_Saved.Print_CustomFrame) then
	    DecursiveTextFrame:AddMessage(Message, 1, 0.1, 0.1, 0.9);
	end
    end
end --}}}

function Dcr_PrintPriorityList() --{{{
    for id, name in Dcr_Saved.PriorityList do
	Dcr_println( id.." - "..name);
    end
end --}}}

function TMP_pr() --{{{
    for index, unit in Dcr_Unit_Array do
	Dcr_println( unit.." - "..MakePlayerName((UnitName(unit))) .. " Index: "..index);
    end
end --}}}

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

-- Show Hide FUNCTIONS -- {{{

function Dcr_ShowHideAfflictedListUI()  --{{{-- here for compatibility -- deprecated
    Dcr_Hide(false);
end --}}}

function Dcr_ShowHideLiveList(hide) --{{{
    -- if hide is requested or if hide is not set and the live-list is shown
    if (hide==1 or (not hide and DecursiveAfflictedListFrame:IsVisible())) then
	Dcr_Saved.Hide_LiveList = true;
	DecursiveAfflictedListFrame:Hide();
    else
	Dcr_Saved.Hide_LiveList = false;
	DecursiveAfflictedListFrame:ClearAllPoints();
	DecursiveAfflictedListFrame:SetPoint("TOPLEFT", "DecursiveMainBar", "BOTTOMLEFT");
	DecursiveAfflictedListFrame:Show();
    end

    if (Dcr_Saved.Hide_LiveList) then
	DcrOptionsFrameHideLL:SetChecked(1);
    else
	DcrOptionsFrameHideLL:SetChecked(0);
    end

end --}}}

function Dcr_Hide(hide) --{{{

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

    if DecursiveMainBar:IsVisible() and DecursiveAfflictedListFrame:IsVisible() then
	DecursiveAfflictedListFrame:ClearAllPoints();
	DecursiveAfflictedListFrame:SetPoint("TOPLEFT", "DecursiveMainBar", "BOTTOMLEFT");
    else
	Dcr_Saved.Dcr_OutputWindow:AddMessage(DCR_SHOW_MSG, 0.3, 0.5, 1);
    end
end --}}}

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

-- skip list stuff
function Dcr_ShowHideSkipListUI() --{{{
    if (DecursiveSkipListFrame:IsVisible()) then
	DecursiveSkipListFrame:Hide();
    else
	DecursiveSkipListFrame:Show();
    end
end --}}}

function Dcr_ShowHideOptionsUI() --{{{
    if (DcrOptionsFrame:IsVisible()) then
	DcrOptionsFrame:Hide();
    else
	DcrOptionsFrame:Show();
	DcrOptionsFrame2:ClearAllPoints();
	DcrOptionsFrame2:SetPoint("TOPLEFT", "DcrOptionsFrame", "TOPRIGHT");
    end
end --}}}

function Dcr_ShowHideTextAnchor() --{{{
    if (DecursiveAnchor:IsVisible()) then
	DecursiveAnchor:Hide();
    else
	DecursiveAnchor:Show();
    end
end --}}}

function Dcr_ShowHideButtons(UseCurrentValue) --{{{

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

    DCRframeObject = getglobal(DecrFrame);

    if (not UseCurrentValue) then
	Dcr_Saved.HideButtons = (not Dcr_Saved.HideButtons);
    end

    for _, ButtonName in buttons do
	Button = getglobal(ButtonName);

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

    end



end --}}}

-- }}}

-- OPTION RELATED FUNCTIONS {{{

function Dcr_ChangeTextFrameDirection(bottom) --{{{
    buton = DecursiveAnchorDirection;
    if (bottom) then
	DecursiveTextFrame:SetInsertMode("BOTTOM");
	buton:SetText("v");
    else
	DecursiveTextFrame:SetInsertMode("TOP");
	buton:SetText("^");
    end
end --}}}


function Dcr_AmountOfAfflictedSlider_OnShow() --{{{
    getglobal(this:GetName().."High"):SetText("15");
    getglobal(this:GetName().."Low"):SetText("5");

    getglobal(this:GetName() .. "Text"):SetText(DCR_AMOUNT_AFFLIC .. Dcr_Saved.Amount_Of_Afflicted);

    this:SetMinMaxValues(1, 15);
    this:SetValueStep(1);
    this:SetValue(Dcr_Saved.Amount_Of_Afflicted);
end --}}}

function Dcr_AmountOfAfflictedSlider_OnValueChanged() --{{{
    Dcr_Saved.Amount_Of_Afflicted = this:GetValue();
    getglobal(this:GetName() .. "Text"):SetText(DCR_AMOUNT_AFFLIC .. Dcr_Saved.Amount_Of_Afflicted);
end --}}}

function Dcr_ScanTimeSlider_OnShow() --{{{
    getglobal(this:GetName().."High"):SetText("1");
    getglobal(this:GetName().."Low"):SetText("0.1");

    getglobal(this:GetName() .. "Text"):SetText(DCR_SCAN_LENGTH .. Dcr_Saved.ScanTime);

    this:SetMinMaxValues(0.1, 1);
    this:SetValueStep(0.1);
    this:SetValue(Dcr_Saved.ScanTime);
end --}}}

function Dcr_ScanTimeSlider_OnValueChanged() --{{{
    Dcr_Saved.ScanTime = this:GetValue() * 10;
    if (Dcr_Saved.ScanTime < 0) then
	Dcr_Saved.ScanTime = ceil(Dcr_Saved.ScanTime - 0.5)
    else
	Dcr_Saved.ScanTime = floor(Dcr_Saved.ScanTime + 0.5)
    end
    Dcr_Saved.ScanTime = Dcr_Saved.ScanTime / 10;
    getglobal(this:GetName() .. "Text"):SetText(DCR_SCAN_LENGTH .. Dcr_Saved.ScanTime);
end --}}}

function Dcr_CureBlacklistSlider_OnShow() --{{{
    getglobal(this:GetName().."High"):SetText("20");
    getglobal(this:GetName().."Low"):SetText("1");

    getglobal(this:GetName() .. "Text"):SetText(DCR_BLACK_LENGTH .. Dcr_Saved.CureBlacklist);

    this:SetMinMaxValues(1, 20);
    this:SetValueStep(0.1);
    this:SetValue(Dcr_Saved.CureBlacklist);
end --}}}

function Dcr_CureBlacklistSlider_OnValueChanged() --{{{
    Dcr_Saved.CureBlacklist = this:GetValue() * 10;
    if (Dcr_Saved.CureBlacklist < 0) then
	Dcr_Saved.CureBlacklist = ceil(Dcr_Saved.CureBlacklist - 0.5)
    else
	Dcr_Saved.CureBlacklist = floor(Dcr_Saved.CureBlacklist + 0.5)
    end
    Dcr_Saved.CureBlacklist = Dcr_Saved.CureBlacklist / 10;
    getglobal(this:GetName() .. "Text"):SetText(DCR_BLACK_LENGTH .. Dcr_Saved.CureBlacklist);
end --}}}



function Dcr_On_CureOrderCheckBox_Update (CheckBox) -- {{{

    --Dcr_debug_bis(CheckBox.CureType .. " cb called");
    -- if it's unchecked
    if (CheckBox.CurePos ~= 0 and not CheckBox:GetChecked()) then
	Dcr_debug_bis("===> UN checked");
	-- remove its position in the cure order list
	CheckBox.CurePos = 0;

	-- remove the debuff type from the saved ordered list
	Dcr_tremovebyval(Dcr_Saved.CureOrderList, CheckBox.CureType);

	-- remove the green number before the checkbox text
	getglobal(CheckBox:GetName().."LText"):SetText("");

	-- if done here then update the actual cure order list
	Dcr_SetCureOrderList ();
    elseif ( CheckBox.CurePos == 0 and CheckBox:GetChecked()) then -- it seems that (not CheckBox.CurePos) is not the same than (CheckBox.CurePos == false) in LUA so we forgot booleans................
	Dcr_debug_bis("===> CHECKED");

	-- set the object position
	CheckBox.CurePos = table.getn(Dcr_Saved.CureOrderList) + 1;

	-- register its type in the saved list
	Dcr_Saved.CureOrderList[CheckBox.CurePos] = CheckBox.CureType;

	-- if done here then update the actual cure order list
	Dcr_SetCureOrderList ();
    end

end -- }}}

function Dcr_SetCureOrderList () -- {{{
    -- Dcr_debug_bis("Dcr_SetCureOrderList called");
    local i;
    local j = 0;
    local CheckBox;
    local temp_table = {};

    -- clear curing functions
    Curing_functions = {};
    for i=1, 4 do
	if (Dcr_Saved.CureOrderList[i]) then
	    -- set a shortcut to our checkbox object
	    CheckBox =  Dcr_CureTypeCheckBoxes[Dcr_Saved.CureOrderList[i]];

	    -- re-index Dcr_Saved.CureOrderList
	    j = j + 1;
	    temp_table[j] = Dcr_Saved.CureOrderList[i];

	    -- register the curing function
	    Curing_functions[j] = CheckBox.CureFunction;
	    CheckBox.CurePos = j;

	    -- set a green number before the checkbox text
	    getglobal(CheckBox:GetName().."LText"):SetText("|cFF00FF00"..j.."|r ");
	end
    end

    -- save our reordered Dcr_Saved.CureOrderList
    Dcr_Saved.CureOrderList = temp_table;

end -- }}}

function VerifyOrderList ()

    local TempTable = {};
    local i;

    Dcr_debug_bis("Verifying CureOrderList...");
    -- take the 4 first value of the Dcr_Saved.CureOrderList table
    for i=1, 4 do
	-- add it only if not already listed
	if ( Dcr_Saved.CureOrderList[i] and not Dcr_tcheckforval(TempTable, Dcr_Saved.CureOrderList[i])) then
	    TempTable[i] = Dcr_Saved.CureOrderList[i];
	    Dcr_debug_bis("Already in list: "..TempTable[i]);
	end
    end

    if (Dcr_Saved.CureMagic	and	not Dcr_tcheckforval(TempTable,   DCR_MAGIC	)) then 
	table.insert(TempTable, DCR_MAGIC);
	Dcr_debug_bis("Adding " .. DCR_MAGIC);
    end

    if (Dcr_Saved.CureCurse	and	not Dcr_tcheckforval(TempTable,   DCR_CURSE	)) then 
	table.insert(TempTable, DCR_CURSE);
	Dcr_debug_bis("Adding " .. DCR_CURSE);
    end

    if (Dcr_Saved.CurePoison    and	not Dcr_tcheckforval(TempTable,   DCR_POISON	)) then
	table.insert(TempTable, DCR_POISON);
	Dcr_debug_bis("Adding " .. DCR_POISON);
    end

    if (Dcr_Saved.CureDisease   and	not Dcr_tcheckforval(TempTable,   DCR_DISEASE	)) then 
	table.insert(TempTable, DCR_DISEASE);
	Dcr_debug_bis("Adding " .. DCR_DISEASE);
    end

    Dcr_Saved.CureOrderList = TempTable;

end

function Dcr_tremovebyval(tab, val) -- {{{
    local k;
    local v;
    for k,v in tab do
	if(v==val) then
	    table.remove(tab, k);
	    return true;
	end
    end
    return false;
end -- }}}

function Dcr_tcheckforval(tab, val) -- {{{
    local k;
    local v;
    for k,v in tab do
	if(v==val) then
	    return true;
	end
    end
    return false;
end -- }}}

-- // }}}

-- this resets the location of the windows
function Dcr_ResetWindow() --{{{


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

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

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

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

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

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

    DcrOptionsFrame2:ClearAllPoints();
    DcrOptionsFrame2:SetPoint("TOPLEFT", "DcrOptionsFrame", "TOPRIGHT");
end --}}}

function Dcr_ThisSetText(text) --{{{
    getglobal(this:GetName().."Text"):SetText(text);
end --}}}

function Dcr_DisplayTooltip(Message, RelativeTo) --{{{
    DcrDisplay_Tooltip:SetOwner(RelativeTo, "ANCHOR_TOPRIGHT");
    DcrDisplay_Tooltip:ClearLines();
    DcrDisplay_Tooltip:SetText(Message);
    DcrDisplay_Tooltip:Show();
end --}}}

function Dcr_DebuffTemplate_OnEnter() --{{{
    if (Dcr_Saved.AfflictionTooltips) then
	DcrDisplay_Tooltip:SetOwner(this, "ANCHOR_CURSOR");
	DcrDisplay_Tooltip:ClearLines();
	DcrDisplay_Tooltip:SetUnitDebuff(this.unit,this.debuff); -- OK
	DcrDisplay_Tooltip:Show();
    end
end --}}}

-- Prio and skip list LIST {{{
function Dcr_PriorityListEntryTemplate_OnClick() --{{{
    local id = this:GetID();
    if (id) then
	if (this.Priority) then
	    Dcr_RemoveIDFromPriorityList(id);
	else
	    Dcr_RemoveIDFromSkipList(id);
	end
    end
    this.UpdateYourself = true;

end --}}}

function Dcr_PriorityListEntryTemplate_OnUpdate() --{{{
    if (this.UpdateYourself) then
	this.UpdateYourself = false;
	local baseName = this:GetName();
	local NameText = getglobal(baseName.."Name");

	local id = this:GetID();
	if (id) then
	    local name
	    if (this.Priority) then
		name = Dcr_Saved.PriorityList[id];
	    else
		name = Dcr_Saved.SkipList[id];
	    end
	    if (name) then
		NameText:SetText(id.." - "..name);
	    else
		NameText:SetText("Error - ID Invalid!");
	    end
	else
	    NameText:SetText("Error - No ID!");
	end
    end
end --}}}

function Dcr_PriorityListFrame_OnUpdate() --{{{
    if (this.UpdateYourself) then
	this.UpdateYourself = false;
	Dcr_Groups_datas_are_invalid = true;
	local baseName = this:GetName();
	local up = getglobal(baseName.."Up");
	local down = getglobal(baseName.."Down");


	local size = table.getn(Dcr_Saved.PriorityList);

	if (size < 11 ) then
	    this.Offset = 0;
	    up:Hide();
	    down:Hide();
	else
	    if (this.Offset <= 0) then
		this.Offset = 0;
		up:Hide();
		down:Show();
	    elseif (this.Offset >= (size - 10)) then
		this.Offset = (size - 10);
		up:Show();
		down:Hide();
	    else
		up:Show();
		down:Show();
	    end
	end

	local i;
	for i = 1, 10 do
	    local id = ""..i;
	    if (i < 10) then
		id = "0"..i;
	    end
	    local btn = getglobal(baseName.."Index"..id);

	    btn:SetID( i + this.Offset);
	    btn.UpdateYourself = true;

	    if (i <= size) then
		btn:Show();
	    else
		btn:Hide();
	    end
	end
    end

end --}}}

function Dcr_SkipListFrame_OnUpdate() --{{{
    if (this.UpdateYourself) then
	this.UpdateYourself = false;
	Dcr_Groups_datas_are_invalid = true;
	local baseName = this:GetName();
	local up = getglobal(baseName.."Up");
	local down = getglobal(baseName.."Down");


	local size = table.getn(Dcr_Saved.SkipList);

	if (size < 11 ) then
	    this.Offset = 0;
	    up:Hide();
	    down:Hide();
	else
	    if (this.Offset <= 0) then
		this.Offset = 0;
		up:Hide();
		down:Show();
	    elseif (this.Offset >= (size - 10)) then
		this.Offset = (size - 10);
		up:Show();
		down:Hide();
	    else
		up:Show();
		down:Show();
	    end
	end

	local i;
	for i = 1, 10 do
	    local id = ""..i;
	    if (i < 10) then
		id = "0"..i;
	    end
	    local btn = getglobal(baseName.."Index"..id);

	    btn:SetID( i + this.Offset);
	    btn.UpdateYourself = true;

	    if (i <= size) then
		btn:Show();
	    else
		btn:Hide();
	    end
	end
    end

end --}}}

function Dcr_PopulateButtonPress() --{{{
    local addFunction = this:GetParent().addFunction;

    if (this.ClassType) then
	-- for the class type stuff... we do party

	local _, pclass = UnitClass("player");
	if (pclass == this.ClassType) then
	    addFunction("player");
	end

	_, pclass = UnitClass("party1");
	if (pclass == this.ClassType) then
	    addFunction("party1");
	end
	_, pclass = UnitClass("party2");
	if (pclass == this.ClassType) then
	    addFunction("party2");
	end
	_, pclass = UnitClass("party3");
	if (pclass == this.ClassType) then
	    addFunction("party3");
	end
	_, pclass = UnitClass("party4");
	if (pclass == this.ClassType) then
	    addFunction("party4");
	end
    end

    local max = GetNumRaidMembers();
    local i;
    if (max > 0) then
	for i = 1, max do
	    local _, _, pgroup, _, _, pclass = GetRaidRosterInfo(i);

	    if (this.ClassType) then
		if (pclass == this.ClassType) then
		    addFunction("raid"..i);
		end
	    end
	    if (this.GroupNumber) then
		if (pgroup == this.GroupNumber) then
		    addFunction("raid"..i);
		end
	    end
	end
    end

end --}}}



-- }}}

function Dcr_PlaySound () --{{{
    if (Dcr_Saved.PlaySound and not Dcr_SoundPlayed) then
	-- good sounds: Sound\\Doodad\\BellTollTribal.wav
	--		Sound\\interface\\AuctionWindowOpen.wav
	PlaySoundFile("Sound\\Doodad\\BellTollTribal.wav");
	Dcr_SoundPlayed = true;
    end
end --}}}


-- LIVE DISPLAY functions {{{

function Dcr_AfflictedListFrame_OnUpdate(elapsed) --{{{


    -- XXX find the use of this block
    if Dcr_Saved.Amount_Of_Afflicted < 1 then
	Dcr_Saved.Amount_Of_Afflicted = 1;
    elseif Dcr_Saved.Amount_Of_Afflicted > DCR_MAX_LIVE_SLOTS then
	Dcr_Saved.Amount_Of_Afflicted = DCR_MAX_LIVE_SLOTS;
    end

    Dcr_timeLeft = Dcr_timeLeft - elapsed;
    if (Dcr_timeLeft <= 0) then
	Dcr_timeLeft = Dcr_Saved.ScanTime;
	local Dcr_Unit_Array = Dcr_Unit_Array;
	local index = 1;
	local targetexists = false;
	Dcr_GetUnitArray();

	-- First scan the current target
	if (UnitExists("target") and UnitIsFriend("target", "player")) then
	    if (UnitIsVisible("target")) then
		targetexists = true;
		if (Dcr_ScanUnit("target", index)) then
		    if (index == 1) then
			Dcr_PlaySound();
		    end
		    index = index + 1;
		end
	    end
	end

	if (DCR_CAN_CURE_ENEMY_MAGIC) then
	    for _, unit in Dcr_Unit_Array do
		if (index > Dcr_Saved.Amount_Of_Afflicted) then
		    break;
		end
		if (UnitIsVisible(unit) and not (targetexists and UnitIsUnit(unit, "target"))) then
		    -- if the unit is even close by
		    if (UnitIsCharmed(unit)) then
			-- if the unit is mind controlled
			if (Dcr_ScanUnit(unit, index)) then
			    if (index == 1) then
				Dcr_PlaySound();
			    end
			    index = index + 1;
			end
		    end
		end
	    end
	end

	-- Dcr_debug(" normal loop");
	for _, unit in Dcr_Unit_Array do
	    if (index > Dcr_Saved.Amount_Of_Afflicted) then
		break;
	    end
	    if (UnitIsVisible(unit) and not (targetexists and UnitIsUnit(unit, "target"))) then
		if (not UnitIsCharmed(unit)) then
		    -- if the unit is even close by
		    if (Dcr_ScanUnit(unit, index)) then
			if (index == 1) then
			    Dcr_PlaySound();
			end
			index = index + 1;
		    end
		end
	    end
	end

	-- clear livelist
	local i;
	for i = index, DCR_MAX_LIVE_SLOTS do
	    if i == 1 then
		Dcr_SoundPlayed = false;
	    end
	    local Index = i;
	    if (Dcr_Saved.ReverseLiveDisplay and not (i > Dcr_Saved.Amount_Of_Afflicted)) then
		Index = Dcr_Saved.Amount_Of_Afflicted + -1 * (Index - 1);
	    end
	    local item = getglobal("DecursiveAfflictedListFrameListItem"..Index);
	    item.unit = "player";
	    item.debuff = 0;
	    item:Hide();
	end

	-- for testing only		
	-- Dcr_UpdateLiveDisplay( 1, "player", 1)

    end
end --}}}

function Dcr_ScanUnit( Unit, Index) --{{{
    local AllUnitDebuffs = {};

    AllUnitDebuffs = Dcr_GetUnitDebuffAll(Unit);

    for debuff_name, debuff_params in AllUnitDebuffs do
	-- test if we have to ignore this debuf  {{{ --
	if (DCR_IGNORELIST[debuff_name]) then
	    -- these are the BAD ones... the ones that make the target immune... abort the user
	    -- Dcr_debug( string.gsub( string.gsub(DCR_IGNORE_STRING, "$t", (UnitName(Unit))), "$a", debuff_name));
	    return false;
	end

	if (DCR_SKIP_LIST[debuff_name]) then
	    -- these are just ones you don't care about
	    -- Dcr_debug( string.gsub( string.gsub(DCR_IGNORE_STRING, "$t", (UnitName(Unit))), "$a", debuff_name));
	    break;
	end

	if (UnitAffectingCombat("player")) then
	    if (DCR_SKIP_BY_CLASS_LIST[UClass]) then
		if (DCR_SKIP_BY_CLASS_LIST[UClass][debuff_name]) then
		    -- these are just ones you don't care about by class
		    -- Dcr_debug( string.gsub( string.gsub(DCR_IGNORE_STRING, "$t", (UnitName(Unit))), "$a", debuff_name));
		    break;
		end
	    end
	end
	-- }}}

	if (debuff_params.debuff_type and debuff_params.debuff_type ~= "") then -- debuff_type can be an empty string
	    -- // {{{ --
	    if (debuff_params.debuff_type == DCR_MAGIC and Dcr_Saved.CureMagic) then
		if (UnitIsCharmed(Unit)) then
		    if (DCR_CAN_CURE_ENEMY_MAGIC) then
			Dcr_UpdateLiveDisplay(Index, Unit, debuff_params);
			return true;
		    end
		else
		    if (DCR_CAN_CURE_MAGIC) then
			Dcr_UpdateLiveDisplay(Index, Unit, debuff_params);
			return true;
		    end
		end
	    elseif (debuff_params.debuff_type == DCR_DISEASE and Dcr_Saved.CureDisease) then
		if (DCR_CAN_CURE_DISEASE) then
		    Dcr_UpdateLiveDisplay(Index, Unit, debuff_params);
		    return true;
		end
	    elseif (debuff_params.debuff_type == DCR_POISON and Dcr_Saved.CurePoison) then
		if (DCR_CAN_CURE_POISON) then
		    Dcr_UpdateLiveDisplay(Index, Unit, debuff_params);
		    return true;
		end
	    elseif (debuff_params.debuff_type == DCR_CURSE and Dcr_Saved.CureCurse) then
		if (DCR_CAN_CURE_CURSE) then
		    -- Dcr_debug_bis("updating for curse");
		    Dcr_UpdateLiveDisplay(Index, Unit, debuff_params);
		    return true;
		end
	    end
	    -- // }}}
	end
    end
    return false;
end --}}}

function Dcr_UpdateLiveDisplay( Index, Unit, debuff_params) --{{{

    if (Dcr_Saved.ReverseLiveDisplay) then
	Index = Dcr_Saved.Amount_Of_Afflicted + -1 * (Index - 1);
    end

    local baseFrame = "DecursiveAfflictedListFrameListItem";

    local item = getglobal(baseFrame..Index);
    if (item.debuff == debuff_params.index and item.debuff_name == debuff_params.debuff_name and item.unit == Unit and item.DebuffApps == debuff_params.debuffApplications) then
	-- it's already displayed...
	return
    end
    Dcr_debug_bis("Updating: "..baseFrame.. Index);

    item.unit = Unit;
    item.debuff_name = debuff_params.debuff_name;
    item.debuff = debuff_params.index;
    item.DebuffApps = debuff_params.debuffApplications;

    getglobal(baseFrame..Index.."DebuffIcon"):SetTexture(debuff_params.DebuffTexture);

    if (debuff_params.debuffApplications > 0) then
	getglobal(baseFrame..Index.."DebuffCount"):SetText(debuff_params.debuffApplications);
    else
	getglobal(baseFrame..Index.."DebuffCount"):SetText("");
    end

    getglobal(baseFrame..Index.."Name"):SetText(MakePlayerName((UnitName(Unit))));

    getglobal(baseFrame..Index.."Type"):SetText(MakeAfflictionName(debuff_params.debuff_type));

    getglobal(baseFrame..Index.."Affliction"):SetText(debuff_params.debuff_name);
    --
    --
    -- item.UpdateMe = true;
    item:Show();

    item = getglobal(baseFrame..Index.."Debuff");
    item.unit = Unit;
    item.debuff = debuff_params.index;

    item = getglobal(baseFrame..Index.."ClickMe");
    item.unit = Unit;
    item.debuff = debuff_params.index;
end --}}}

    
-- }}}

-- // }}}
-------------------------------------------------------------------------------
-- TODO see how to implement a conditional rescan (use RAID_ROSTER_UPDATE)
-------------------------------------------------------------------------------
-- the priority and skip list function... stuff to manage the lists // {{{
-------------------------------------------------------------------------------

function Dcr_AddTargetToPriorityList() --{{{
    Dcr_debug( "Adding the target to the priority list");
    DcrAddUnitToPriorityList("target");
end --}}}

function DcrAddUnitToPriorityList( unit) --{{{
    if (UnitExists(unit)) then
	if (UnitIsPlayer(unit)) then
	    local name = (UnitName( unit));
	    for _, pname in Dcr_Saved.PriorityList do
		if (name == pname) then
		    return;
		end
	    end
	    table.insert(Dcr_Saved.PriorityList,name);
	end
	DecursivePriorityListFrame.UpdateYourself = true;
    end
end --}}}

function Dcr_RemoveIDFromPriorityList(id) --{{{
    table.remove( Dcr_Saved.PriorityList,id);
    DecursivePriorityListFrame.UpdateYourself = true;
end --}}}

function Dcr_ClearPriorityList() --{{{
    Dcr_Saved.PriorityList = {};
    
    DecursivePriorityListFrame.UpdateYourself = true;
end --}}}

function Dcr_AddTargetToSkipList() --{{{
    Dcr_debug( "Adding the target to the Skip list");
    DcrAddUnitToSkipList("target");
end --}}}

function DcrAddUnitToSkipList( unit) --{{{
    if (UnitExists(unit)) then
	if (UnitIsPlayer(unit)) then
	    local name = (UnitName( unit));
	    for _, pname in Dcr_Saved.SkipList do
		if (name == pname) then
		    return;
		end
	    end
	    table.insert(Dcr_Saved.SkipList,name);
	    DecursiveSkipListFrame.UpdateYourself = true;
	end
    end
end --}}}

function Dcr_RemoveIDFromSkipList(id) --{{{
    table.remove( Dcr_Saved.SkipList,id);
    DecursiveSkipListFrame.UpdateYourself = true;
end --}}}

function Dcr_ClearSkipList() --{{{
    local i;

    Dcr_Saved.SkipList = { };
    --[[
    local max = table.getn(Dcr_Saved.SkipList);
    for i = 1, max do
	table.remove( Dcr_Saved.SkipList);
    end
    ]]
    DecursiveSkipListFrame.UpdateYourself = true;
end --}}}

function Dcr_PrintSkipList() --{{{
    for id, name in Dcr_Saved.SkipList do
	Dcr_println( id.." - "..name);
    end
end --}}}

function Dcr_IsInPriorList (name) --{{{
    for _, PriorName in Dcr_Saved.PriorityList do
	if (PriorName == name) then
	    return true;
	end
    end
    return false;
end --}}}

function Dcr_IsInSkipList (name) --{{{
    for _, SkipName in Dcr_Saved.SkipList do
	if (SkipName == name) then
	    return true;
	end
    end
    return false
end --}}}

function Dcr_IsInSkipOrPriorList( name )  --{{{-- used internally
    if (InternalSkipList[name]) then
	return true;
    end

    if (InternalPrioList[name]) then
	return true;
    end
    return false;


end --}}}
-- }}}
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-- init functions and configuration functions {{{
-------------------------------------------------------------------------------
StaticPopupDialogs["DCR_DISABLE_AUTOSELFCAST"] = {
  text = DCR_DISABLE_AUTOSELFCAST,
  button1 = TEXT(ACCEPT),
  button2 = TEXT(CANCEL),
  OnAccept = function()
      SetCVar("autoSelfCast", "0");
  end,
  timeout = 0,
  whileDead = 1,
  hideOnEscape = 1,
  ShowAlert = 1,
};

function Dcr_CheckAuSelfCastStaus ()
    if (GetCVar("autoSelfCast") == "1") then
	StaticPopup_Show ("DCR_DISABLE_AUTOSELFCAST", AUTO_SELF_CAST_TEXT);
    end

end


function Dcr_Init() --{{{

    -- I don't know for you but I personally hate Dcr_Saved.Dcr_OutputWindow...
    if (Dcr_Saved.Dcr_OutputWindow == nil or not Dcr_Saved.Dcr_OutputWindow) then
	Dcr_Saved.Dcr_OutputWindow = DEFAULT_CHAT_FRAME;
    end

    Dcr_println(DCR_VERSION_STRING);

    Dcr_debug_bis( "Decursive Initialization started!");
    -- Dcr_CheckAuSelfCastStaus ();

    -- Register the / cmds {{{
    Dcr_debug( "Registering the slash commands");
    SLASH_DECURSIVE1 = DCR_MACRO_COMMAND;
    SlashCmdList["DECURSIVE"] = function(msg)
	Dcr_Clean();
    end

    SLASH_DECURSIVEPRADD1 = DCR_MACRO_PRADD;
    SlashCmdList["DECURSIVEPRADD"] = function(msg)
	Dcr_AddTargetToPriorityList();
    end
    SLASH_DECURSIVEPRCLEAR1 = DCR_MACRO_PRCLEAR;
    SlashCmdList["DECURSIVEPRCLEAR"] = function(msg)
	Dcr_ClearPriorityList();
    end
    SLASH_DECURSIVEPRLIST1 = DCR_MACRO_PRLIST;
    SlashCmdList["DECURSIVEPRLIST"] = function(msg)
	Dcr_PrintPriorityList();
    end
    SLASH_DECURSIVEPRSHOW1 = DCR_MACRO_PRSHOW;
    SlashCmdList["DECURSIVEPRSHOW"] = function(msg)
	Dcr_ShowHidePriorityListUI();
    end

    SLASH_DECURSIVESKADD1 = DCR_MACRO_SKADD;
    SlashCmdList["DECURSIVESKADD"] = function(msg)
	Dcr_AddTargetToSkipList();
    end
    SLASH_DECURSIVESKCLEAR1 = DCR_MACRO_SKCLEAR;
    SlashCmdList["DECURSIVESKCLEAR"] = function(msg)
	Dcr_ClearSkipList();
    end
    SLASH_DECURSIVESKLIST1 = DCR_MACRO_SKLIST;
    SlashCmdList["DECURSIVESKLIST"] = function(msg)
	Dcr_PrintSkipList();
    end
    SLASH_DECURSIVESKSHOW1 = DCR_MACRO_SKSHOW;
    SlashCmdList["DECURSIVESKSHOW"] = function(msg)
	Dcr_ShowHideSkipListUI();
    end

    SLASH_DECURSIVESHOW1 = DCR_MACRO_SHOW;
    SlashCmdList["DECURSIVESHOW"] = function(msg)
	Dcr_Hide(0);
    end

    SLASH_DECURSIVERESET1 = DCR_MACRO_RESET;
    SlashCmdList["DECURSIVERESET"] = function(msg)
	Dcr_ResetWindow();
    end

    SLASH_DECURSIVEHIDE1 = DCR_MACRO_HIDE;
    SlashCmdList["DECURSIVEHIDE"] = function(msg)
	Dcr_Hide(1);
    end

    SLASH_DECURSIVEOPTION1 = DCR_MACRO_OPTION;
    SlashCmdList["DECURSIVEOPTION"] = function(msg)
	Dcr_ShowHideOptionsUI();
    end

    SLASH_DECURSIVEDBGBISTOG1 = DCR_MACRO_DEBUG
    SlashCmdList["DECURSIVEDBGBISTOG"] = function(msg)
	Dcr_Toggle_debug_bis();
    end

    -- }}}

    if (Dcr_Saved.Hide_LiveList) then
	DecursiveAfflictedListFrame:Hide();
    else
	DecursiveAfflictedListFrame:ClearAllPoints();
	DecursiveAfflictedListFrame:SetPoint("TOPLEFT", "DecursiveMainBar", "BOTTOMLEFT");
	DecursiveAfflictedListFrame:Show();
    end

    if (Dcr_Saved.Hidden) then
	DecursiveMainBar:Hide();
    else
	DecursiveMainBar:Show();
    end

    if (Dcr_Saved.AlwaysUseBestSpell == nil) then
	Dcr_Saved.AlwaysUseBestSpell = true;
    end

    if (Dcr_Saved.HideButtons == nil) then
	Dcr_Saved.HideButtons = false;
    end

    if (Dcr_Saved.CureMagic == nil) then
	Dcr_Saved.CureMagic = true;
    end

    if (Dcr_Saved.CurePoison == nil) then
	Dcr_Saved.CurePoison = true;
    end

    if (Dcr_Saved.CureDisease == nil) then
	Dcr_Saved.CureDisease = true;
    end

    if (Dcr_Saved.CureCurse == nil) then
	Dcr_Saved.CureCurse = true;
    end

    if (Dcr_Saved.AfflictionTooltips == nil) then
	Dcr_Saved.AfflictionTooltips = true;
    end
    

    if (Dcr_Saved.CureOrderList == nil) then
	Dcr_Saved.CureOrderList = {
	    [1] = DCR_MAGIC,
	    [2] = DCR_CURSE,
	    [3] = DCR_POISON,
	    [4] = DCR_DISEASE
	}
    end


    Dcr_ShowHideButtons(true);

    Dcr_ChangeTextFrameDirection(Dcr_Saved.CustomeFrameInsertBottom);

    if (Dcr_Saved.Print_CustomFrame) then
	DcrOptionsFrameAnchor:Enable();
    else
	DcrOptionsFrameAnchor:Disable();
    end
    -- check the spellbook once
    Dcr_Configure();
    Dcr_Saved.Dcr_OutputWindow:AddMessage(DCR_IS_HERE_MSG, 0.3, 0.5, 1);
    Dcr_Saved.Dcr_OutputWindow:AddMessage(DCR_SHOW_MSG, 0.3, 0.5, 1);

    DecursiveTextFrame:SetFading(true);
    DecursiveTextFrame:SetFadeDuration(DCR_TEXT_LIFETIME / 3);
    DecursiveTextFrame:SetTimeVisible(DCR_TEXT_LIFETIME);
    
    -- add support Earth panel
    if(EarthFeature_AddButton) then
	EarthFeature_AddButton(
	{
	    id = "Decursive";
	    name = BINDING_HEADER_DECURSIVE;
	    subtext = DCR_SKIP_OPT_STR;
	    tooltip = BINDING_NAME_DCRSHOW;
	    icon = "Interface\\Icons\\Spell_Nature_RemoveCurse";
	    callback = Dcr_ShowHidePriorityListUI;
	}
        );
    end




end --}}}

function Dcr_ReConfigure() --{{{

    if not DCR_HAS_SPELLS then
	return;
    end

    Dcr_debug_bis("Dcr_ReConfigure was called!");

    local DoNotReconfigure = true;

    DoNotReconfigure = Dcr_CheckSpellName(DCR_SPELL_MAGIC_1[1], DCR_SPELL_MAGIC_1[2], DCR_SPELL_MAGIC_1[3]);
    DoNotReconfigure = Dcr_CheckSpellName(DCR_SPELL_MAGIC_2[1], DCR_SPELL_MAGIC_2[2], DCR_SPELL_MAGIC_2[3]);

    DoNotReconfigure = Dcr_CheckSpellName(DCR_SPELL_ENEMY_MAGIC_1[1], DCR_SPELL_ENEMY_MAGIC_1[2], DCR_SPELL_ENEMY_MAGIC_1[3]);
    DoNotReconfigure = Dcr_CheckSpellName(DCR_SPELL_ENEMY_MAGIC_2[1], DCR_SPELL_ENEMY_MAGIC_2[2], DCR_SPELL_ENEMY_MAGIC_2[3]);

    DoNotReconfigure = Dcr_CheckSpellName(DCR_SPELL_DISEASE_1[1], DCR_SPELL_DISEASE_1[2], DCR_SPELL_DISEASE_1[3]);
    DoNotReconfigure = Dcr_CheckSpellName(DCR_SPELL_DISEASE_2[1], DCR_SPELL_DISEASE_2[2], DCR_SPELL_DISEASE_2[3]);

    DoNotReconfigure = Dcr_CheckSpellName(DCR_SPELL_POISON_1[1], DCR_SPELL_POISON_1[2], DCR_SPELL_POISON_1[3]);
    DoNotReconfigure = Dcr_CheckSpellName(DCR_SPELL_POISON_2[1], DCR_SPELL_POISON_2[2], DCR_SPELL_POISON_2[3]);

    DoNotReconfigure = Dcr_CheckSpellName(DCR_SPELL_CURSE[1], DCR_SPELL_CURSE[2], DCR_SPELL_CURSE[3]);

    if DoNotReconfigure == false then
	Dcr_debug_bis("Dcr_ReConfigure RECONFIGURATION!");
	Dcr_Configure();
	return;
    end
    Dcr_debug_bis("Dcr_ReConfigure No reconfiguration required!");

end --}}}

function Dcr_Configure() --{{{

    -- first empty out the old "spellbook"
    DCR_HAS_SPELLS = false;
    DCR_SPELL_MAGIC_1 = {0,"", ""};
    DCR_SPELL_MAGIC_2 = {0,"", ""};
    DCR_CAN_CURE_MAGIC = false;
    DCR_SPELL_ENEMY_MAGIC_1 = {0,"", ""};
    DCR_SPELL_ENEMY_MAGIC_2 = {0,"", ""};
    DCR_CAN_CURE_ENEMY_MAGIC = false;
    DCR_SPELL_DISEASE_1 = {0,"", ""};
    DCR_SPELL_DISEASE_2 = {0,"", ""};
    DCR_CAN_CURE_DISEASE = false;
    DCR_SPELL_POISON_1 = {0,"", ""};
    DCR_SPELL_POISON_2 = {0,"", ""};
    DCR_CAN_CURE_POISON = false;
    DCR_SPELL_CURSE = {0,"", ""};
    DCR_CAN_CURE_CURSE = false;


    Dcr_debug_bis("Configuring Decursive...");
    -- parse through the entire library...
    -- look for known cleaning spells...
    -- this will be called everytime the spellbook changes

    -- this is just used to make things simpler in the checking
    local Dcr_Name_Array = {
	[DCR_SPELL_CURE_DISEASE] = true,
	[DCR_SPELL_ABOLISH_DISEASE] = true,
	[DCR_SPELL_PURIFY] = true,
	[DCR_SPELL_CLEANSE] = true,
	[DCR_SPELL_DISPELL_MAGIC] = true,
	[DCR_SPELL_CURE_POISON] = true,
	[DCR_SPELL_ABOLISH_POISON] = true,
	[DCR_SPELL_REMOVE_LESSER_CURSE] = true,
	[DCR_SPELL_REMOVE_CURSE] = true,
	[DCR_SPELL_PURGE] = true,
	[DCR_PET_FEL_CAST] = true,
	[DCR_PET_DOOM_CAST] = true,
    }

    local i = 1;

    local BookType = BOOKTYPE_SPELL;
    local break_flag = false
    while not break_flag  do
	while (true) do -- I wish there was a continue statement in LUA...
	    local spellName, spellRank = GetSpellName(i, BookType);
	    if (not spellName) then
		if (BookType == BOOKTYPE_PET) then
		    break_flag = true;
		    break;
		end
		BookType = BOOKTYPE_PET;
		i = 1;
		break;

	    end

	    Dcr_debug( "Checking spell - "..spellName);

	    if (Dcr_Name_Array[spellName]) then
		Dcr_debug( "Its one we care about");
		DCR_HAS_SPELLS = true;

		-- any of them will work for the cooldown... we store the last
		DCR_SPELL_COOLDOWN_CHECK[1] = i; DCR_SPELL_COOLDOWN_CHECK[2] = BookType;

		-- put it in the range icon array
		--local icon = GetSpellTexture(i, BookType)
		--Dcr_CuringAction_Icons[icon] = spellName;

		-- print out the spell
		Dcr_debug( string.gsub(DCR_SPELL_FOUND, "$s", spellName));
		if (Dcr_Print_Spell_Found) then
		    Dcr_println( string.gsub(DCR_SPELL_FOUND, "$s", spellName));
		end

		-- big ass if statement... due to the way that the different localizations work
		-- I used to do this more elegantly... but the german WoW broke it

		if ((spellName == DCR_SPELL_CURE_DISEASE) or (spellName == DCR_SPELL_ABOLISH_DISEASE) or
		    (spellName == DCR_SPELL_PURIFY) or (spellName == DCR_SPELL_CLEANSE)) then
		    DCR_CAN_CURE_DISEASE = true;
		    if ((spellName == DCR_SPELL_CURE_DISEASE) or (spellName == DCR_SPELL_PURIFY)) then
			Dcr_debug_bis( "Adding to disease 1");
			DCR_SPELL_DISEASE_1[1] = i; DCR_SPELL_DISEASE_1[2] = BookType; DCR_SPELL_DISEASE_1[3] = spellName;
		    else
			Dcr_debug_bis( "Adding to disease 2");
			DCR_SPELL_DISEASE_2[1] = i; DCR_SPELL_DISEASE_2[2] = BookType; DCR_SPELL_DISEASE_2[3] = spellName;
		    end
		end

		if ((spellName == DCR_SPELL_CURE_POISON) or (spellName == DCR_SPELL_ABOLISH_POISON) or
		    (spellName == DCR_SPELL_PURIFY) or (spellName == DCR_SPELL_CLEANSE)) then
		    DCR_CAN_CURE_POISON = true;
		    if ((spellName == DCR_SPELL_CURE_POISON) or (spellName == DCR_SPELL_PURIFY)) then
			Dcr_debug_bis( "Adding to poison 1");
			DCR_SPELL_POISON_1[1] = i; DCR_SPELL_POISON_1[2] = BookType; DCR_SPELL_POISON_1[3] = spellName;
		    else
			Dcr_debug_bis( "Adding to poison 2");
			DCR_SPELL_POISON_2[1] = i; DCR_SPELL_POISON_2[2] = BookType; DCR_SPELL_POISON_2[3] = spellName;
		    end
		end

		if ((spellName == DCR_SPELL_REMOVE_CURSE) or (spellName == DCR_SPELL_REMOVE_LESSER_CURSE)) then
		    Dcr_debug_bis( "Adding to curse");
		    DCR_CAN_CURE_CURSE = true;
		    DCR_SPELL_CURSE[1] = i; DCR_SPELL_CURSE[2] =  BookType; DCR_SPELL_CURSE[3] = spellName;
		end

		if ((spellName == DCR_SPELL_DISPELL_MAGIC) or (spellName == DCR_SPELL_CLEANSE) or (spellName == DCR_PET_FEL_CAST) or (spellName == DCR_PET_DOOM_CAST)) then
		    DCR_CAN_CURE_MAGIC = true;
		    if (spellName == DCR_SPELL_CLEANSE) then
			Dcr_debug_bis( "Adding to magic 1");
			DCR_SPELL_MAGIC_1[1] = i; DCR_SPELL_MAGIC_1[2] = BookType; DCR_SPELL_MAGIC_1[3] = spellName;
		    else
			if (spellRank == DCR_SPELL_RANK_1) then
			    Dcr_debug_bis( "Adding to magic 1");
			    DCR_SPELL_MAGIC_1[1] = i; DCR_SPELL_MAGIC_1[2] = BookType; DCR_SPELL_MAGIC_1[3] = spellName;
			else
			    Dcr_debug( "adding to magic 2");
			    Dcr_debug_bis( "Adding to magic 2");
			    DCR_SPELL_MAGIC_2[1] = i; DCR_SPELL_MAGIC_2[2] = BookType; DCR_SPELL_MAGIC_2[3] = spellName;
			end
		    end
		end

		if ((spellName == DCR_SPELL_DISPELL_MAGIC) or (spellName == DCR_SPELL_PURGE) or (spellName == DCR_PET_FEL_CAST) or (spellName == DCR_PET_DOOM_CAST)) then
		    DCR_CAN_CURE_ENEMY_MAGIC = true;
		    if (spellRank == DCR_SPELL_RANK_1) then
			Dcr_debug_bis( "Adding to enemy magic 1");
			DCR_SPELL_ENEMY_MAGIC_1[1] = i; DCR_SPELL_ENEMY_MAGIC_1[2] = BookType; DCR_SPELL_ENEMY_MAGIC_1[3] = spellName;
		    else
			Dcr_debug_bis( "Adding to enemy magic 2");
			DCR_SPELL_ENEMY_MAGIC_2[1] = i; DCR_SPELL_ENEMY_MAGIC_2[2] = BookType; DCR_SPELL_ENEMY_MAGIC_2[3] = spellName;
		    end
		end

	    end

	    i = i + 1
	end
    end

    local _, PlayerClass = UnitClass("player");

    Dcr_PlayerClass = PlayerClass;

    -- verify OrderList consistency
    VerifyOrderList();
    -- configure the cure order list
    Dcr_SetCureOrderList ();

end --}}}

function Dcr_CheckSpellName (id, booktype, spellname) --{{{

    if id ~= 0  then
	Dcr_debug_bis("testing spell for name changes: id="..id);
	local found_spellname, spellrank = GetSpellName(id, booktype);

	if spellname ~= found_spellname then
	    return false;
	end
    end

    return true;
end --}}}


--[[
function Dcr_DecurseChoose (button, )
end
--]]

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

-------------------------------------------------------------------------------
-- EVENT AND MAIN ONUPDATE FUNCTIONS {{{
-------------------------------------------------------------------------------
function Dcr_OnLoad (Frame) --{{{
   Frame:RegisterEvent("PLAYER_LOGIN");
end --}}}

local frame=CreateFrame("Frame");
frame:RegisterEvent("VARIABLES_LOADED");
frame:SetScript("OnEvent",function(self,event,...)
   if (Dcr_Saved.CureMutatingInjection) then
      DCR_SKIP_LIST["Mutating Injection"] = false
   end
   if (Dcr_Saved.CureWyvernSting) then
      DCR_SKIP_LIST["Wyvern Sting"] = false
   end
end);

function Dcr_OnEvent (event) --{{{
    local Frame = this;

     -- Dcr_debug_bis ("Event was catch: " .. event);

    if (event == "UNIT_PET" ) then
	if (UnitInRaid(arg1) or UnitInParty(arg1)) then
	    Dcr_Groups_datas_are_invalid = true;
	end
	if ( arg1 == "player" and not Dcr_CheckingPET) then
	    Dcr_CheckingPET = true;
	    Dcr_debug_bis ("PLAYER pet detected! Poll in 2 seconds");
	end
	return;
    elseif (event == "PLAYER_ENTER_COMBAT") then
	Dcr_EnterCombat();
	return;
    elseif (event == "PLAYER_LEAVE_COMBAT") then
	Dcr_LeaveCombat();
	return;
    elseif (event == "UI_ERROR_MESSAGE") then

	if (arg1 == SPELL_FAILED_LINE_OF_SIGHT or arg1 == SPELL_FAILED_BAD_TARGETS) then
	    Dcr_SpellCastFailed();

	    -- Throw an error if WE were casting something
	    if (Dcr_Casting_Spell_On and arg1 == SPELL_FAILED_LINE_OF_SIGHT) then
		Dcr_errln("Out of sight!");
	    end

	end

	return;

    elseif (event == "SPELLCAST_STOP") then
	Dcr_SpellWasCast();
	return;
    elseif (not Dcr_DelayedReconf and event == "SPELLS_CHANGED" and arg1==nil) then
	Dcr_DelayedReconf = true;
	return;
    elseif (event == "PARTY_MEMBERS_CHANGED" or event == "PARTY_LEADER_CHANGED") then
	Dcr_Groups_datas_are_invalid = true; 
	Dcr_debug_bis("Groups changed");
	return;
    elseif (event == "LEARNED_SPELL_IN_TAB") then
	Dcr_Configure();
	return;
    end

    if (event == "PLAYER_LOGIN") then
	Frame:RegisterEvent("PLAYER_ENTERING_WORLD");
	Frame:RegisterEvent("PLAYER_LEAVING_WORLD");
	Dcr_Init();
	return;
    end

    if (
	(event == "PLAYER_ENTERING_WORLD")
	) then
	Dcr_Groups_datas_are_invalid = true;
	Frame:RegisterEvent("PLAYER_ENTER_COMBAT");
	Frame:RegisterEvent("PLAYER_LEAVE_COMBAT");
	-- Frame:RegisterEvent("SPELLCAST_FAILED");
	-- Frame:RegisterEvent("SPELLCAST_INTERRUPTED");
	Frame:RegisterEvent("SPELLCAST_STOP");
	Frame:RegisterEvent("UNIT_PET");
	Frame:RegisterEvent("SPELLS_CHANGED");
	Frame:RegisterEvent("LEARNED_SPELL_IN_TAB");
	Frame:RegisterEvent("UI_ERROR_MESSAGE");
	
	Frame:RegisterEvent("PARTY_MEMBERS_CHANGED");
	Frame:RegisterEvent("PARTY_LEADER_CHANGED");

    elseif (event == "PLAYER_LEAVING_WORLD") then

	Frame:UnregisterEvent("PLAYER_ENTER_COMBAT");
	Frame:UnregisterEvent("PLAYER_LEAVE_COMBAT");
	-- Frame:UnregisterEvent("SPELLCAST_FAILED");
	-- Frame:UnregisterEvent("SPELLCAST_INTERRUPTED");
	Frame:UnregisterEvent("SPELLCAST_STOP");
	Frame:UnregisterEvent("UNIT_PET");
	Frame:UnregisterEvent("SPELLS_CHANGED");
	Frame:UnregisterEvent("LEARNED_SPELL_IN_TAB");
	Frame:UnregisterEvent("UI_ERROR_MESSAGE");
	
	Frame:UnregisterEvent("PARTY_MEMBERS_CHANGED");
	Frame:UnregisterEvent("PARTY_LEADER_CHANGED");
    end

end --}}}

-- This function update Decursive states :
--   - Reconf if needed
--   - check for a pet
--   - clear the black list
--   - clear cached buff and debuffs
function Dcr_OnUpdate(arg1) --{{{


    --checkinfgfor reconf need
    if Dcr_DelayedReconf then
	Dcr_DelayedReconf_timer = Dcr_DelayedReconf_timer + arg1;
	if (Dcr_DelayedReconf_timer >= Dcr_DelayedReconf_delay) then
	    Dcr_DelayedReconf_timer = 0;

	    Dcr_ReConfigure();
	    Dcr_DelayedReconf = false;
	    return;
	end
    end

    -- checking for pet
    if (Dcr_CheckingPET) then
	Dcr_CheckPet_Timer = Dcr_CheckPet_Timer + arg1;

	if (Dcr_CheckPet_Timer >= Dcr_CheckPet_Delay) then
	    Dcr_CheckPet_Timer = 0;

	    curr_DemonType = UnitCreatureFamily("pet");

	    if (curr_DemonType) then Dcr_debug_bis ("Pet Type: " .. curr_DemonType);  end;

	    if (last_DemonType ~= curr_DemonType) then
		if (curr_DemonType) then Dcr_debug_bis ("Pet name changed: " .. curr_DemonType); else  Dcr_debug_bis ("No more pet!"); end;

		last_DemonType = curr_DemonType;
		Dcr_Configure();
	    end

	    Dcr_CheckingPET = false;
	    return;
	end

    end

    -- clean up the blacklist
    for unit in Dcr_Blacklist_Array do
	Dcr_Blacklist_Array[unit] = Dcr_Blacklist_Array[unit] - arg1;
	if (Dcr_Blacklist_Array[unit] < 0) then
	    Dcr_Blacklist_Array[unit] = nil;
	end
    end

    -- wow the next command SPAMS alot
    -- Dcr_debug("got update "..arg1);

    -- this is the fix for the AttackTarget() bug
    if (Dcr_Delay_Timer > 0) then
	Dcr_Delay_Timer = Dcr_Delay_Timer - arg1;
	if (Dcr_Delay_Timer <= 0) then
	    if (not Dcr_CombatMode) then
		Dcr_debug("trying to reset the combat mode");
		AttackTarget();
	    else
		Dcr_debug("already in combat mode");
	    end
	end;
    end

    -- clear Debuffs and Buffs caches
    if (Dcr_Debuff_Texture_to_name_cache_life ~= 0) then
	Dcr_Debuff_Texture_to_name_cache_life = Dcr_Debuff_Texture_to_name_cache_life - arg1;

	if (Dcr_Debuff_Texture_to_name_cache_life < 0) then
	    Dcr_Debuff_Texture_to_name_cache_life = 0;
	    Dcr_Debuff_Texture_to_name_cache = {};
	    Dcr_debug_bis("Debuff cache cleared!");
	end
    end

    if (Dcr_Buff_Texture_to_name_cache_life ~= 0) then
	Dcr_Buff_Texture_to_name_cache_life = Dcr_Buff_Texture_to_name_cache_life - arg1;

	if (Dcr_Buff_Texture_to_name_cache_life < 0) then
	    Dcr_Buff_Texture_to_name_cache_life = 0;
	    Dcr_Buff_Texture_to_name_cache = {};
	    Dcr_debug_bis("Buff cache cleared!");
	end
    end

    if (RestorSelfAutoCast) then
	RestorSelfAutoCastTimeOut = RestorSelfAutoCastTimeOut - arg1;
	if (RestorSelfAutoCastTimeOut < 0) then
	    RestorSelfAutoCast = false;
	    SetCVar("autoSelfCast", "1");

	    Dcr_debug_bis("autoSelfCast restored!");
	end
    end

end --}}}

-- the combat saver functions and events. These keep us in combat mode // {{{
-------------------------------------------------------------------------------
function Dcr_EnterCombat() --{{{
    Dcr_debug("Entering combat");
    Dcr_CombatMode = true;
end --}}}

function Dcr_LeaveCombat() --{{{
    Dcr_debug("Leaving combat");
    Dcr_CombatMode = false;
end --}}}
-- }}}

function Dcr_SpellCastFailed() --{{{
    if (
	Dcr_Casting_Spell_On	    -- a cast failed and we were casting on someone
	and not (
	UnitIsUnit(Dcr_Casting_Spell_On, "player")   -- we do not blacklist ourself
	or
	(
	-- we do not blacklist people in the priority list
	Dcr_Saved.DoNot_Blacklist_Prio_List and Dcr_IsInPriorList ( (UnitName(Dcr_Casting_Spell_On)) )
	)
	)
	) then

	Dcr_Blacklist_Array[Dcr_Casting_Spell_On] = nil;
	Dcr_Blacklist_Array[Dcr_Casting_Spell_On] = Dcr_Saved.CureBlacklist;
	DCR_ThisCleanBlaclisted[Dcr_Casting_Spell_On] = true;
    end
end --}}}

function Dcr_SpellWasCast() --{{{
    Dcr_Casting_Spell_On = nil;
end --}}}
-- }}}
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-- Scanning functionalties {{{
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-- GROUP STATUS UPDATE, these functions update the UNIT table to scan {{{
-------------------------------------------------------------------------------

-- this gets an array of units for us to check
function Dcr_GetUnitArray() --{{{

    -- if the groups composition did not changed
    if (not Dcr_Groups_datas_are_invalid) then
	return;
    end

    Dcr_debug_bis ("|cFFFF44FF-->|r Updating Units Array");

    local pname;
    local raidnum = GetNumRaidMembers();
    local MyName = (UnitName( "player"));

    local SortIndex = 1;

    -- clear all the arrays
    InternalPrioList = { };
    InternalSkipList = { };
    Dcr_Unit_Array   = { };
    SortingTable     = { };

    -- First sort the prioritylist (remove missing units) and add its content to the main list
    for _, pname in Dcr_Saved.PriorityList do
	local unit = Dcr_NameToUnit( pname );
	if (unit) then
	    InternalPrioList[pname] = unit;
	    -- use this loop to add prio characters to the main list
	    Dcr_Unit_Array[pname] = unit;
	    Dcr_AddToSort(unit, SortIndex); SortIndex = SortIndex + 1;
	end
    end

    -- The same with the skip list
    for _, pname in Dcr_Saved.SkipList do
	local unit = Dcr_NameToUnit( pname );
	if (unit) then
	    InternalSkipList[pname] = unit;
	end
    end

    -- Add the player to the main list if needed
    if (not Dcr_IsInSkipOrPriorList(MyName)) then
	Dcr_Unit_Array[MyName] = "player";
	Dcr_AddToSort( "player", SortIndex); SortIndex = SortIndex + 1;
    end

    -- add the party members... if they exist
    for i = 1, 4 do
	if (UnitExists("party"..i)) then
	    pname = (UnitName("party"..i));
	    if (not pname) then -- at logon sometimes pname is nil...
		pname = "party"..i;
	    end
	    -- check the name to see if we skip
	    if (not Dcr_IsInSkipOrPriorList(pname)) then
		-- we don't skip him
		Dcr_Unit_Array[pname] = "party"..i;
		Dcr_AddToSort("party"..i, SortIndex); SortIndex = SortIndex + 1;
	    end
	end
    end


    if ( raidnum > 0 ) then -- if we are in a raid
	local temp_raid_table = {}; -- used to avoid the repeated call of GetRaidRosterInfo
	local currentGroup = 0;

	-- Cache the raid roster info eliminating useless info and already listed members
	for i = 1, raidnum do
	    local rName, _, rGroup = GetRaidRosterInfo(i);

	    -- find our group (a whole iteration is required, raid info are not ordered)
	    if ( currentGroup==0 and rName == MyName) then
		currentGroup = rGroup;
	    end

	    -- add all except: our own group and member to skip
	    if (currentGroup ~= rGroup and not Dcr_IsInSkipOrPriorList(rName)) then
		temp_raid_table[i] = {};
		if (not rName) then -- at logon sometimes rName is nil...
		    rName = rGroup.."unknown"..i;
		end
		temp_raid_table[i].rName    = rName;
		temp_raid_table[i].rGroup   = rGroup;
		temp_raid_table[i].rIndex   = i; -- I can't trust lua, the manual is not clear at all about table behavior...

		
	    end

	end

	for _, raidMember in temp_raid_table do

	    if (raidMember.rGroup > currentGroup) then
		    Dcr_Unit_Array[raidMember.rName] = "raid" .. raidMember.rIndex;
		    -- add first the groups that are after ours
		    Dcr_AddToSort("raid" .. raidMember.rIndex, raidMember.rGroup * 100 + SortIndex); SortIndex =  SortIndex + 1;
		end

		if (raidMember.rGroup < currentGroup) then
		    Dcr_Unit_Array[raidMember.rName] = "raid" .. raidMember.rIndex;
		    -- add then the groups that are before ours
		    Dcr_AddToSort("raid" .. raidMember.rIndex, raidMember.rGroup * 100 + 1000 + SortIndex); SortIndex = SortIndex + 1;
		end

	end

	SortIndex = SortIndex + 8 * 100 + 1000 + 1;

    end -- END if we are in a raid

    -- Exit if we don't have to scan pets...
    if ( Dcr_Saved.Scan_Pets) then

	-- add our own pet
	if (UnitExists("pet")) then
	    Dcr_Unit_Array[(UnitName("pet"))] = "pet";
	    Dcr_AddToSort("pet", SortIndex); SortIndex = SortIndex + 1;
	end

	-- the parties pets if they have them
	for i = 1, 4 do
	    if (UnitExists("partypet"..i)) then
		pname = (UnitName("partypet"..i));
		if (not pname) then -- at logon sometimes pname is nil...
		    pname = "partypet"..i;
		end
		Dcr_Unit_Array[pname] = "partypet"..i;
		Dcr_AddToSort("partypet"..i, SortIndex); SortIndex = SortIndex + 1;
	    end
	end

	-- and then the raid pets if they are out
	if (raidnum > 0) then
	    for i = 1, raidnum do
		if (UnitExists("raidpet"..i)) then
		    pname = (UnitName("raidpet"..i));
		    if (not pname) then -- at logon sometimes pname is nil...
			pname = "raidpet"..i;
		    end
		    -- add it only of not already in
		    if (not Dcr_Unit_Array[pname]) then
			Dcr_Unit_Array[pname] = "raidpet"..i;
			Dcr_AddToSort("raidpet"..i, SortIndex); SortIndex = SortIndex + 1;
		    end
		end
	    end
	end
    end

    local Lua_Table_Library_Is_Really_A_Piece_Of_Shit = {};
    for _, value in Dcr_Unit_Array do
	table.insert(Lua_Table_Library_Is_Really_A_Piece_Of_Shit, value);
    end
    Dcr_Unit_ArrayByName = Dcr_Unit_Array;
    Dcr_Unit_Array = Lua_Table_Library_Is_Really_A_Piece_Of_Shit;

    table.sort(Dcr_Unit_Array, function (a,b) return SortingTable[a] < SortingTable[b] end);

    target_added = false;
    Dcr_Groups_datas_are_invalid = false;
    return;
end --}}}

function Dcr_AddToSort (unit, index) -- // {{{
    if (Dcr_Saved.Random_Order and (not InternalPrioList[(UnitName(unit))]) and not UnitIsUnit(unit,"player")) then
	index = random (1, 3000);
    end
    SortingTable[unit] = index;
    -- Dcr_debug_bis(unit.." - "..index.."   ");
end --}}}

-- Raid/Party Name Check Function (a terrible function, need optimising)
-- this returns the UnitID that the Name points to
-- this does not check "target" or "mouseover"
function Dcr_NameToUnit( Name) --{{{
    if (not Name) then
	return false;
    elseif (Name == (UnitName("player"))) then
	return "player";
    elseif (Name == (UnitName("pet"))) then
	return "pet";
    elseif (Name == (UnitName("party1"))) then
	return "party1";
    elseif (Name == (UnitName("party2"))) then
	return "party2";
    elseif (Name == (UnitName("party3"))) then
	return "party3";
    elseif (Name == (UnitName("party4"))) then
	return "party4";
    elseif (Name == (UnitName("partypet1"))) then
	return "partypet1";
    elseif (Name == (UnitName("partypet2"))) then
	return "partypet2";
    elseif (Name == (UnitName("partypet3"))) then
	return "partypet3";
    elseif (Name == (UnitName("partypet4"))) then
	return "partypet4";
    else
	local numRaidMembers = GetNumRaidMembers();
	if (numRaidMembers > 0) then
	    -- we are in a raid
	    local i;
	    for i=1, numRaidMembers do
		local RaidName = GetRaidRosterInfo(i);
		if ( Name == RaidName) then
		    return "raid"..i;
		end
		if ( Name == (UnitName("raidpet"..i))) then
		    return "raidpet"..i;
		end
	    end
	end
    end
    return false;
end --}}}
-- }}}
-------------------------------------------------------------------------------

function Dcr_Clean(UseThisTarget, SwitchToTarget) --{{{
    -----------------------------------------------------------------------
    -- first we do the setup, make sure we can cast the spells
    -----------------------------------------------------------------------

    -- reset autoSelfCast restor timeout while you're spamming...
    RestorSelfAutoCastTimeOut = 1;

    if (GetCVar("autoSelfCast") == "1") then
	RestorSelfAutoCast = true;
	Dcr_debug_bis("autoSelfCast is active... Temp disabling");
	SetCVar("autoSelfCast", "0");
    end

    -- Dcr_CheckAuSelfCastStaus ();

    if (not DCR_HAS_SPELLS) then
	-- check the spellbook again... (mod by Archarodim)
	Dcr_errln(DCR_NO_SPELLS);
	Dcr_Configure();
	if (not DCR_HAS_SPELLS) then
	    Dcr_errln(DCR_NO_SPELLS);
	    return false;
	end
    end


    Dcr_RestoreTarget = true;
    if (UseThisTarget and SwitchToTarget) then
	TargetUnit(UseThisTarget);
	Dcr_RestoreTarget = false;
    end

    if (Dcr_AlreadyCleanning) then
	Dcr_debug_bis("I'm already cleaning!!!!"); -- seems to be useless
	return false;
    end

    Dcr_AlreadyCleanning = true;

    -- will cancel anyspell upon Decursive call, may be dangerous...
    SpellStopTargeting();
    -- always cancel current spell unless it's pet
    if ( DCR_SPELL_COOLDOWN_CHECK[2] ~= "pet") then SpellStopCasting(); end

    local _, cooldown = GetSpellCooldown(DCR_SPELL_COOLDOWN_CHECK[1], DCR_SPELL_COOLDOWN_CHECK[2]);
    if (cooldown ~= 0) then
	-- this used to be an errline... changed it to debugg
	Dcr_debug_bis(DCR_NO_SPELLS_RDY);
	Dcr_AlreadyCleanning = false;
	return false;
    end


    -- reset blaclisted people in this clean session
    DCR_ThisCleanBlaclisted = { };
    -- reset the number of out of ranged units for this clean session
    DCR_ThisNumberOoRUnits  = 0;


    -----------------------------------------------------------------------
    -----------------------------------------------------------------------
    -- then we see what our target looks like, if freindly, check them
    -----------------------------------------------------------------------

    local targetEnemy = false;
    local targetName = nil; -- if friendly
    local cleaned = false;
    local resetCombatMode = false;
    Dcr_Casting_Spell_On = nil;



    if (UnitExists("target")) then
	Dcr_debug("We have a target");
	-- if we are currently targeting something
	-- ###
	-- This block is here to know what the current target is, so we can clean it, restor it
	-- or clear the target at the end of this function
	-- ###

	if (Dcr_CombatMode) then
	    Dcr_debug("when done scanning... if switched target reset the mode!");
	    resetCombatMode = true;
	end

	if (
	    ( UnitIsFriend("target", "player") ) -- unit is a friend ie: not FriendLY just a friend that could be MC :/
	    and 
	    (not UnitIsCharmed("target")) -- and is not mind controlled
	    ) then
	    Dcr_debug(" It is friendly");
	    -- try cleaning the current target first
	    -- if we are not asked to clean a specific target
	    -- or if we already switched to the target to clean
	    if (not UseThisTarget or SwitchToTarget) then 
		cleaned = Dcr_CureUnit("target");
	    end

	    -- we are targeting a player that is not MC, save the name to switch back later
	    targetName = (UnitName("target"));

	else -- unit is aggressiv or is charmed
	    Dcr_debug(" It is not friendly");
	    -- we are targeting an enemy... switch back when done
	    targetEnemy = true;

	    if ( UnitIsCharmed("target")) then
		Dcr_debug( "Unit is enemey... and charmed... so its a mind controlled friendly");
		-- try cleaning mind controlled person first
		if (not UseThisTarget or SwitchToTarget) then 
		    cleaned = Dcr_CureUnit("target");
		end
	    end
	end
    end

    if (UseThisTarget and not SwitchToTarget and not cleaned) then
	Dcr_debug( "A target to clean was specifyed");
	if (UnitIsVisible(UseThisTarget)) then
	    -- if the unit is even close by


	    if (DCR_CAN_CURE_ENEMY_MAGIC and UnitIsCharmed(UseThisTarget)) then
		-- if the unit is mind controlled and we can cure it
		if (Dcr_CureUnit(UseThisTarget)) then
		    cleaned = true;
		end

	    else -- we can't cure magic on enemies or the unit is not charmed
		if (not Dcr_CheckUnitStealth(UseThisTarget)) then
		    -- we are either not ignoring the stealthed people,
		    -- or it's not stealthed
		    if (Dcr_CureUnit(UseThisTarget)) then
			cleaned = true;
		    end
		end
	    end
	end
    end

    if (not cleaned) then

	-----------------------------------------------------------------------
	-----------------------------------------------------------------------
	-- now we check the partys (raid and local)
	-----------------------------------------------------------------------
	Dcr_debug( "Checking the arrays");

	-- this is the cleaning loops...
	Dcr_GetUnitArray();
	-- the order is player, party1-4, raid, pet, partypet1-4, raidpet1-40
	-- the raid is current party + 1 to 8... then 1 to current party - 1

	-- mind control first
	if( not cleaned) then
	    Dcr_debug(" looking for mind controll");
	    if (DCR_CAN_CURE_ENEMY_MAGIC) then
		for _, unit in Dcr_Unit_Array do
		    -- all of the units...
		    if (not Dcr_Blacklist_Array[unit]) then
			-- if the unit is not black listed
			if (UnitIsVisible(unit)) then
			    -- if the unit is even close by
			    if (UnitIsCharmed(unit)) then
				-- if the unit is mind controlled
				if (Dcr_CureUnit(unit)) then
				    cleaned = true;
				    break;
				end
			    end
			end
		    end
		end
	    end
	end

	-- normal cleaning
	if( not cleaned) then
	    -- Dcr_debug(" normal loop");
	    for _, unit in Dcr_Unit_Array do
		-- all of the units...
		if (not Dcr_Blacklist_Array[unit]) then
		    -- if the unit is not black listed
		    if (UnitIsVisible(unit)) then
			-- if the unit is even close by
			if (not UnitIsCharmed(unit)) then
			    -- we can't cure mind controlled people
			    if (not Dcr_CheckUnitStealth(unit)) then
				-- we are either not ignoring the stealthed people,
				-- or it's not stealthed
				if (Dcr_CureUnit(unit)) then
				    cleaned = true;
				    break;
				end
			    end
			end
		    end
		end
	    end
	end

	if ( not cleaned) then
	    Dcr_debug(" double check the black list");
	    for unit in Dcr_Blacklist_Array do
		-- now... all of the black listed units
		if (not DCR_ThisCleanBlaclisted[unit]) then
		    -- we do not re-check unit that have been blaclisted just before
		    if (UnitExists(unit)) then
			-- if the unit still exists
			if (UnitIsVisible(unit)) then
			    -- if the unit is even close by
			    if (not Dcr_CheckUnitStealth(unit)) then
				-- we are either not ignoring the stealthed people,
				-- or it's not stealthed
				if (Dcr_CureUnit(unit)) then
				    -- hey... we cleaned it... remove from the black list
				    Dcr_Blacklist_Array[unit] = nil;
				    cleaned = true;
				    break;
				end
			    end
			end
		    end
		end
	    end
	end
    end
    -----------------------------------------------------------------------
    -----------------------------------------------------------------------
    -- ok... done with the cleaning... lets try to clean this up
    -- basically switch targets back if they were changed
    -----------------------------------------------------------------------

    if (not SwitchToTarget) then -- if not explicitly ask to switch to the target
	if (targetEnemy) then
	    -- we had somethign "bad" targeted
	    if (not UnitIsEnemy("target", "player")) then
		-- and we tested for range, cast dispell magic, or some how broke target... switch back
		Dcr_debug("targeting enemy");
		TargetUnit("playertarget"); -- XXX to test
		if (resetCombatMode) then
		    -- resetCombatMode is the fix for "auto attack"
		    Dcr_Delay_Timer = Dcr_SpellCombatDelay;
		    Dcr_debug("done... now we wait for the leave combat event");
		end
	    end
	elseif (targetName) then
	    -- we had a friendly targeted... switch back if not still targeted
	    if ( targetName ~= (UnitName("target")) ) then
		TargetByName(targetName);
	    end
	else
	    -- we had nobody targeted originally
	    if (UnitExists("target")) then
		-- we checked for range
		ClearTarget();
	    end
	end
    end

    if (not cleaned) then
	Dcr_println( DCR_NOT_CLEANED);
	SendAddonMessage("decursive", "decursing", "RAID")
    end

    Dcr_AlreadyCleanning = false;
    return cleaned;
end --}}}

-- Buff and debuffs scanning functions {{{
function Dcr_GetUnitBuff (Unit, i) --{{{
    Dcr_ScanningTooltipTextLeft1:SetText("");

    Dcr_ScanningTooltip:SetUnitBuff(Unit, i); -- fill this fake thing with buff info

    return Dcr_ScanningTooltipTextLeft1:GetText(); -- get the buff name

end --}}}

function Dcr_GetUnitDebuff  (Unit, i) --{{{
    local DebuffTexture, debuffApplications, debuff_type;

    DebuffTexture, debuffApplications, debuff_type = UnitDebuff(Unit, i);

    if (DebuffTexture) then

	debuff_name = Dcr_GetUnitDebuffName(Unit, i, DebuffTexture);

	return debuff_name, debuff_type, debuffApplications, DebuffTexture;
    else
	return false, false, false, false;
    end
end --}}}

function Dcr_GetUnitDebuffName  (Unit, i, DebuffTexture) --{{{
    local debuff_name;

    if (not Dcr_Debuff_Texture_to_name_cache[DebuffTexture]) then

	Dcr_ScanningTooltipTextLeft1:SetText(""); -- clean

	Dcr_ScanningTooltip:SetUnitDebuff(Unit, i); -- fill this fake thing with Debuff info

	debuff_name = Dcr_ScanningTooltipTextLeft1:GetText(); -- get the debuff name from the tooltip

	if (debuff_name == nil) then
	    -- this should only happen when things are "broke"
	    Dcr_errln("%$#@*& !!! Impossible to get debuff info from tooltip :'(, if this error continues to show up, type /console reloadui");
	    Dcr_debug( "Debuff name not found!");
	elseif (debuff_name ~= "") then
	    -- reset cache lifetime
	    Dcr_Debuff_Texture_to_name_cache_life = DEBUFF_CACHE_LIFE;
	    Dcr_debug_bis("Debuff cache lifetime RESET (assignement) !");
	    -- save the name to cache for faster access
	    Dcr_Debuff_Texture_to_name_cache[DebuffTexture] = debuff_name;
	    Dcr_debug_bis(debuff_name .. " put in cache ( " .. DebuffTexture .. " )");
	end

    else
	debuff_name = Dcr_Debuff_Texture_to_name_cache[DebuffTexture];
    end

    -- reset cache lifetime
    -- Dcr_debug_bis("Debuff cache lifetime RESET (normal) !");
    Dcr_Debuff_Texture_to_name_cache_life = DEBUFF_CACHE_LIFE;

    return debuff_name;
end --}}}

function Dcr_GetUnitDebuffAll (unit) --{{{
    local DebuffTexture, debuffApplications, debuff_type, debuff_name, i;
    local ThisUnitDebuffs = {};

    i = 1;
    while (true) do
	debuff_name, debuff_type, debuffApplications, DebuffTexture = Dcr_GetUnitDebuff(unit, i);

	if (not debuff_name) then
	    break;
	end
	ThisUnitDebuffs[debuff_name] = {};
	ThisUnitDebuffs[debuff_name].DebuffTexture	= DebuffTexture;
	ThisUnitDebuffs[debuff_name].debuffApplications = debuffApplications;
	ThisUnitDebuffs[debuff_name].debuff_type	= debuff_type;
	ThisUnitDebuffs[debuff_name].debuff_name	= debuff_name;
	ThisUnitDebuffs[debuff_name].index		= i;

	i = i + 1;
    end


    return ThisUnitDebuffs;
end --}}}


function Dcr_CheckUnitForBuff(Unit, BuffNameToCheck) --{{{
    local i = 1, texture, found_buff_name;

    while (true) do
	texture = UnitBuff (Unit, i);

	if (not texture) then
	    break;
	end

	if (not Dcr_Buff_Texture_to_name_cache[texture]) then
	    found_buff_name = Dcr_GetUnitBuff(Unit, i);

	    if (found_buff_name == nil) then
		-- this should only happen when things are "broke"
		Dcr_errln("%$#@*& !!! Impossible to get buff info from tooltip :'(, if this error continues to show up, type /console reloadui");
		Dcr_debug( "Buff name not found!");
	    elseif (found_buff_name ~= "") then

		-- reset cache lifetime
		Dcr_Buff_Texture_to_name_cache_life = DEBUFF_CACHE_LIFE;
		-- save the name to cache for faster access
		Dcr_Buff_Texture_to_name_cache[texture] = found_buff_name;
		Dcr_debug_bis(found_buff_name .. " put in cache ( " .. texture .. " )");
	    end
	else
	    found_buff_name = Dcr_Buff_Texture_to_name_cache[texture];
	end

	if (i > 1) then 
	    -- reset cache lifetime
	    Dcr_Buff_Texture_to_name_cache_life = DEBUFF_CACHE_LIFE;
	end

	if (found_buff_name == BuffNameToCheck) then
	    return true;
	end

	i = i + 1;
    end
    return false;
end --}}}

function Dcr_CheckUnitStealth(Unit) --{{{
    if (Dcr_Saved.Ingore_Stealthed) then
	for BuffName in DCR_INVISIBLE_LIST do
	    if Dcr_CheckUnitForBuff(Unit, BuffName) then
		return true;
	    end
	end
    end
    return false;
end --}}}
-- }}}

function Dcr_UnitInRange(Unit) --{{{
    if (CheckInteractDistance(Unit, 4)) then
	return true;
    end
    return false;
end --}}}
-- }}}

-------------------------------------------------------------------------------
-- Curring functions {{{
-------------------------------------------------------------------------------
function Dcr_CureUnit(Unit)  --{{{
    Dcr_debug( "Scanning to cure unit - "..Unit);

    local Magic_Count	= 0;
    local Disease_Count = 0;
    local Poison_Count	= 0;
    local Curse_Count	= 0;

    local TClass, UClass = UnitClass(Unit);

    local AllUnitDebuffs = {};

    AllUnitDebuffs = Dcr_GetUnitDebuffAll(Unit);

    local Go_On;

    for debuff_name, debuff_params in AllUnitDebuffs do
	-- the "break" was not working all that well, so we stor a go on variable
	Go_On = true;

	Dcr_debug( debuff_name.." found!");

	-- test if we have to ignore this debuff {{{ --
	-- Ignore the ones that make the target immune... abort the user
	if (DCR_IGNORELIST[debuff_name]) then
	    Dcr_errln( string.gsub( string.gsub(DCR_IGNORE_STRING, "$t", (UnitName(Unit))), "$a", debuff_name));
	    return false;
	end


	-- Ignore debuffs that are in fact buffs
	if (DCR_SKIP_LIST[debuff_name]) then
	    Dcr_errln( string.gsub( string.gsub(DCR_IGNORE_STRING, "$t", (UnitName(Unit))), "$a", debuff_name));
	    Go_On = false; -- == continue
	end

	-- If we are in combat lets see if there is any debuffs we can afford to not remove until ths fight is over
	if (UnitAffectingCombat("player")) then
	    if (DCR_SKIP_BY_CLASS_LIST[UClass]) then
		if (DCR_SKIP_BY_CLASS_LIST[UClass][debuff_name]) then
		    -- these are just ones you don't care about by class
		    Dcr_errln( string.gsub( string.gsub(DCR_IGNORE_STRING, "$t", (UnitName(Unit))), "$a", debuff_name));
		    Go_On = false; -- == continue
		end
	    end
	end
	-- }}}

	if (Go_On) then
	    -- it is one we "care" about... lets catalog it -- {{{ --
	    if (debuff_params.debuff_type and debuff_params.debuff_type ~= "") then
		if (debuff_params.debuff_type == DCR_MAGIC) then
		    Dcr_debug( "it's magic");
		    Magic_Count = Magic_Count + debuff_params.debuffApplications + 1;
		elseif (debuff_params.debuff_type == DCR_DISEASE) then
		    Dcr_debug( "it's disease");
		    Disease_Count = Disease_Count + debuff_params.debuffApplications + 1;
		elseif (debuff_params.debuff_type == DCR_POISON) then
		    Dcr_debug( "it's poison");
		    Poison_Count = Poison_Count + debuff_params.debuffApplications + 1;
		elseif (debuff_params.debuff_type == DCR_CURSE) then
		    Dcr_debug( "it's curse");
		    Curse_Count = Curse_Count + debuff_params.debuffApplications + 1
		else
		    Dcr_debug( "it's unknown - "..debuff_params.debuff_type);
		end
	    else
		Dcr_debug( "it's untyped");
	    end
	    -- }}}
	end

    end

    local res = false;
    local counts = {};
    counts.Magic_Count = Magic_Count;
    counts.Curse_Count = Curse_Count;
    counts.Poison_Count = Poison_Count;
    counts.Disease_Count = Disease_Count;


    local i;
    for i = 1, 4 do
	if Curing_functions[i] then
	    res = Curing_functions[i](counts, Unit);
	    if res then
		break;
	    end
	end
    end

    -- order these in the way you find most important 
    --[[
    if (not res) then
	res = Dcr_Cure_Magic( counts, Unit);
    end
    if (not res) then
	res = Dcr_Cure_Curse( counts, Unit);
    end
    if (not res) then
	res = Dcr_Cure_Poison( counts, Unit);
    end
    if (not res) then
	res = Dcr_Cure_Disease( counts, Unit);
    end
    --]]

    return res;
end -- // }}}

function Dcr_Cure_Magic(counts, Unit)  --{{{
    Dcr_debug( "magic count "..counts.Magic_Count);
    if (DCR_CAN_CURE_MAGIC) then
	Dcr_debug( "Can cure magic");
    end
    if (DCR_CAN_CURE_ENEMY_MAGIC) then
	Dcr_debug( "Can cure enemy magic");
    end

    if ( (not (DCR_CAN_CURE_MAGIC or DCR_CAN_CURE_ENEMY_MAGIC)) or (counts.Magic_Count == 0) or not Dcr_Saved.CureMagic) then
	-- here is no magical effects... or
	-- we can't cure magic don't bother going forward
	Dcr_debug( "no magic");
	return false;
    end
    Dcr_debug( "curing magic");

    if ( DCR_CAN_CURE_ENEMY_MAGIC and UnitIsCharmed(Unit) and UnitCanAttack("player", Unit) ) then
	-- unit is charmed... and has magic debuffs on them... and we CAN attack it
	-- there is a good chance that it is the mind controll type spell
	-- the checking for the UnitCanAttack is due to the mind controlled pets and other enslaves
	if (DCR_SPELL_ENEMY_MAGIC_2[1] ~= 0 ) and (Dcr_Saved.AlwaysUseBestSpell or (counts.Magic_Count > 1) or (DCR_SPELL_MAGIC_1[1] == 0)) then
	    return Dcr_Cast_CureSpell( DCR_SPELL_ENEMY_MAGIC_2	, Unit, DCR_CHARMED, true);
	else
	    return Dcr_Cast_CureSpell( DCR_SPELL_ENEMY_MAGIC_1	, Unit, DCR_CHARMED, true);
	end
    elseif (DCR_CAN_CURE_MAGIC and (not UnitCanAttack("player", Unit))) then
	-- we can cure magic... and the unit is NOT hostile to us (we can't cast on those)
	if (DCR_SPELL_MAGIC_2[1] ~= 0 ) and (Dcr_Saved.AlwaysUseBestSpell or (counts.Magic_Count > 1) or (DCR_SPELL_MAGIC_1[1] == 0)) then
	    return Dcr_Cast_CureSpell( DCR_SPELL_MAGIC_2	, Unit, DCR_MAGIC, DCR_CAN_CURE_ENEMY_MAGIC);
	else
	    return Dcr_Cast_CureSpell( DCR_SPELL_MAGIC_1, Unit, DCR_MAGIC, DCR_CAN_CURE_ENEMY_MAGIC);
	end
	-- else
	-- what it means:
	-- not (DCR_CAN_CURE_ENEMY_MAGIC and UnitIsCharmed(Unit) and UnitCanAttack("player", Unit)
	-- not (DCR_CAN_CURE_MAGIC and (not UnitCanAttack("player", Unit)))
	--
	-- !DCR_CAN_CURE_ENEMY_MAGIC or !UnitIsCharmed(Unit) or !UnitCanAttack("player", Unit)   =====> not MC or we can't attack it
	-- AND
	-- !DCR_CAN_CURE_MAGIC UnitCanAttack("player", Unit)
	--
	-- we can't cure enemy magic
	-- Dcr_errln("Something strange happened :/ Dcr_Cure_Magic() did nothing :-o");
    end
    return false;
end -- // }}}

function Dcr_Cure_Curse( counts, Unit)  -- {{{
    if ( (not DCR_CAN_CURE_CURSE) or (counts.Curse_Count == 0) or not Dcr_Saved.CureCurse) then
	-- no curses or no curse curing spells
	Dcr_debug( "no curse");
	return false;
    end
    Dcr_debug( "curing curse");

    if (UnitIsCharmed(Unit)) then
	-- we can not cure a mind contorolled player
	return;
    end

    if (DCR_SPELL_CURSE ~= 0) then
	return Dcr_Cast_CureSpell(DCR_SPELL_CURSE, Unit, DCR_CURSE, false);
    end
    return false;
end -- // }}}

function Dcr_Cure_Poison(counts, Unit)  -- {{{
    if ( (not DCR_CAN_CURE_POISON) or (counts.Poison_Count == 0) or not Dcr_Saved.CurePoison) then
	-- here is no magical effects... or
	-- we can't cure magic don't bother going forward
	Dcr_debug( "no poison");
	return false;
    end
    Dcr_debug( "curing poison");

    if (UnitIsCharmed(Unit)) then
	-- we can not cure a mind contorolled player
	return;
    end

    if (Dcr_Saved.Check_For_Abolish and Dcr_CheckUnitForBuff(Unit, DCR_SPELL_ABOLISH_POISON)) then
	return false;
    end

    if (DCR_SPELL_POISON_2[1] ~= 0 ) and (Dcr_Saved.AlwaysUseBestSpell or (counts.Poison_Count > 1)) then
	return Dcr_Cast_CureSpell( DCR_SPELL_POISON_2, Unit, DCR_POISON, false);
    else
	return Dcr_Cast_CureSpell( DCR_SPELL_POISON_1, Unit, DCR_POISON, false);
    end
end -- // }}}

function Dcr_Cure_Disease(counts, Unit) --{{{
    if ( (not DCR_CAN_CURE_DISEASE) or (counts.Disease_Count == 0) or not Dcr_Saved.CureDisease) then
	-- here is no magical effects... or
	-- we can't cure magic don't bother going forward
	Dcr_debug( "no disease");
	return false;
    end
    Dcr_debug( "curing disease");

    if (UnitIsCharmed(Unit)) then
	-- we can not cure a mind contorolled player
	return;
    end

    if (Dcr_Saved.Check_For_Abolish and Dcr_CheckUnitForBuff(Unit, DCR_SPELL_ABOLISH_DISEASE)) then
	return false;
    end

    if (DCR_SPELL_DISEASE_2[1] ~= 0 ) and (Dcr_Saved.AlwaysUseBestSpell or (counts.Disease_Count > 1)) then
	return Dcr_Cast_CureSpell( DCR_SPELL_DISEASE_2, Unit, DCR_DISEASE, false);
    else
	return Dcr_Cast_CureSpell( DCR_SPELL_DISEASE_1, Unit, DCR_DISEASE, false);
    end
end --}}}

function Dcr_Cast_CureSpell( spellID, Unit, AfflictionType, ClearCurrentTarget) --{{{
    local name = (UnitName(Unit));


    if (spellID[1] == 0) then
	Dcr_errln("Stupid call to Dcr_Cast_CureSpell() with a null spellID!!!");
	return false;
    end

    -- check to see if we are in range
    if (
	(spellID[2] ~= BOOKTYPE_PET) and
	(not Dcr_UnitInRange(Unit))
	) then

	-- XXX We do not blacklist out of range people any more, they don't prevent anything from hapenning
	-- it will just spam a bit if there are a lot of them...

	-- Dcr_Blacklist_Array[Unit] = nil; -- attempt to remove it
	-- Dcr_Blacklist_Array[Unit] = Dcr_Saved.CureBlacklist; -- add it to the blacklist, hopefully at the end

	-- DCR_ThisCleanBlaclisted[Unit] = true;

	Dcr_errln( string.gsub( string.gsub(DCR_OUT_OF_RANGE, "$t", MakePlayerName(name)), "$a", MakeAfflictionName(AfflictionType)));
	-- DCR_ThisNumberOoRUnits = DCR_ThisNumberOoRUnits + 1;
	return false;
    end

    Dcr_debug_bis( "try to cast: "..spellID[1] .." - ".. spellID[2]);
    local spellName = GetSpellName(spellID[1], spellID[2]);
    Dcr_debug( "casting - "..spellName);

    -- clear the target if it will interfear
    if (ClearCurrentTarget) then
	-- it can target enemys... do don't target ANYTHING else
	if ( not UnitIsUnit( "target", Unit) ) then
	    ClearTarget();
	end
    elseif ( UnitIsFriend( "player", "target") ) then
	-- we can accedenally cure friendly targets...
	if ( not UnitIsUnit( "target", Unit) ) then
	    -- and we want to cure someone else who is not targeted
	    ClearTarget();
	end
    end

    Dcr_println( string.gsub( string.gsub(DCR_CLEAN_STRING, "$t", MakePlayerName(name)), "$a", MakeAfflictionName(AfflictionType)));
    Dcr_debug_bis( "casting on " .. (UnitName(Unit)) .. " -- " .. Unit);
    if (spellID[2] == BOOKTYPE_PET or spellID[3] == DCR_SPELL_PURGE) then
	TargetUnit(Unit);
    end

    -- if a spell is awaiting for a target, cancel it
    -- if ( SpellIsTargeting()) then
    --	SpellStopTargeting();
    -- always cancel current spell
    --	SpellStopCasting();

    -- end

    -- cast the spell
    Dcr_Casting_Spell_On = Unit;
    CastSpell(spellID[1],  spellID[2]);
	SendAddonMessage("decursive", "decursing", "RAID")

    -- if the spell doesn't need a target
    if (Dcr_RestoreTarget and (spellID[2] == BOOKTYPE_PET or spellID[3] == DCR_SPELL_PURGE)) then
	TargetUnit("playertarget"); -- restore previous target 
    else
	-- if the cast succeeded
	if (SpellIsTargeting()) then
	    SpellTargetUnit(Unit);
	end
    end

    -- if the targeting failed (still waiting for a target), cancel the cast
    if ( SpellIsTargeting()) then
	SpellStopTargeting();
    end

    return true;
end --}}}
-- }}}
-------------------------------------------------------------------------------


