local UIPrefabObject = require "app/bf/unity/uiprefab_object"
local BattlePool = require "app/module/battle/helper/battle_pool"
local BattleScheduler = require "app/module/battle/helper/battle_scheduler"

local BattleHelper = {}

local BATTLE_FX_PATH = "assets/prefabs/effects/battle/%s.prefab"
local BATTLE_SKILL_SOUND_PATH = "assets/arts/sounds/sfx/battle/%s.wav"

function BattleHelper:init()
	self.isClear = false
	self.characterPools = {}
	self.characterMap = {}
    self.buffEffectPool = {}
    self.battleEffectTextMap = {}
    self.battleEffectTextPool = {}
    self.battleFxPaths = {}
    self.effectPools = BattlePool:getEffectPool()
    self.effectMap = {}
    self.seed = tonumber(tostring(os.time()):reverse():sub(1,6))
    self.baseOrder = 0
    self.skillSoundPath = {}
    self.damageOrCureType2MatchType = {}
end

function BattleHelper:setBaseOrder(baseOrder)
    self.baseOrder = baseOrder
end

function BattleHelper:getBaseOrder()
    return self.baseOrder
end

function BattleHelper:setBattleData(battleBaseData)
    self.battleData = battleBaseData
end

function BattleHelper:getBattleData()
    return self.battleData
end

function BattleHelper:getFxConfig()
    if self.fxCfg == nil then
        self.fxCfg = ConfigManager:getConfig("fx")
    end
    return self.fxCfg
end

function BattleHelper:setTimeScale(timeScale)
    if self.effectMap then
        for k, effect in pairs(self.effectMap) do
            effect:setTimeScale(timeScale)
        end
    end
    if self.characterMap then
        for k, character in pairs(self.characterMap) do
            character:setTimeScale(timeScale)
        end
    end
end

function BattleHelper:random(min, max)
    self.seed = (self.seed*9301 + 49297)%233280
    return min + self.seed*(max - min + 1)//233280
end

function BattleHelper:getSupportInterval()
    if self.supportInterval == nil then
        self.supportInterval = GFunc.getConstIntValue("support_interval")/1000
    end
    return self.supportInterval
end

function BattleHelper:getDefaultCrittime()
    if self.defaultCrittime == nil then
        self.defaultCrittime = GFunc.getConstIntValue("crit_dmg")
    end
    return self.defaultCrittime
end

function BattleHelper:loadBattleHeroModel(id, parent, callback)
    local pool = self.characterPools[id]
    if pool and #pool > 0 then
        local spineObject = table.remove(pool)
        spineObject:setActive(true)
        if self.characterMap then
            self.characterMap[spineObject:getInstanceID()] = spineObject
        end
        local timeScale = self:getBattleData():getTimeScale()
        spineObject:setTimeScale(timeScale)
        callback(spineObject)
    else
        SpineManager:loadHeroAsync(id, parent, function(spineObject)
            spineObject:setDefaultMix(0)
            if self.characterMap then
                self.characterMap[spineObject:getInstanceID()] = spineObject
                local timeScale = self:getBattleData():getTimeScale()
                spineObject:setTimeScale(timeScale)
                callback(spineObject)
            end
        end)
    end
end

function BattleHelper:recycleBattleHeroModel(modelId, character)
    if character:isDestroyed() then
		return
	end
    character:setActive(false)
    if self.characterMap then
        self.characterMap[character:getInstanceID()] = nil
    end
    if self.characterPools then
        local pool = self.characterPools[modelId]
        if pool == nil then
            pool = {}
            self.characterPools[modelId] = pool
        end
        table.insert(pool, character)
    end
end

function BattleHelper:getEffectAsync(name, parent, callback)
    local path = self.battleFxPaths[name]
    if path == nil then
        path = string.format(BATTLE_FX_PATH, name)
        self.battleFxPaths[name] = path
    end
    local effect = self.effectPools:pop(path)
    if effect then
        -- if parent then
        --     effect:setParent(parent, false)
        -- end
        if self.effectMap then
            self.effectMap[effect:getInstanceID()] = effect
        end
        local timeScale = self:getBattleData():getTimeScale()
        effect:setTimeScale(timeScale)
        effect:setActive(true)
        callback(effect)
    else
        EffectManager:loadBattleEffectAsync(path, parent, function(effect)
            if self.effectMap then
                effect:initParticleSystemRendererList()
                self.effectMap[effect:getInstanceID()] = effect
                local timeScale = self:getBattleData():getTimeScale()
                effect:setTimeScale(timeScale)
                callback(effect)
            end
        end)
    end
end

function BattleHelper:recycleEffect(effect)
    if effect:isDestroyed() then
		return
	end
    local comps = effect:getAllComponents()
    if comps then
        local boneFollower = comps[GConst.TYPEOF_UNITY_CLASS.BONE_FOLLOWER_GRAPHIC]
        if boneFollower and boneFollower.SkeletonGraphic then
            boneFollower.SkeletonGraphic = nil
        end
    end
    effect:stop()
    effect:setActive(false)
    if self.effectPools then
        self.effectPools:push(effect, true)
    end
    if self.effectMap then
        self.effectMap[effect:getInstanceID()] = nil
    end
end

function BattleHelper:addSpineBoneFollowerGraphic(effectObj)
    local boneFollower = effectObj:getComponent(GConst.TYPEOF_UNITY_CLASS.BONE_FOLLOWER_GRAPHIC)
    if boneFollower == nil then
        boneFollower = effectObj:addComponent(GConst.TYPEOF_UNITY_CLASS.BONE_FOLLOWER_GRAPHIC)
        boneFollower.followBoneRotation = false
        boneFollower.followSkeletonFlip = false
    else
        boneFollower.enabled = true
    end
    return boneFollower
end

function BattleHelper:setEffectTextCache(cache1, cache2, cache3, cache4, cache5)
    if self.effectTextCacheList == nil then
        self.effectTextCacheList = {}
    end
    self.effectTextCacheList[1] = cache1
    self.effectTextCacheList[2] = cache2
    self.effectTextCacheList[3] = cache3
    self.effectTextCacheList[4] = cache4
    self.effectTextCacheList[5] = cache5
end

function BattleHelper:getEffectText(parent, colorType)
    local pool = self.battleEffectTextPool[colorType]
    if pool == nil then
        pool = {}
        self.battleEffectTextPool[colorType] = {}
    end
    if #pool <= 0 then
        local prefab = CS.UnityEngine.Object.Instantiate(self.effectTextCacheList[colorType]:getGameObject())
        local prefabObject = UIPrefabObject:create()
		prefabObject:initWithPrefab(GConst.EMPTY_STRING, prefab)
		prefabObject:initPrefabHelper()
        prefabObject:getTransform():SetAsLastSibling()
        prefabObject:setParent(parent, false)
        local comp = prefabObject:addLuaComponent(GConst.BattleConst.TYPEOF_LUA_COMP.BATTLE_NUMBER_COMPONENT)
        comp:setColorType(colorType)
        comp:setEnabled(true)
		return comp
	else
		local effectComp = table.remove(pool)
		effectComp:setEnabled(true)
		effectComp.baseObject:getTransform():SetAsLastSibling()
		self.battleEffectTextMap[effectComp.baseObject:getInstanceID()] = effectComp
		return effectComp
	end
end

function BattleHelper:recycleEffectText(comp)
	if comp:isDestroyed() then
		return
	end
    comp:setEnabled(false)
    local colorType = comp:getColorType()
    local pool = self.battleEffectTextPool[colorType]
    if pool == nil then
        pool = {}
        self.battleEffectTextPool[colorType] = {}
    end
	table.insert(pool, comp)
	if self.battleEffectTextMap then
        self.battleEffectTextMap[comp.baseObject:getInstanceID()] = nil
    end
end

function BattleHelper:getBuffEffect()
    if #self.buffEffectPool > 0 then
        return table.remove(self.buffEffectPool)
    end
    return {}
end

function BattleHelper:recycleBuffEffect(buffEffect)
    table.insert(self.buffEffectPool, buffEffect)
end

function BattleHelper:playSkillSound(name, delay)
    if self.skillSoundPath == nil or not name then
        return
    end
    local soundFullPath = self.skillSoundPath[name]
    if soundFullPath == nil then
        soundFullPath = string.format(BATTLE_SKILL_SOUND_PATH, name)
        self.skillSoundPath[name] = soundFullPath
    end
    if delay and delay > 0 then
        self:performDurationDelay(delay, function()
            AudioManager:playEffect(soundFullPath)
        end)
    else
        AudioManager:playEffect(soundFullPath)
    end
end

function BattleHelper:getDamageOrCureType2MatchType(damageOrCureType)
    if not damageOrCureType then
        return GConst.BattleConst.ELEMENT_TYPE.NONE
    end

    if not self.damageOrCureType2MatchType[damageOrCureType] then
        local elementType = GConst.BattleConst.ELEMENT_TYPE.NONE
        if string.match(damageOrCureType, "red") then
            elementType = GConst.BattleConst.ELEMENT_TYPE.RED
        elseif string.match(damageOrCureType, "yellow") then
            elementType = GConst.BattleConst.ELEMENT_TYPE.YELLOW
        elseif string.match(damageOrCureType, "green") then
            elementType = GConst.BattleConst.ELEMENT_TYPE.GREEN
        elseif string.match(damageOrCureType, "blue") then
            elementType = GConst.BattleConst.ELEMENT_TYPE.BLUE
        elseif string.match(damageOrCureType, "purple") then
            elementType = GConst.BattleConst.ELEMENT_TYPE.PURPLE
        end
        self.damageOrCureType2MatchType[damageOrCureType] = elementType
    end

    return self.damageOrCureType2MatchType[damageOrCureType]
end

function BattleHelper:performDurationDelay(delay, func)
    return BattleScheduler:performWithDelayGlobal(func, delay)
end

function BattleHelper:scheduleGlobal(inter, func)
    return BattleScheduler:scheduleGlobal(func, inter)
end

function BattleHelper:unscheduleGlobal(scheduleId)
    BattleScheduler:unscheduleGlobal(scheduleId)
end

function BattleHelper:clear()
	self.isClear = true
	self.characterPools = nil
	self.characterMap = nil
    self.effectTextCache = nil
    self.buffEffectPool = nil
    self.battleEffectTextMap = nil
    self.battleEffectTextPool = nil
    self.battleFxPaths = nil
    self.effectPools = nil
    self.effectMap = nil
    self.skillSoundPath = nil
end

return BattleHelper