local Tinkr, Bastion = ...

local SubModulue = Bastion.Module:New('sub')
local Evaluator = Tinkr.Util.Evaluator
local Player = Bastion.UnitManager:Get('player')
local None = Bastion.UnitManager:Get('none')
local Target = Bastion.UnitManager:Get('target')

local RollTheBones       = Bastion.SpellBook:GetSpell(315508)
local SliceAndDice       = Bastion.SpellBook:GetSpell(315496)
local BetweenTheEyes     = Bastion.SpellBook:GetSpell(315341)
local BladeRush          = Bastion.SpellBook:GetSpell(271877)
local Vanish             = Bastion.SpellBook:GetSpell(1856)
local Dispatch           = Bastion.SpellBook:GetSpell(2098)
local Ambush             = Bastion.SpellBook:GetSpell(8676)
local Stealth            = Bastion.SpellBook:GetSpell(1784)
local PistolShot         = Bastion.SpellBook:GetSpell(185763)
local Opportunity        = Bastion.SpellBook:GetSpell(195627)
local SinisterStrike     = Bastion.SpellBook:GetSpell(193315)
local GrandMelee         = Bastion.SpellBook:GetSpell(193358)
local Broadside          = Bastion.SpellBook:GetSpell(193356)
local TrueBearing        = Bastion.SpellBook:GetSpell(193359)
local RuthlessPrecision  = Bastion.SpellBook:GetSpell(193357)
local SkullAndCrossbones = Bastion.SpellBook:GetSpell(199603)
local BuriedTreasure     = Bastion.SpellBook:GetSpell(199600)
local AdrenalineRush     = Bastion.SpellBook:GetSpell(13750)
local ShadowDance        = Bastion.SpellBook:GetSpell(185313)
local ShadowDanceAura    = Bastion.SpellBook:GetSpell(185422)
local Audacity           = Bastion.SpellBook:GetSpell(381845)
local Flagellation       = Bastion.SpellBook:GetSpell(323654)
local Dreadblades        = Bastion.SpellBook:GetSpell(343142)
local JollyRoger         = Bastion.SpellBook:GetSpell(199603)
local BladeFlurry        = Bastion.SpellBook:GetSpell(13877)
local Kick               = Bastion.SpellBook:GetSpell(1766)
local MarkedForDeath     = Bastion.SpellBook:GetSpell(137619)
local CrimsonVial        = Bastion.SpellBook:GetSpell(185311)
local Shiv               = Bastion.SpellBook:GetSpell(5938)
local KidneyShot         = Bastion.SpellBook:GetSpell(408)
local InstantPoison      = Bastion.SpellBook:GetSpell(315584)
local Sanguine           = Bastion.SpellBook:GetSpell(326509)
local AtrophicPosion     = Bastion.SpellBook:GetSpell(381637)
local Evasion            = Bastion.SpellBook:GetSpell(5277)
local TricksOfTheTrade   = Bastion.SpellBook:GetSpell(57934)
local CheapShot          = Bastion.SpellBook:GetSpell(1833)
local BagOfTricks        = Bastion.SpellBook:GetSpell(312411)
local AutoAttack         = Bastion.SpellBook:GetSpell(6603)
local SymbolsOfDeath     = Bastion.SpellBook:GetSpell(212283)
local ShadowBlades       = Bastion.SpellBook:GetSpell(121471)
local ColdBlood          = Bastion.SpellBook:GetSpell(382245)
local ShurikenTornado    = Bastion.SpellBook:GetSpell(277925)
local ThistleTea         = Bastion.SpellBook:GetSpell(381623)
local Gloomblade         = Bastion.SpellBook:GetSpell(200758)
local Shadowstrike       = Bastion.SpellBook:GetSpell(185438)
local Rupture            = Bastion.SpellBook:GetSpell(1943)
local Eviscerate         = Bastion.SpellBook:GetSpell(196819)
local NumbingPoison      = Bastion.SpellBook:GetSpell(5761)
local ShurikenStorm      = Bastion.SpellBook:GetSpell(197835)
local BlackPowder        = Bastion.SpellBook:GetSpell(319175)
local SecretTechnique    = Bastion.SpellBook:GetSpell(280719)
local DarkBrew           = Bastion.SpellBook:GetSpell(310454)
local Premeditation      = Bastion.SpellBook:GetSpell(343173)
local DanseMacabre       = Bastion.SpellBook:GetSpell(393969)

local IrideusFragment = Bastion.ItemBook:GetItem(193743)
local Healthstone = Bastion.ItemBook:GetItem(5512)
local WindscarWhetstone = Bastion.ItemBook:GetItem(137486)
local DarkMoonRime = Bastion.ItemBook:GetItem(198477)

local RimeCards = {
    One = Bastion.SpellBook:GetSpell(382844),
    Two = Bastion.SpellBook:GetSpell(382845),
    Three = Bastion.SpellBook:GetSpell(382846),
    Four = Bastion.SpellBook:GetSpell(382847),
    Five = Bastion.SpellBook:GetSpell(382848),
    Six = Bastion.SpellBook:GetSpell(382849),
    Seven = Bastion.SpellBook:GetSpell(382850),
    Eight = Bastion.SpellBook:GetSpell(382851),
}

local PurgeTarget = Bastion.UnitManager:CreateCustomUnit('purge', function(unit)
    local purge = nil

    Bastion.UnitManager:EnumEnemies(function(unit)
        if unit:IsDead() then
            return false
        end

        if not Player:CanSee(unit) then
            return false
        end

        if Player:GetDistance(unit) > 40 then
            return false
        end

        if unit:GetAuras():HasAnyStealableAura() then
            purge = unit
            return true
        end
    end)

    if purge == nil then
        purge = None
    end

    return purge
end)

local KickTarget = Bastion.UnitManager:CreateCustomUnit('kick', function(unit)
    local kick = nil

    Bastion.UnitManager:EnumEnemies(function(unit)
        if unit:IsDead() then
            return false
        end

        if not Player:CanSee(unit) then
            return false
        end

        if Player:GetDistance(unit) > 40 then
            return false
        end

        if Player:InMelee(unit) and Player:IsFacing(unit) and Bastion.MythicPlusUtils:CastingCriticalKick(unit, 5) then
            kick = unit
            return true
        end
    end)

    if kick == nil then
        kick = None
    end

    return kick
end)

local Tank = Bastion.UnitManager:CreateCustomUnit('tank', function(unit)
    local tank = nil

    Bastion.UnitManager:EnumFriends(function(unit)
        if Player:GetDistance(unit) > 40 then
            return false
        end

        if not Player:CanSee(unit) then
            return false
        end

        if unit:IsDead() then
            return false
        end

        if unit:IsTank() then
            tank = unit
            return true
        end

        return false
    end)

    if tank == nil then
        tank = None
    end

    return tank
end)

local RuptureTarget = Bastion.UnitManager:CreateCustomUnit('rupture', function()
    local target = nil

    Bastion.UnitManager:EnumEnemies(function(unit)
        if unit:IsDead() then
            return false
        end

        if not Player:CanSee(unit) then
            return false
        end

        if not Player:InMelee(unit) then
            return false
        end

        if not Player:IsFacing(unit) then
            return false
        end

        if (
            not unit:GetAuras():FindMy(Rupture):IsUp() or
                unit:GetAuras():FindMy(Rupture):GetRemainingTime() < 6
            )
            and unit:TimeToDie() > 12
        then
            target = unit
            return true
        end
    end)

    if target == nil then
        target = None
    end

    return target
end)

local DefaultAPL = Bastion.APL:New('default')
local AOEAPL = Bastion.APL:New('aoe')
local SpecialAPL = Bastion.APL:New('special')
local RacialsAPL = Bastion.APL:New('racials')

SpecialAPL:AddSpell(
    Kick:CastableIf(function(self)
        return KickTarget:Exists() and Player:InMelee(KickTarget) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling()
    end):SetTarget(KickTarget)
)

SpecialAPL:AddSpell(
    KidneyShot:CastableIf(function(self)
        return KickTarget:Exists() and Player:InMelee(KickTarget) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            Kick:GetTimeSinceLastCast() > 2 and
            (Player:GetComboPoints(Target) >= 5 or
                (
                Player:GetComboPoints(Target) >= 4 and
                    (Player:GetAuras():FindMy(Broadside):IsUp() or Player:GetAuras():FindMy(Opportunity):IsUp())))
            and not Target:GetAuras():Find(Sanguine):IsUp()

    end):SetTarget(KickTarget)
)

SpecialAPL:AddSpell(
    CheapShot:CastableIf(function(self)
        return KickTarget:Exists() and Player:InMelee(KickTarget) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and Player:GetAuras():FindMy(Stealth):IsUp()
    end):SetTarget(KickTarget)
)

SpecialAPL:AddSpell(
    Stealth:CastableIf(function(self)
        return self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and not Player:IsAffectingCombat() and
            not Player:GetAuras():FindMy(Stealth):IsUp() and not IsMounted()
    end):SetTarget(Player)
)

SpecialAPL:AddSpell(
    CrimsonVial:CastableIf(function(self)
        return self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            Player:GetHealthPercent() < 70
    end):SetTarget(Player)
)

SpecialAPL:AddSpell(
    Shiv:CastableIf(function(self)
        return PurgeTarget:Exists() and Player:InMelee(PurgeTarget) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and PurgeTarget:GetAuras():HasAnyStealableAura()
    end):SetTarget(PurgeTarget)
)

SpecialAPL:AddSpell(
    InstantPoison:CastableIf(function(self)
        return self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            not Player:GetAuras():FindMy(InstantPoison):IsUp() and not Player:IsMoving()
    end):SetTarget(Player)
)

SpecialAPL:AddSpell(
    AtrophicPosion:CastableIf(function(self)
        return self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            not Player:GetAuras():FindMy(AtrophicPosion):IsUp() and not Player:IsMoving()
    end):SetTarget(Player)
)

SpecialAPL:AddSpell(
    NumbingPoison:CastableIf(function(self)
        return self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            not Player:GetAuras():FindMy(NumbingPoison):IsUp() and not Player:IsMoving()
    end):SetTarget(Player)
)

SpecialAPL:AddItem(
    Healthstone:UsableIf(function(self)
        return self:IsEquippedAndUsable() and
            not Player:IsCastingOrChanneling() and
            Player:GetHealthPercent() < 40
    end):SetTarget(Player)
)

SpecialAPL:AddSpell(
    TricksOfTheTrade:CastableIf(function(self)
        return Tank:Exists() and self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            Player:IsTanking(Target)
    end):SetTarget(Tank)
)

SpecialAPL:AddSpell(
    Evasion:CastableIf(function(self)
        return self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            Player:GetHealthPercent() < 40
    end):SetTarget(Player)
)

SpecialAPL:AddItem(
    IrideusFragment:UsableIf(function(self)
        return self:IsEquippedAndUsable() and
            not Player:IsCastingOrChanneling() and (Player:GetMeleeAttackers() > 2 or Target:IsBoss())
    end):SetTarget(Player)
)

SpecialAPL:AddItem(
    WindscarWhetstone:UsableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and self:IsEquippedAndUsable() and
            not Player:IsCastingOrChanneling() and (Player:GetMeleeAttackers() > 2 or Target:IsBoss())
    end):SetTarget(Player)
)

SpecialAPL:AddItem(
    DarkMoonRime:UsableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and self:IsEquippedAndUsable() and
            not Player:IsCastingOrChanneling() and (Player:GetMeleeAttackers() > 2 or Target:IsBoss()) and
            (Player:GetAuras():FindMy(RimeCards.One):IsUp() or
                Player:GetAuras():FindMy(RimeCards.Two):IsUp() or
                Player:GetAuras():FindMy(RimeCards.Three):IsUp() or
                Player:GetAuras():FindMy(RimeCards.Four):IsUp() or
                Player:GetAuras():FindMy(RimeCards.Five):IsUp() or
                Player:GetAuras():FindMy(RimeCards.Six):IsUp() or
                Player:GetAuras():FindMy(RimeCards.Seven):IsUp() or
                Player:GetAuras():FindMy(RimeCards.Eight):IsUp()
            )
    end):SetTarget(Target)
)

-- Use  Shadowstrike during  Shadow Dance.
SpecialAPL:AddSpell(
    Shadowstrike:CastableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and Player:GetAuras():FindMy(Premeditation):IsUp() and
            Player:GetEnemies(10) <= 3
    end):SetTarget(Target)
)

RacialsAPL:AddSpell(
    BagOfTricks:CastableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling()
    end):SetTarget(Target)
)

-- Use  Symbols of Death on cooldown as much as possible.
DefaultAPL:AddSpell(
    SymbolsOfDeath:CastableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling()
    end):SetTarget(Player):OnCast(function()
        SpellCancelQueuedSpell()
        ShurikenTornado:Cast(Target)
        SpellCancelQueuedSpell()
    end)
)

-- Use  Shadow Blades on cooldown.
DefaultAPL:AddSpell(
    ShadowBlades:CastableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling()
    end):SetTarget(Player)
)

-- Use  Cold Blood before a finishing move, ideally before  Secret Technique.
DefaultAPL:AddSpell(
    ColdBlood:CastableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            Player:GetComboPoints(Target) >= 5 and SecretTechnique:IsKnownAndUsable() and
            Player:GetAuras():FindMy(SliceAndDice):IsUp() and
            Target:GetAuras():FindMy(Rupture):IsUp()
    end):SetTarget(Player):OnCast(function()
        SpellCancelQueuedSpell()
        SecretTechnique:Cast(Target)
        SpellCancelQueuedSpell()
    end)
)

-- Line up  Shuriken Tornado with  Symbols of Death.
DefaultAPL:AddSpell(
    ShurikenTornado:CastableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            Player:GetAuras():FindMy(SymbolsOfDeath):IsUp()
    end):SetTarget(Player)
)

-- Use  Shadow Dance on cooldown as much as possible.
DefaultAPL:AddSpell(
    ShadowDance:CastableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and Gloomblade:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and Player:GetComboPoints(Target) <= 2
    end):SetTarget(Player):OnCast(function()
        SpellCancelQueuedSpell()
        Gloomblade:Cast(Target) -- We want to cast gloomblade immediately with shadow dance to trigger 1 stack of danse macabre
        SpellCancelQueuedSpell()
    end)
)

-- Use  Thistle Tea when low on energy.
-- actions.cds+=/thistle_tea,if=cooldown.symbols_of_death.remains>=3&!buff.thistle_tea.up&(energy.deficit>=100|cooldown.thistle_tea.charges_fractional>=2.75&buff.shadow_dance.up)|buff.shadow_dance.remains>=4&!buff.thistle_tea.up&spell_targets.shuriken_storm>=3|!buff.thistle_tea.up&fight_remains<=(6*cooldown.thistle_tea.charges)
DefaultAPL:AddSpell(
    ThistleTea:CastableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            Player:GetPowerDeficit() >= 100 and
            ThistleTea:GetTimeSinceLastCast() >= 3
    end):SetTarget(Player)

)

-- Use Finishing moves with 6 or more combo points (5 or more during  Shadow Dance) with the following priority:
-- Cast  Slice and Dice if it needs to be refreshed for maintenance or if it is not up.
DefaultAPL:AddSpell(
    SliceAndDice:CastableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            (Player:GetComboPoints(Target) >= 6 or
                (Player:GetComboPoints(Target) >= 5 and
                    Player:GetAuras():FindMy(ShadowDanceAura):IsUp())) and
            (
            not Player:GetAuras():FindMy(SliceAndDice):IsUp() or
                Player:GetAuras():FindMy(SliceAndDice):GetRemainingTime() < 12
            )
    end):SetTarget(Player)
)

-- Cast  Rupture if it needs to be refreshed for maintenance or if it is not up.
DefaultAPL:AddSpell(
    Rupture:CastableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            (Player:GetComboPoints(Target) >= 6 or
                (Player:GetComboPoints(Target) >= 5 and
                    Player:GetAuras():FindMy(ShadowDanceAura):IsUp())) and (
            not Target:GetAuras():FindMy(Rupture):IsUp() or
                Target:GetAuras():FindMy(Rupture):GetRemainingTime() < 6
            ) and not Player:GetAuras():FindMy(ShadowDanceAura):IsUp()
    end):SetTarget(Target)
)

--  Secret Technique - Best is to use it during  Shadow Dance.
DefaultAPL:AddSpell(
    SecretTechnique:CastableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            (Player:GetComboPoints(Target) >= 6 or
                (Player:GetComboPoints(Target) >= 5 and
                    Player:GetAuras():FindMy(ShadowDanceAura):IsUp())) and Target:GetAuras():FindMy(Rupture):IsUp()
    end):SetTarget(Target)
)

-- Cast  Eviscerate.
DefaultAPL:AddSpell(
    Eviscerate:CastableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            (Player:GetComboPoints(Target) >= 6 or
                (Player:GetComboPoints(Target) >= 5 and
                    Player:GetAuras():FindMy(ShadowDanceAura):IsUp()))
    end):SetTarget(Target)
)

-- Vanish - Is a fairly weak cooldown. It is best to use on low combo points for a  Shadowstrike cast. Use it after  Secret Technique in  Shadow Dance when playing with  Danse Macabre.
DefaultAPL:AddSpell(
    Vanish:CastableIf(function(self)
        return Tank:Exists() and Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            Player:GetComboPoints(Target) < 4
    end):SetTarget(Player)
)

-- Use Combo Point builder with the following priority:
-- Use  Gloomblade outside of  Shadow Dance.
DefaultAPL:AddSpell(
    Gloomblade:CastableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            not Player:GetAuras():FindMy(ShadowDanceAura):IsUp()
    end):SetTarget(Target)
)

-- Use  Shadowstrike during  Shadow Dance.
DefaultAPL:AddSpell(
    Shadowstrike:CastableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            Player:GetAuras():FindMy(ShadowDanceAura):IsUp()
    end):SetTarget(Target)
)

-- AOE

-- Use  Symbols of Death on cooldown as much as possible.
AOEAPL:AddSpell(
    SymbolsOfDeath:CastableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling()
    end):SetTarget(Player):OnCast(function()
        SpellCancelQueuedSpell()
        ShurikenTornado:Cast(Target)
        SpellCancelQueuedSpell()
    end)
)

-- Use  Shadow Blades on cooldown.
AOEAPL:AddSpell(
    ShadowBlades:CastableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling()
    end):SetTarget(Player)
)

-- Use  Cold Blood before a finishing move.
AOEAPL:AddSpell(
    ColdBlood:CastableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            Player:GetComboPoints(Target) >= 5 and SecretTechnique:IsKnownAndUsable() and
            Player:GetAuras():FindMy(SliceAndDice):IsUp() and
            Target:GetAuras():FindMy(Rupture):IsUp()
    end):SetTarget(Player):OnCast(function()
        SpellCancelQueuedSpell()
        SecretTechnique:Cast(Target)
        SpellCancelQueuedSpell()
    end)
)

-- Line up  Shuriken Tornado with  Symbols of Death.
AOEAPL:AddSpell(
    ShurikenTornado:CastableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            Player:GetAuras():FindMy(SymbolsOfDeath):IsUp()
    end):SetTarget(Target)
)

-- Use  Shadow Dance on cooldown as much as possible.
AOEAPL:AddSpell(
    ShadowDance:CastableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and Gloomblade:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and Player:GetComboPoints(Target) <= 2
    end):SetTarget(Player):OnCast(function()
        SpellCancelQueuedSpell()
        Gloomblade:Cast(Target) -- We want to cast gloomblade immediately with shadow dance to trigger 1 stack of danse macabre
        SpellCancelQueuedSpell()
    end)
)

-- Use  Thistle Tea with  Shadow Dance.
AOEAPL:AddSpell(
    ThistleTea:CastableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            Player:GetPowerDeficit() >= 100 and
            ThistleTea:GetTimeSinceLastCast() >= 3
    end):SetTarget(Player)
)

-- Use Finishing moves with 5 or more combo points with the following priority:
-- Cast  Slice and Dice if it needs to be refreshed for maintenance or if it is not up.
AOEAPL:AddSpell(
    SliceAndDice:CastableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            (Player:GetComboPoints(Target) >= 5) and
            (
            not Player:GetAuras():FindMy(SliceAndDice):IsUp() or
                Player:GetAuras():FindMy(SliceAndDice):GetRemainingTime() < 6
            )
            and Player:GetEnemies(10) < 6
    end):SetTarget(Target)
)

-- Cast  Rupture if it needs to be refreshed for maintenance or if it is not up.
AOEAPL:AddSpell(
    Rupture:CastableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            (Player:GetComboPoints(Target) >= 5) and (
            not Target:GetAuras():FindMy(Rupture):IsUp() or
                Target:GetAuras():FindMy(Rupture):GetRemainingTime() < 6
            )
            and not Player:GetAuras():FindMy(ShadowDanceAura):IsUp()
            and not Player:GetAuras():FindMy(SymbolsOfDeath):IsUp()
            and not Player:GetAuras():FindMy(ThistleTea):IsUp()
    end):SetTarget(Target)
)

-- Cast  Rupture on all targets. (scam??)
AOEAPL:AddSpell(
    Rupture:CastableIf(function(self)
        return RuptureTarget:Exists() and Player:InMelee(RuptureTarget) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            (Player:GetComboPoints(RuptureTarget) >= 6) and (
            not RuptureTarget:GetAuras():FindMy(Rupture):IsUp() or
                RuptureTarget:GetAuras():FindMy(Rupture):GetRemainingTime() < 6
            )
            and not Player:GetAuras():FindMy(ShadowDanceAura):IsUp()
    end):SetTarget(RuptureTarget)
)

AOEAPL:AddSpell(
    SecretTechnique:CastableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            (Player:GetComboPoints(Target) >= 6 or
                (Player:GetComboPoints(Target) >= 5 and
                    Player:GetAuras():FindMy(ShadowDanceAura):IsUp())) and Target:GetAuras():FindMy(Rupture):IsUp()
    end):SetTarget(Target)
)

-- Cast  Black Powder with 3 or more targets, 2 or more when talented into  Dark Brew.
AOEAPL:AddSpell(
    BlackPowder:CastableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            (Player:GetComboPoints(Target) >= 5) and
            (Player:GetEnemies(10) >= 3 or
                (Player:GetEnemies(10) >= 2 and
                    DarkBrew:IsKnown()))
    end):SetTarget(Target)
)

-- Cast  Eviscerate.
AOEAPL:AddSpell(
    Eviscerate:CastableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            Player:GetComboPoints(Target) >= 5
    end):SetTarget(Target)
)

-- Vanish - Is a fairly weak cooldown. It is best to use on low combo points for a  Shadowstrike cast. Use it after  Secret Technique in  Shadow Dance when playing with  Danse Macabre.
AOEAPL:AddSpell(
    Vanish:CastableIf(function(self)
        return Tank:Exists() and Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            Player:GetComboPoints(Target) < 4
    end):SetTarget(Player)
)

-- Use Combo Point builder with the following priority:
-- Use  Shuriken Storm on 2 targets outside of  Shadow Dance.
AOEAPL:AddSpell(
    ShurikenStorm:CastableIf(function(self)
        return Target:Exists() and Player:GetDistance(Target) <= 10 and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            Player:GetEnemies(10) == 2 and
            not Player:GetAuras():FindMy(ShadowDanceAura):IsUp()
    end):SetTarget(Player)
)

-- Use  Shadowstrike on 2 and 3 targets during  Shadow Dance or to proc  Premeditation.
AOEAPL:AddSpell(
    Shadowstrike:CastableIf(function(self)
        return Target:Exists() and Player:InMelee(Target) and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling() and
            Player:GetEnemies(10) >= 2 and Player:GetEnemies(10) <= 3 and
            Player:GetAuras():FindMy(ShadowDanceAura):IsUp()
    end):SetTarget(Target)
)

-- Use  Shuriken Storm
AOEAPL:AddSpell(
    ShurikenStorm:CastableIf(function(self)
        return Target:Exists() and Player:GetDistance(Target) <= 10 and
            self:IsKnownAndUsable() and
            not Player:IsCastingOrChanneling()
    end):SetTarget(Player)
)

SubModulue:Sync(function()
    SpecialAPL:Execute()
    if Player:GetEnemies(10) >= 2 then
        AOEAPL:Execute()
    else
        DefaultAPL:Execute()
    end
    RacialsAPL:Execute()
end)

Bastion:Register(SubModulue)