local UISpineObject = require "app/bf/unity/ui_spine_object"
local MeshSpineObject = require "app/bf/unity/mesh_spine_object"
local CharacterSpineObject = require "app/bf/unity/character_spine_object"
local SpineManager = {
	heroCacheList = {},
	heroCacheMap = {},
	battleCacheList = {},
	battleCacheMap = {},
	heroSpineAsset = {}
}

local TYPE_OF_GAME_OBJECT = GConst.TYPEOF_UNITY_CLASS.GAME_OBJECT
local TYPE_OF_SPINE_ASSET = GConst.TYPEOF_UNITY_CLASS.SKELETON_DATA_ASSET

local UI_SPINE_PREFAB_PATH = "assets/prefabs/spine/ui/ui_spine_obj.prefab"
local MESH_SPINE_PREFAB_PATH = "assets/prefabs/spine/mesh/mesh_spine_obj.prefab"

local UI_SPINE_ASSET_PATH = "assets/arts/spines/ui/%s/%s_skeletondata.asset"
local MESH_SPINE_ASSET_PATH = "assets/arts/spines/mesh/%s/%s_skeletondata.asset"
local HERO_SPINE_ASSET_PATH = "assets/arts/spines/characters/%s/%s_skeletondata.asset"

local HERO_CACHE_SIZE = 10	-- 英雄缓存容量
local BATTLE_CACHE_SIZE = 30  -- 战斗特效缓存容量

---- canvas renderer
function SpineManager:loadUISpineWidgetAsync(name, parent, callback)
	local path = string.format(UI_SPINE_ASSET_PATH, name, name)
	ResourceManager:loadOriginAssetAsync(path, TYPE_OF_SPINE_ASSET, function(spineAssetPath, spineAsset)
		if parent and parent:isDestroyed() then
			ResourceManager:unload(spineAssetPath)
			return
		end
		self:loadUISpinePrefabAsync(parent, spineAssetPath, spineAsset, callback)
	end)
end

function SpineManager:loadUIHeroSpineWidgetAsync(name, parent, callback)
	local path = string.format(HERO_SPINE_ASSET_PATH, name, name)
	ResourceManager:loadOriginAssetAsync(path, TYPE_OF_SPINE_ASSET, function(spineAssetPath, spineAsset)
		if parent and parent:isDestroyed() then
			ResourceManager:unload(spineAssetPath)
			return
		end
		self:loadUISpinePrefabAsync(parent, spineAssetPath, spineAsset, callback)
	end)
end

function SpineManager:loadUISpinePrefabAsync(parent, spineAssetPath, spineAsset, callback)
	ResourceManager:loadAsync(UI_SPINE_PREFAB_PATH, TYPE_OF_GAME_OBJECT, function(prefabPath, prefab)
		if parent and parent:isDestroyed() then
			ResourceManager:destroyPrefab(prefab)
			ResourceManager:unload(spineAssetPath)
			ResourceManager:unload(prefabPath)
			return
		end
		local uiSpineObject = UISpineObject:create()
		uiSpineObject:initWithPrefab(UI_SPINE_PREFAB_PATH, prefab)
		uiSpineObject:getAnimationState()
		uiSpineObject:initSkeletonDataAsset(spineAsset)
		uiSpineObject:addUnloadCallback(function(obj)
			ResourceManager:unload(spineAssetPath)
			ResourceManager:unload(prefabPath)
		end)
		if parent then
			uiSpineObject:setParent(parent, false)
		end
		if callback then
			callback(uiSpineObject)
		end
	end)
end

function SpineManager:loadHeroAsync(id, parent, callback)
	local path = "assets/prefabs/spine/ui/characters/" .. id .. ".prefab"
	ResourceManager:loadAsync(path, TYPE_OF_GAME_OBJECT, function(assetPath, prefab)
		if parent and parent:isDestroyed() then
			ResourceManager:destroyPrefab(prefab)
			ResourceManager:unload(assetPath)
			return
		end
		local characterObject = CharacterSpineObject:create()
		characterObject:initWithPrefab(assetPath, id, prefab)
		characterObject:addUnloadCallback(function(obj)
			local modelPath = obj:getAssetPath()
			if self.heroCacheMap[modelPath] then
				ResourceManager:unload(modelPath)
			else
				if #self.heroCacheList >= HERO_CACHE_SIZE then
					local headPath = table.remove(self.heroCacheList, 1)
					ResourceManager:unload(headPath)
					self.heroCacheMap[headPath] = nil
				end
				self.heroCacheMap[modelPath] = true
				table.insert(self.heroCacheList, modelPath)
			end
		end)
		if parent then
			characterObject:setParent(parent, false)
		end
		if callback then
			callback(characterObject)
		end
	end)
end

function SpineManager:loadHeroSpineAssetAsync(id, parent, callback)
	if self.heroSpineAsset[id] then
        callback(self.heroSpineAsset[id])
    else
        local path = string.format(HERO_SPINE_ASSET_PATH, id, id)
		ResourceManager:loadOriginAssetAsync(path, TYPE_OF_SPINE_ASSET, function(spineAssetPath, spineAsset)
			if parent and parent:isDestroyed() then
				ResourceManager:unload(spineAssetPath)
				return
			end
			if self.heroSpineAsset[id] then
                ResourceManager:unload(spineAssetPath)
            else
                self.heroSpineAsset[id] = spineAsset
            end
            callback(spineAsset)
		end)
    end
end

function SpineManager:loadBattleSpineAssetAsync(path, parent, callback)
	ResourceManager:loadOriginAssetAsync(path, TYPE_OF_SPINE_ASSET, function(spineAssetPath, spineAsset)
		if parent and parent:isDestroyed() then
			ResourceManager:unload(spineAssetPath)
			return
		end
		callback(spineAssetPath, spineAsset)
	end)
end

function SpineManager:loadBattleEffectAsync(path, parent, callback)
	ResourceManager:loadAsync(path, TYPE_OF_GAME_OBJECT, function(assetPath, prefab)
		if parent and parent:isDestroyed() then
			ResourceManager:destroyPrefab(prefab)
			ResourceManager:unload(assetPath)
			return
		end
		local spineObject = CharacterSpineObject:create()
		spineObject:initWithPrefab(assetPath, prefab)
		spineObject:addUnloadCallback(function(obj)
			local effectPath = obj:getAssetPath()
			if self.battleCacheMap[effectPath] then
				ResourceManager:unload(effectPath)
			else
				if #self.battleCacheList == BATTLE_CACHE_SIZE then
					local headPath = table.remove(self.battleCacheList, 1)
					ResourceManager:unload(headPath)
					self.battleCacheMap[headPath] = nil
				end
				self.battleCacheMap[effectPath] = true
				table.insert(self.battleCacheList, effectPath)
			end
		end)
		if parent then
			spineObject:setParent(parent, false)
		end
		if callback then
			callback(spineObject)
		end
	end)
end

---- mesh renderer
function SpineManager:loadMeshSpineAsync(name, parent, callback)
	local path = string.format(MESH_SPINE_ASSET_PATH, name, name)
	ResourceManager:loadOriginAssetAsync(path, TYPE_OF_SPINE_ASSET, function(spineAssetPath, spineAsset)
		if parent and parent:isDestroyed() then
			ResourceManager:unload(spineAssetPath)
			return
		end
		self:loadMeshSpinePrefabAsync(parent, spineAssetPath, spineAsset, callback)
	end)
end

function SpineManager:loadMeshSpinePrefabAsync(parent, spineAssetPath, spineAsset, callback)
	ResourceManager:loadAsync(MESH_SPINE_PREFAB_PATH, TYPE_OF_GAME_OBJECT, function(prefabPath, prefab)
		if parent and parent:isDestroyed() then
			ResourceManager:destroyPrefab(prefab)
			ResourceManager:unload(spineAssetPath)
			ResourceManager:unload(prefabPath)
			return
		end
		local meshSpineObject = MeshSpineObject:create()
		meshSpineObject:initWithPrefab(MESH_SPINE_PREFAB_PATH, prefab)
		meshSpineObject:getAnimationState()
		meshSpineObject:initSkeletonDataAsset(spineAsset)
		meshSpineObject:addUnloadCallback(function(obj)
			ResourceManager:unload(spineAssetPath)
			ResourceManager:unload(prefabPath)
		end)
		if parent then
			meshSpineObject:setParent(parent, false)
		end
		if callback then
			callback(meshSpineObject)
		end
	end)
end

return SpineManager