-- 部分函数和变量名前加了下划线主要是避免被子类不经意间重写，同时也不应该直接调用这些属性或者方法

local BF_UI_HELPER = GConst.TYPEOF_UNITY_CLASS.BF_UI_HELPER
local BF_BASE_SORTING_ORDER_HELPER = GConst.TYPEOF_UNITY_CLASS.BF_BASE_SORTING_ORDER_HELPER
local BaseUI = {
	_baseAlreadyClosed = false,
	_baseUIOrder = 0
}

function BaseUI:ctor()
end

-- 需要在打开界面之前预先加载的资源列表,例如
-- {
-- 	[GConst.TYPEOF_UNITY_CLASS.GAME_OBJECT] = {
-- 		"xxx1", "xxx2"
-- 	}
-- }
function BaseUI:getPreLoadList()
	return nil
end

function BaseUI:showCommonBG()
	return false
end

-- 界面prefab路径
function BaseUI:getPrefabPath()
	return ""
end

-- 关闭界面
function BaseUI:closeUI()
	UIManager:closeUI(self)
end

function BaseUI:addLoadUICompleteListener(callback)
	if self._baseLoadComplete then
		if callback then
			callback()
		end
	else
		self._onLoadUICompleteCallback = callback
	end
end

function BaseUI:getIsLoadedComplete()
	return self._baseLoadComplete
end

function BaseUI:addEnterAniCompleteListener(callback)
	self._onEnterAniCompleteCallback = callback
end

function BaseUI:addExitAniCompleteListener(callback)
	self._onExitAniCompleteCallback = callback
end

-- 界面进入动画播放完
function BaseUI:onEnterAnimationComplete()
end

-- 界面退出动画播放完
function BaseUI:onExitAnimationComplete()
end

-- 界面的prefab加载完成后被调用
function BaseUI:onLoadRootComplete()
end

-- 当UI对象被创建时调用，注意此时还没加载好ui预制件
function BaseUI:onCreate()
end

-- 当此界面关闭时调用
function BaseUI:onClose()
end

-- 当此界面之上的其他界面都关闭，导致此界面处于最顶层时调用，第一次创建此界面的时候不会调用
-- 与onCover配对
function BaseUI:onReshow()
end

-- 当此界面处于最顶层的时候打开一个新的界面添加到此界面之上时调用
-- 与onReshow配对
function BaseUI:onCover()
end

-- 当此界面需要刷新时,界面刷新逻辑都在这里面处理
function BaseUI:onRefresh()
end

-- 当界面隐藏或从隐藏状态恢复显示时
function BaseUI:onUIVisibleChanged()
end

-- 当界面上的模型激活状态改变时
function BaseUI:onUIModelActiveChanged(active)
end

-- 当界面层级被重新设置时
function BaseUI:onSetUIOrder()

end

-- 此界面是否已经关闭了
function BaseUI:isClosed()
	return self._baseAlreadyClosed
end

-- 此界面是否被隐藏了，只有当被全屏界面挡住才会隐藏当前界面
function BaseUI:isVisible()
	return self._baseVisible ~= false
end

-- 此界面是不是当前最上层的
function BaseUI:isTopUI()
	return UIManager:getTopUIObj() == self
end

-- 响应安卓后退事件
function BaseUI:onPressBackspace()
end

-- 添加粒子之类的特效
function BaseUI:addEffect(effect, parent, order)
	if self:isClosed() then
		return
	end
	if self._baseEffectList == nil then
		self._baseEffectList = {}
	end
	self._baseEffectList[effect] = 1

	if self._baseUIHelper == nil then
		self._baseUIHelper = self.root:getGameObject():AddComponent(BF_UI_HELPER)
	end
	self._baseUIHelper:AddEffect(effect:getComponent(BF_BASE_SORTING_ORDER_HELPER), self._baseUIOrder, order)
	if parent then
		effect:setParent(parent, false)
	end
end

-- 对已经存在的子节点层级排序
function BaseUI:sortChildOrder(childObj, order)
	if self._baseUIHelper == nil then
		self._baseUIHelper = self.root:getGameObject():AddComponent(BF_UI_HELPER)
	end
	self._baseUIHelper:AddEffect(childObj:getComponent(BF_BASE_SORTING_ORDER_HELPER), self._baseUIOrder, order)
end

-- 移除粒子之类的特效
function BaseUI:removeEffect(effect, isNeedCleanUp)
	if self._baseEffectList then
		local effectNode = self._baseEffectList[effect]
		if effectNode then
			self._baseEffectList[effect] = nil
			if self._baseUIHelper then
				self._baseUIHelper:RemoveEffect(effect:getComponent(BF_BASE_SORTING_ORDER_HELPER))
			end
			effect:removeFromParent(isNeedCleanUp)
		end
	end
end

-- 界面的自定义事件添加监听
function BaseUI:addEventListener(key, func, priority)
	if self:isClosed() then
		return
	end
	local tag = EventManager:addEventListener(key, func, priority)
	self._baseEventListeners = self._baseEventListeners or {}
	self._baseEventListeners[key] = tag
end

-- 界面的自定义事件移除监听
function BaseUI:removeEventListener(key)
	if self._baseEventListeners and self._baseEventListeners[key] then
		EventManager:removeEventListener(key, self._baseEventListeners[key])
		self._baseEventListeners[key] = nil
	end
end

-- 请查看UIManager.UI_TYPE的说明
function BaseUI:getUIType()
	return UIManager.UI_TYPE.DEFAULT
end

-- 获取界面索引，其实就是界面lua的路径
function BaseUI:getUIIndex()
	return self._baseUIIndex
end

-- 此界面配套的BGM
function BaseUI:getBGMId()
	return nil
end

function BaseUI:setUIIndex(index)
	self._baseUIIndex = index
end

function BaseUI:setUIOrder(siblingIndex, order)
	self._baseSiblingIndex = siblingIndex
	self._baseUIOrder = order
	if self.root then
		self.root:getTransform():SetSiblingIndex(self._baseSiblingIndex)
		self._baseRootCanvas.sortingOrder = order
		if self._baseUIHelper then
			self._baseUIHelper:SetSortingOrder(order)
			self:onSetUIOrder()
		end
	end
end

function BaseUI:getUIOrder()
	return self._baseUIOrder
end

function BaseUI:getSiblingIndex()
	return self._baseSiblingIndex
end

function BaseUI:getIsShowComplete()
	return self._showComplete
end

function BaseUI:isFullScreen()
	return true
end

function BaseUI:getCurrencyParams()
	return nil, false
end

function BaseUI:updateCurrencyBar()
	UIManager:showCurrencyBar(self)
end

function BaseUI:swallowTouchBetweenUI()
	return true
end

-- 加载需要显示在UI上的模型，统一接口方便管理
function BaseUI:loadUIModelRoot(path, callback)
	ModelManager:loadModelAsync(path, nil, function(roleShowModel)
		if self:isClosed() then
			roleShowModel:destroy()
			return
		end
		self._baseShowModelList = self._baseShowModelList or {}
		table.insert(self._baseShowModelList, roleShowModel)
		callback(roleShowModel)
		if self._baseVisible == false then
			roleShowModel:setActive(false)
			self:onUIModelActiveChanged(false)
		end
	end)
end

function BaseUI:bind(data, fieldName, bindFunc, immediately)
	if self:isClosed() then
		return
	end
	if not self._baseBindData then
		self._baseBindData = {}
	end
	if not self._baseBindData[data] then
		self._baseBindData[data] = {}
	end
	table.insert(self._baseBindData[data], fieldName)
	data:bind(fieldName, self, bindFunc, immediately)
end

function BaseUI:unBind(data, fieldName)
	if self._baseBindData and self._baseBindData[data] then
		for i, field in ipairs(self._baseBindData[data]) do
			if field == fieldName then
				data:unBind(field, self)
				table.remove(self._baseBindData[data], i)
				break
			end
		end
	end
end

function BaseUI:unBindAll()
	if not self._baseBindData then
		return
	end

	for data, fields in pairs(self._baseBindData) do
		for _, field in ipairs(fields) do
			data:unBind(field, self)
		end
	end
	self._baseBindData = nil
end

function BaseUI:scheduleGlobal(func, inter)
	if self:isClosed() then
		return 0
	end
	local sid = SchedulerManager:scheduleGlobal(func, inter)
	if self._schedulerIds == nil then
		self._schedulerIds = {}
	end
	table.insert(self._schedulerIds, sid)
	return sid
end

function BaseUI:performWithDelayGlobal(func, delay)
	if self:isClosed() then
		return 0
	end
	local sid = SchedulerManager:performWithDelayGlobal(func, delay)
	if self._schedulerIds == nil then
		self._schedulerIds = {}
	end
	table.insert(self._schedulerIds, sid)
	return sid
end

function BaseUI:pauseScheduleGlobal(sid)
	SchedulerManager:pause(sid)
end

function BaseUI:resumeScheduleGlobal(sid)
	SchedulerManager:resume(sid)
end

function BaseUI:unscheduleGlobal(sid)
	if self._schedulerIds == nil then
		return
	end
	for k, v in ipairs(self._schedulerIds) do
		if v == sid then
			table.remove(self._schedulerIds, k)
			break
		end
	end
	SchedulerManager:unscheduleGlobal(sid)
end

function BaseUI:unloadPreloadList()
	if self._preloadListFinished then
		if self._preloadList then
			for objType, list in pairs(self._preloadList) do
				for _, path in ipairs(list) do
					ResourceManager:unload(path)
				end
			end
			self._preloadList = nil
		end
	end
end

function BaseUI:disableUITouch()
	if not self._baseTouchDisabled and not self:isClosed() then
		self._baseTouchDisabled = true
		UIManager:disableTouch()
	end
end

function BaseUI:enableUITouch()
	if self._baseTouchDisabled then
		self._baseTouchDisabled = false
		UIManager:enableTouch()
	end
end

-- 以下都是private function,不要在外部直接调用
function BaseUI:_onCreate()
	self:_loadRoot()
	self:onCreate()
end

-- 异步加载界面prefab文件，每个界面只能有一个root prefab
function BaseUI:_loadRoot()
	if self.root then
		return
	end

	local totalCount = 0
	local function finishCallback()
		totalCount = totalCount - 1
		if totalCount <= 0 then
			self._preloadListFinished = true
			if self:isClosed() then
				self:unloadPreloadList()
				self:_killAniTween()
				return
			end
			UIManager:getUIPrefab(self:getPrefabPath(), function(root)
				if self:isClosed() then
					self:unloadPreloadList()
					UIManager:putbackUIPrefab(root)
					return
				end
				self._baseUIHelper = root:getComponent(BF_UI_HELPER)
				self.root = root
				self:_fitNotchScreen()
				self:_onLoadRootComplete()
				-- 个别特殊的界面可能会在init或者refresh的时候关闭自己
				if self:isClosed() then
					self:_onEnterAnimationComplete()
					return
				end
				self:_playEnterAnimation()
				if self._baseUICovered then
					self:onCover()
				end
			end)
		end
	end

	self._preloadList = self:getPreLoadList()
	if self._preloadList then
		for objType, list in pairs(self._preloadList) do
			totalCount = totalCount + #list
		end
		for objType, list in pairs(self._preloadList) do
			for _, path in ipairs(list) do
				ResourceManager:loadOriginAssetAsync(path, objType, finishCallback)
			end
		end
	else
		totalCount = totalCount + 1
		finishCallback()
	end
end


function BaseUI:_recordParams(params)
	self._baseUIParams = params
end

function BaseUI:_getRecordParams()
	return self._baseUIParams
end

-- 显示或者隐藏界面,只是给UIManager用来隐藏被遮挡的界面减少渲染压力用的
function BaseUI:_setVisible(visible)
	if self._baseVisible == visible then
		return
	end
	self._baseVisible = visible
	if self._baseRootCanvas then
		local pos = visible and 0 or 10000000000
		self.root:setLocalPosition(0, 0, pos)
	end
	if self._baseShowModelList then
		for k, v in ipairs(self._baseShowModelList) do
			v:setActive(visible)
		end
		self:onUIModelActiveChanged(visible)
	end
	self:onUIVisibleChanged()
end

function BaseUI:_getRootCanvas()
	self._baseRootCanvas = self.root:getComponent(GConst.TYPEOF_UNITY_CLASS.CANVAS)
	if not self._baseRootCanvas then
		Logger.logTodo("ui root canvas is not add on prefab:%s", self.root:getAssetPath())
		self._baseRootCanvas = self.root:addComponent(GConst.TYPEOF_UNITY_CLASS.CANVAS)
	end
	return self._baseRootCanvas
end

function BaseUI:_onLoadRootComplete()
	self:_getRootCanvas()
	self._baseRootCanvas.overrideSorting = true
	if not self.root:getComponent(GConst.TYPEOF_UNITY_CLASS.GRAPHIC_RAYCASTER) then
		Logger.logTodo("ui root GraphicRaycaster is not add on prefab:%s", self.root:getAssetPath())
		self.root:addComponent(GConst.TYPEOF_UNITY_CLASS.GRAPHIC_RAYCASTER)
	end
	if self._baseVisible == false then
		self.root:setLocalPosition(0, 0, 10000000000)
	else
		self.root:setLocalPosition(0, 0, 0)
	end
	if self._baseSiblingIndex then
		self.root:getTransform():SetSiblingIndex(self._baseSiblingIndex)
		self._baseRootCanvas.sortingOrder = self._baseUIOrder
		if self._baseUIHelper then
			self._baseUIHelper:SetSortingOrder(self._baseUIOrder)
		end
	end

	UIManager:showCurrencyBar(self)

	self:onLoadRootComplete()
	self:onRefresh()
	self._baseLoadComplete = true
	UIManager:onUILoadedComplete(self)
	if self._onLoadUICompleteCallback then
		self._onLoadUICompleteCallback()
		self._onLoadUICompleteCallback = nil
	end
end

-- 适配异形屏
function BaseUI:_fitNotchScreen()
	-- 只有绑定了该组件的UI 才需要适配
	if self._baseUIHelper then
		local notchScreenNodeCount = self._baseUIHelper:GetNotchScreenNodeCount()
		if notchScreenNodeCount > 0 and not self._baseUIHelper:GetHasInitDefaultNotchScreenHeight() then
			-- 获取偏移值
			local height = SafeAreaManager:getNotchScreenHeight()
			if height < 1 then -- 没有刘海
				return
			end
			local index = 0
			for i = 1, notchScreenNodeCount do
				index = i - 1
				local haveNode = self._baseUIHelper:GetIsHaveNotchScreenNodeGameObject(index)
				local adjustHeight = self._baseUIHelper:GetNotchScreenNodeAdjustHeight(index)
				if haveNode then
					-- 获取anchor数据 根据情况处理位置
					self._baseUIHelper:CacheAnchorMin(index)
					local anchorMinX = self._baseUIHelper.PositionX
					local anchorMinY = self._baseUIHelper.PositionY

					self._baseUIHelper:CacheAnchorMax(index)
					local anchorMaxX = self._baseUIHelper.PositionX
					local anchorMaxY = self._baseUIHelper.PositionY

					if math.abs(anchorMinX - 0.5) < 0.00001 and math.abs(anchorMinY) < 0.00001 and math.abs(anchorMaxX - 0.5) < 0.00001 and math.abs(anchorMaxY - 1) < 0.00001 then -- 上下填充，仅调整高度
						self._baseUIHelper:CacheOffsetMax(index)
						local offsetMaxX = self._baseUIHelper.PositionX
						local offsetMaxY = self._baseUIHelper.PositionY
						self._baseUIHelper:SetOffsetMax(index, offsetMaxX, offsetMaxY - height + adjustHeight)

					elseif math.abs(anchorMinX) < 0.00001 and math.abs(anchorMinY) < 0.00001 and math.abs(anchorMaxX - 1) < 0.00001 and math.abs(anchorMaxY - 1) < 0.00001 then -- 整体填充，仅调整高度
						self._baseUIHelper:CacheOffsetMax(index)
						local offsetMaxX = self._baseUIHelper.PositionX
						local offsetMaxY = self._baseUIHelper.PositionY
						self._baseUIHelper:SetOffsetMax(index, offsetMaxX, offsetMaxY - height + adjustHeight)
					else -- 直接下移
						self._baseUIHelper:CacheAnchoredPosition(index)
						local notchScreenNodeOriginPosX = self._baseUIHelper.PositionX
						local notchScreenNodeOriginPosY = self._baseUIHelper.PositionY
						self._baseUIHelper:SetAnchoredPosition(index, notchScreenNodeOriginPosX, notchScreenNodeOriginPosY - height + adjustHeight)
					end
				end
			end
			self._baseUIHelper:SetInit(true)
		end
	end
end

function BaseUI:_playEnterAnimation()
	local aniType = UIManager:getAniType(self)
	if aniType then
		if aniType == UIManager.ANI_TYPE.POP then
			self.root:setAnchoredPosition(0, 0)
			self:_playPop()
		else
			UIManager:_updateUISwallowOrder()
			self.root:setAnchoredPosition(0, 0)
			self:_onEnterAnimationComplete()
		end
	else
		self.root:setAnchoredPosition(0, 0)
		self:_onEnterAnimationComplete()
	end
end

function BaseUI:_playExitAnimation()
	self:_onExitAnimationComplete()
end

function BaseUI:_onEnterAnimationComplete()
	self._showComplete = true
	if self:getUIType() == UIManager.UI_TYPE.MAIN then
		UIManager:closeBehindUI(self)
	end

	if self:isFullScreen() then
		UIManager:hideBehindUI(self)
	end

	if self._onEnterAniCompleteCallback then
		self._onEnterAniCompleteCallback()
	end
	self:onEnterAnimationComplete()

	-- 部分ui会有动画,所以在这里算完成
	EventManager:dispatchEvent(EventManager.CUSTOM_EVENT.UI_SHOW_COMPLETE, self:getUIIndex())
end

function BaseUI:_onExitAnimationComplete()
	self:onExitAnimationComplete()
	if self._onExitAniCompleteCallback then
		self._onExitAniCompleteCallback()
	end
	self:_destroy()
	UIManager:onUIClosedComplete(self)
end

function BaseUI:_playPop()
	self.popSequence = self.root:createBindTweenSequence()

	local scaleTween1 = self.root:getTransform():DOScale(1.05, 0.15)
	self.popSequence:Append(scaleTween1)

	local scaleTween2 = self.root:getTransform():DOScale(1, 0.2)
	self.popSequence:Append(scaleTween2)

	self.popSequence:OnComplete(function()
		UIManager:_updateUISwallowOrder()
		self:_onEnterAnimationComplete()
		self.popSequence = nil
	end)
end

function BaseUI:_killAniTween()
	if self.popSequence then
		self.popSequence:Kill(true)
		self.popSequence = nil
	end
end

function BaseUI:_onCover()
	self._baseUICovered = true
	if self.root then
		self:onCover()
	end
end

function BaseUI:_onReshow()
	if self._baseVisible == false then
		UIManager:updateBarsState(self)
		if self:isFullScreen() then
			UIManager:hideBehindUI(self)
		else
			UIManager:showBehindUI(self)
		end
		self:_setVisible(true)
	end
	self._baseUICovered = false
	if self.root then
		self:onReshow()
	end
end

function BaseUI:_onClose()
	EventManager:dispatchEvent(EventManager.CUSTOM_EVENT.UI_CLOSE, self:getUIIndex())
	self._baseAlreadyClosed = true
	self:onClose()
	self:_baseClear()
end

function BaseUI:_baseClear()
	if self._baseEventListeners then
		for key, tag in pairs(self._baseEventListeners) do
			EventManager:removeEventListener(key, tag)
		end
		self._baseEventListeners = nil
	end
	self._baseEffectList = nil

	if self._schedulerIds then
		for k, v in ipairs(self._schedulerIds) do
			SchedulerManager:unscheduleGlobal(v)
		end
		self._schedulerIds = nil
	end
	self:unBindAll()
	self:enableUITouch()
end

function BaseUI:_destroy()
	if self.root then
		-- 干掉界面上的模型
		if self._baseShowModelList then
			for i = 1, #self._baseShowModelList do
				table.remove(self._baseShowModelList):destroy()
			end
		end
		UIManager:putbackUIPrefab(self.root)
		self:_killAniTween()
		self.root = nil
		self._baseRootCanvas = nil
	end
	self:unloadPreloadList()
end

return BaseUI