if myHero.charName ~= 'Fiora' then return end

local reborn = false
local loaded = false

class 'MyOrbwalker'

function MyOrbwalker:__init(targetSelector)
    self.targetSelector = targetSelector

    self.lastAttack = 0
    self.timeToMove = 0
    self.timeToAttack = 0

    self.holdRange = 57.5 * 57.5
    self.staticRange = myHero.range + 65

    AddTickCallback(function()
        self.dynamicTime = os.clock() + GetLatency() / 1000
        if Menu.Orbwalker.IsActive then
            self.autoAttackTarget = self.targetSelector:getConsideredTarget(0)
            if self.autoAttackTarget and self:canAttack() then
                CastItem(3142)
                myHero:Attack(self.autoAttackTarget)
            elseif self:canMove() and GetDistanceSqr(mousePos) > self.holdRange then
                local moveToVector = Vector(myHero) + (Vector(mousePos) - Vector(myHero)) * 250 / GetDistance(mousePos)
                myHero:MoveTo(moveToVector.x, moveToVector.z)
            end
        end
    end)
    AddProcessSpellCallback(function(unit, spell)
        if unit.isMe then
            if spell.name:lower():find('attack') then
                self.lastAttack = os.clock()
                self.timeToMove = self.lastAttack + spell.windUpTime + Menu.Orbwalker.ExtraWindupTime / 1000
                self.timeToAttack = self.lastAttack + spell.animationTime
                self.afterAttackCheck = true
            elseif spell.name:lower():find('flurry') then
                self.timeToMove = 0
                self.timeToAttack = 0
            end
        end
    end)
    return self
end

function MyOrbwalker:canAttack()
    if self.lastAttack <= os.clock() then
        return self.dynamicTime > self.timeToAttack
    end
    return false
end

function MyOrbwalker:canMove()
    if self.lastAttack <= os.clock() then
        return self.dynamicTime > self.timeToMove
    end
    return false
end

function MyOrbwalker:afterAttack()
    if self.dynamicTime > self.timeToMove and self.afterAttackCheck then
        self.afterAttackCheck = false
        return true
    end
    return false
end

function MyOrbwalker:afterAttackTick()
    return self.dynamicTime > self.timeToMove and self.dynamicTime < self.timeToAttack
end

class 'MyTargetSelector'

function MyTargetSelector:__init()
    self.selectRange = 200 * 200
    self.staticRange = myHero.range + 65

    self.consideredTarget = {}
    self.consideredRatios = {}

    self.opponentHeroes = GetEnemyHeroes()

    AddTickCallback(function()
        if self.selectedTarget and not ValidTarget(self.selectedTarget) then
            self.selectedTarget = nil
        end
        for i = 0, 4 do
            self.consideredRatios[i] = 0
            self.consideredTarget[i] = nil
        end
        for _, opponent in pairs(self.opponentHeroes) do
            if not self.selectedTarget or self.selectedTarget == opponent then
                local consideredRange = 600
                while consideredRange >= 200 do
                    if consideredRange >= 300 then
                        if GetDistanceSqr(opponent) < (consideredRange * consideredRange) - 9 and ValidTarget(opponent) then
                            local index = (700 - consideredRange) / 100
                            local currentRatio = myHero.totalDamage / opponent.armor / opponent.health
                            if currentRatio > self.consideredRatios[index] then
                                self.consideredRatios[index] = currentRatio
                                self.consideredTarget[index] = opponent
                            end
                        end
                    else
                        local autoAttackRange = self.staticRange + opponent.boundingRadius
                        if GetDistanceSqr(opponent) < autoAttackRange * autoAttackRange and ValidTarget(opponent) then
                            local currentRatio = myHero.totalDamage / opponent.armor / opponent.health
                            if currentRatio > self.consideredRatios[0] then
                                self.consideredRatios[0] = currentRatio
                                self.consideredTarget[0] = opponent
                            end
                        end
                    end
                    consideredRange = consideredRange - 100
                end
            end
        end
        if reborn then
            if self.selectedTarget then
                _G.AutoCarry.Crosshair:ForceTarget(self.selectedTarget)
            else
                _G.AutoCarry.Crosshair:ForceTarget()
            end
        end
    end)
    AddMsgCallback(function(message, key)
        if message == 514 and Menu.Orbwalker.SelectedTarget then
            self.selectedTarget = nil
            for _, opponent in pairs(self.opponentHeroes) do
                if GetDistanceSqr(opponent, mousePos) < self.selectRange and ValidTarget(opponent) then
                    self.selectedTarget = opponent
                end
            end
        end
    end)
    AddDrawCallback(function()
        if self.selectedTarget and not myHero.dead then
            local barPos = GetUnitHPBarPos(myHero)
            local barPosOffset = GetUnitHPBarOffset(myHero)
            local startX = barPos.x + (barPosOffset.x * 171)
            local startY = barPos.y + (barPosOffset.y * 46)
            DrawText(self.selectedTarget.charName, 18, startX - 28.5, startY + 126.6, ARGB(255, 0, 0, 0))
            DrawText(self.selectedTarget.charName, 18, startX - 30, startY + 125, ARGB(255, 0, 200, 200))
            DrawCircle2(self.selectedTarget.x, self.selectedTarget.y, self.selectedTarget.z, 150, ARGB(255, 0, 200, 200))
        end
    end)

    return self
end

function MyTargetSelector:getConsideredTarget(i)
    return self.consideredTarget[i]
end

class 'GagongW'

function GagongW:__init()
    self.activeAttacks = {}
    self.opponentHeroes = GetEnemyHeroes()
    self.spellList = {
        missfortune = 'missfortunericochetshot', 
        gangplank = 'parley',
        jax = 'jaxrelentlessattack',
        talon = 'talonnoxiandiplomacyattack',
        renekton = 'renektonexecute', 
        garen = 'garenslash2',
        nasus = 'nasusqattack', 
        shyvana = 'shyvanadoubleattackhit',
        leona = 'leonashieldofdaybreakattack',
        darius = 'dariusnoxiantacticsonhattack'
    }
    self.currentSpellList = {}

    for _, opponent in pairs(self.opponentHeroes) do
        if self.spellList[opponent.charName:lower()] then
            self.currentSpellList[opponent.charName:lower()] = true
        end
    end

    AddTickCallback(function()
        for i, attack in pairs(self.activeAttacks) do
            if attack and os.clock() + GetLatency() / 1000 > attack then
                if myHero:CanUseSpell(1) == 0 then
                    CastSpell(1)
                end
                self.activeAttacks[i] = nil
            end
        end
    end)
    AddProcessSpellCallback(function(unit, spell)
        if unit.type == myHero.type and unit.team ~= myHero.team and spell.target and spell.target == myHero then
            if self.currentSpellList[spell.name:lower()] then
                CastSpell(1)
            elseif spell.name:lower():find('attack') and not spell.name:lower():find('monkeykingdoubleattack') then
                if not Menu.Gagong.BlockAutoAttacks then return end
                if unit.range > 300 then
                    self.activeAttacks[unit.networkID] = os.clock() + spell.windUpTime + GetLatency() / 2000
                else
                    self.activeAttacks[unit.networkID] = os.clock() + spell.windUpTime - GetLatency() / 1000 - 0.025
                end
           end
        end
    end)

    return self
end

class 'GagongR'
function GagongR:__init(targetSelector)
    self.targetSelector = targetSelector

    self.spellData = {
        dariusexecute           = {targeted = true, dangerous = true},
        garenr                  = {targeted = true, dangerous = true},
        syndrar                 = {targeted = true, dangerous = true},
        bustershot              = {targeted = true, dangerous = true},
        veigarprimordialburst   = {targeted = true, dangerous = true},
        alzaharnethergrasp      = {targeted = true, dangerous = true},
        infiniteduress          = {targeted = true, dangerous = true},
        urgotswap2              = {targeted = true, dangerous = true},
        blindmonkrkick          = {targeted = true, dangerous = true},
        skarnerimpale           = {targeted = true, dangerous = true},
        xinzhaosweep            = {targeted = true, dangerous = true},
        pantheon_leapbash       = {targeted = true, health = 3 / 4},
        luluwtwo                = {targeted = true, health = 3 / 4},
        maokaiunstablegrowth    = {targeted = true, health = 2 / 3},
        runeprison              = {targeted = true, health = 2 / 4},
        blindingdart            = {targeted = true, health = 3 / 4},
        headbutt                = {targeted = true, health = 2 / 4},
        terrify                 = {targeted = true, health = 3 / 4},
        vaynecondemn            = {targeted = true, health = 1},
        galioidolofdurand       = {unit = true, range = 600 * 600},
        curseofthesadmummy      = {unit = true, range = 600 * 600},
        rivenizunablade         = {unit = true, range = 350 * 350},
        vir                     = {targeted = true, unit = true, range = 600 * 600},
        infernalguardian        = {range = 250 * 250},
        vladimirhemoplague      = {range = 400 * 400},
        oriannadetonatecommand  = {delay = true, delay = 0.21, range = 380 * 380},
        zedult                  = {targeted = true, delay = true, delay = 0.3}      
    }

    AddTickCallback(function()
        if Menu.Gagong.MagneticTouch and myHero:CanUseSpell(_R) == READY then
            local bestTarget = self.targetSelector:getConsideredTarget(3)
            if bestTarget then
                CastSpell(3, bestTarget)
            end
        end
    end)
    AddProcessSpellCallback(function(unit, spell)
        if unit.type == myHero.type and unit.team ~= myHero.team and Menu.Gagong.DodgeSpells then
            self.dodgeTarget = self.targetSelector:getConsideredTarget(3)
            if self.dodgeTarget and self.spellData[spell.name:lower()] then
                if self.spellData[spell.name:lower()].targeted and spell.target and spell.target == myHero then
                    if self.spellData[spell.name:lower()].dangerous then
                        CastSpell(_R, self.dodgeTarget)
                    elseif self.spellData[spell.name:lower()].health and myHero.health / myHero.maxHealth < self.spellData[spell.name:lower()].health then
                        if Menu.Orbwalker.IsActive then
                            CastSpell(_R, self.dodgeTarget)
                        end
                    elseif self.spellData[spell.name:lower()].unit and GetDistanceSqr(unit) < self.spellData[spell.name:lower()].unit then
                       CastSpell(_R, self.dodgeTarget)
                    elseif self.spellData[spell.name:lower()].delay then
                        DelayAction(function()
                            if self.targetSelector:getConsideredTarget(3) then
                                CastSpell(_R, self.targetSelector:getConsideredTarget(3))
                            end
                        end, self.spellData[spell.name:lower()].delay - GetLatency () / 1000)
                    end
                elseif self.spellData[spell.name:lower()].unit and GetDistanceSqr(unit) < self.spellData[spell.name:lower()].range then
                    CastSpell(_R, self.dodgeTarget)
                elseif self.spellData[spell.name:lower()].delay then
                    if self.spellData[spell.name:lower()].range and GetDistanceSqr(unit) < self.spellData[spell.name:lower()].delay then
                        DelayAction(function()
                            if self.targetSelector:getConsideredTarget(3) then
                                CastSpell(_R, self.targetSelector:getConsideredTarget(3))
                            end
                         end, self.spellData[spell.name:lower()].delay - GetLatency() / 1000)
                    end
                elseif self.spellData[spell.name:lower()].range and GetDistanceSqr(spell.endPos) < self.spellData[spell.name:lower()].range then
                    CastSpell(_R, self.dodgeTarget)
                end
            end    
        end
    end)

    return self
end

class 'GagongCombo'

function GagongCombo:__init(targetSelector, orbwalker)
    self.orbwalker = orbwalker
    self.targetSelector = targetSelector

    self.secondActive = false
    
    AddTickCallback(function()
        if self.secondActive and os.clock() - self.secondActive > 4 then
            self.secondActive = nil
        end
        self.afterAttackTarget = self.targetSelector:getConsideredTarget(0)
        if not reborn and self.orbwalker:afterAttack() then
            if Menu.Gagong.IsActive and self.afterAttackTarget then
                local canCastE = myHero:CanUseSpell(2) == 0
                local canCastQ = myHero:CanUseSpell(0) == 0 and ((Menu.Gagong.AfterAttackFirst and not self.secondActive) or (Menu.Gagong.AfterAttackSecond and self.secondActive))
                if canCastE then
                    if canCastQ then
                        CastSpell(0, self.afterAttackTarget)
                    end
                    CastSpell(2)
                elseif canCastQ then
                    CastSpell(0, self.afterAttackTarget)
                    if true then
                        CastItem(3077)
                        CastItem(3074)
                    end
                else
                    CastItem(3077)
                    CastItem(3074)
                end
            end
        elseif not self.afterAttackTarget and ((reborn and _G.AutoCarry.Orbwalker:CanMove() and not _G.AutoCarry.Orbwalker:CanShoot()) or (not reborn and self.orbwalker:afterAttackTick())) then
            self.afterAttackTarget = self.targetSelector:getConsideredTarget(4)
            if Menu.Gagong.IsActive and self.afterAttackTarget then
                local canCastQ = myHero:CanUseSpell(0) == 0 and ((Menu.Gagong.AfterAttackFirst and not self.secondActive) or (Menu.Gagong.AfterAttackSecond and self.secondActive))
                if canCastQ then
                    CastSpell(0, self.afterAttackTarget)
										CastSpell(2)
                else
										CastSpell(2)
                    CastItem(3077)
                    CastItem(3074)
                end
            end
        end
        if Menu.Gagong.IsActive then
            self.gapCloseTarget = self.targetSelector:getConsideredTarget(1)
            if self.gapCloseTarget and myHero:CanUseSpell(0) == 0 then
                local currentDistance = GetDistanceSqr(self.gapCloseTarget)
                if currentDistance > Menu.Gagong.GapcloseRange * Menu.Gagong.GapcloseRange then
                    CastSpell(0, self.gapCloseTarget)
                    local dynamicRange = 600 - (0.672 * myHero.attackSpeed) * 60
                    if currentDistance > dynamicRange * dynamicRange then
                        CastItem(3077)
                        CastItem(3074)
                    end
                end
            end
        end
    end)
    AddProcessSpellCallback(function(unit, spell)
        if unit.isMe and spell.name:lower():find('fioraq') and not self.secondActive then
            self.secondActive = os.clock() - GetLatency() / 2000
        end
    end)
    return self
end

function GagongCombo:afterAttack()
    self.afterAttackTarget = self.targetSelector:getConsideredTarget(0)
    if Menu.Gagong.IsActive and self.afterAttackTarget then
        local canCastE = myHero:CanUseSpell(2) == 0
        local canCastQ = myHero:CanUseSpell(0) == 0 and ((Menu.Gagong.AfterAttackFirst and not self.secondActive) or (Menu.Gagong.AfterAttackSecond and self.secondActive))
        if canCastE then
            if canCastQ then
                CastSpell(0, self.afterAttackTarget)
            end
            CastSpell(2)
        elseif canCastQ then
            CastSpell(0, self.afterAttackTarget)
            if true then
                CastItem(3077)
                CastItem(3074)
            end
        else
            CastItem(3077)
            CastItem(3074)
        end
    end
end

function OnDraw()
    if myHero.dead or not loaded then return end
    if Menu.Drawing.AutoAttackRange then
        DrawCircle2(myHero.x, myHero.y, myHero.z, myHero.range + 120, ARGB(255, 255, 255, 255))
    end
    if Menu.Drawing.RRange then
        DrawCircle2(myHero.x, myHero.y, myHero.z, 400, ARGB(255, 255, 255, 255))
    end
    if Menu.Drawing.QRange then
        DrawCircle2(myHero.x, myHero.y, myHero.z, 600, ARGB(255, 0, 200, 200))
    end
    if Menu.Drawing.QCastRange then
        DrawCircle2(myHero.x, myHero.y, myHero.z, Menu.Gagong.GapcloseRange, ARGB(255, 0, 200, 200))
    end
    if Menu.Drawing.ComboAwareness then
        local barPos = GetUnitHPBarPos(myHero)
        local barPosOffset = GetUnitHPBarOffset(myHero)
        local startX = barPos.x + (barPosOffset.x * 171) - 42
        local startY = barPos.y + (barPosOffset.y * 46) + 14
        if Menu.Gagong.AfterAttackFirst then
            DrawLine(startX, startY, startX + 15, startY, 14, ARGB(255, 99, 99, 99))
            DrawText('1', 14, startX + 5, startY - 6.5, ARGB(255, 255, 255, 255))
        end
        if Menu.Gagong.AfterAttackSecond then
            DrawLine(startX+15, startY, startX + 66, startY, 14, ARGB(255, 0, 139, 139))
            DrawText('GAGONG', 14, startX + 17, startY - 6.5, ARGB(255, 255, 255, 255))
        end
    end
end

-- vadash etc
function DrawCircleNextLvl(x, y, z, radius, width, color, chordlength)
    radius = radius or 300
    quality = math.max(8,math.floor(180/math.deg((math.asin((chordlength/(2*radius)))))))
    quality = 2 * math.pi / quality
    radius = radius*.92
    local points = {}
    for theta = 0, 2 * math.pi + quality, quality do
        local c = WorldToScreen(D3DXVECTOR3(x + radius * math.cos(theta), y, z - radius * math.sin(theta)))
        points[#points + 1] = D3DXVECTOR2(c.x, c.y)
    end
    DrawLines2(points, 1, color or 4294967295)
end

function DrawCircle2(x, y, z, radius, color)
    local vPos1 = Vector(x, y, z)
    local vPos2 = Vector(cameraPos.x, cameraPos.y, cameraPos.z)
    local tPos = vPos1 - (vPos1 - vPos2):normalized() * radius
    local sPos = WorldToScreen(D3DXVECTOR3(tPos.x, tPos.y, tPos.z))
    if OnScreen({ x = sPos.x, y = sPos.y }, { x = sPos.x, y = sPos.y }) then
        DrawCircleNextLvl(x, y, z, radius, 2, color, 75)
    end
end

function OnLoad()
    if _G.Reborn_Initialised then
        reborn = true
      print('<font color=\"#FA8072\"><b>Gagong Fiora:</b> SAC integrated successfully.')
   elseif _G.Reborn_Loaded then
    print('<font color=\"#FA8072\"><b>Gagong Fiora:</b> Waiting for SAC authentication...')
      DelayAction(OnLoad, 2)
      return
    end
    Menu = scriptConfig('Gagong Fiora', 'GagongFiora_113')
    Menu:addSubMenu('Drawing', 'Drawing')
    Menu.Drawing:addParam('QRange', 'Q Range', SCRIPT_PARAM_ONOFF, true)
    Menu.Drawing:addParam('QCastRange', 'Q Cast Range', SCRIPT_PARAM_ONOFF, true)
    Menu.Drawing:addParam('RRange', 'R Range', SCRIPT_PARAM_ONOFF, false)
    Menu.Drawing:addParam('AutoAttackRange', 'AA Range', SCRIPT_PARAM_ONOFF, true)
    Menu.Drawing:addParam('ComboAwareness', 'Combo Awareness', SCRIPT_PARAM_ONOFF, true)

    local myTs = MyTargetSelector()
    local myOrb = nil
    if not reborn then
        Menu:addSubMenu('Orbwalker', 'Orbwalker')
    Menu.Orbwalker:addParam('DetectCancel', 'Detect Cancel', SCRIPT_PARAM_ONOFF, false)
    Menu.Orbwalker:addParam('SelectedTarget', 'Focus Left Clicked Target', SCRIPT_PARAM_ONOFF, true)
    Menu.Orbwalker:addParam('ExtraWindupTime', 'Additional Windup Time', SCRIPT_PARAM_SLICE, 25, 0, 100)
    Menu.Orbwalker:addParam('IsActive', 'Orbwalk Key', SCRIPT_PARAM_ONKEYDOWN, false, 32)
        myOrb = MyOrbwalker(myTs)
        print('<font color=\"#FA8072\"><b>Gagong Fiora:</b> Internal Orbwalker integrated successfully.')
    else
        Menu:addSubMenu('Selector', 'Orbwalker')
        Menu.Orbwalker:addParam('SelectedTarget', 'Focus Left Clicked Target', SCRIPT_PARAM_ONOFF, true)
    end
    Menu:addSubMenu('Gagong Fiora', 'Gagong')
    Menu.Gagong:addParam('BlockAutoAttacks', 'Block Auto Attacks', SCRIPT_PARAM_ONOFF, true)
    Menu.Gagong:addParam('GapcloseRange', 'Min Range To Cast Q', SCRIPT_PARAM_SLICE, 325, 0, 600)
    Menu.Gagong:addParam('AfterAttackFirst', 'Q1 in AfterAttackCombo', SCRIPT_PARAM_ONKEYTOGGLE, true, string.byte('C'))
    Menu.Gagong:addParam('AfterAttackSecond', 'Q2 in AfterAttackCombo', SCRIPT_PARAM_ONKEYTOGGLE, false, string.byte('V'))
    Menu.Gagong:addParam('DodgeSpells', 'Dodge Spells with R', SCRIPT_PARAM_ONOFF, true)
    Menu.Gagong:addParam('MagneticTouch', 'Cast R on Best Target', SCRIPT_PARAM_ONKEYDOWN, false, string.byte('R'))
    Menu.Gagong:addParam('IsActive', 'Combo Key', SCRIPT_PARAM_ONKEYDOWN, false, 32)
    GagongW()
    GagongR(myTs)
    local myCombo = GagongCombo(myTs, myOrb)
    if reborn then 
        _G.AutoCarry.Plugins:RegisterOnAttacked(function() myCombo:afterAttack() end)
    end
    print('<font color=\"#FA8072\"><b>Gagong Fiora:</b> Loaded.')
    loaded = true
end
