<?php

/**
 * ItemLogic.php
 * 道具逻辑
 * Created On 2022/1/21
 */

namespace Game\Logic;
use Framework\Lib\Utils;
use Framework\Log\LogMark;
use Framework\MVC\ModelManager;
use Game\Constant\ClientErrorCode;
use Game\Constant\ConstTemplate\TemplateCar;
use Game\Constant\ConstTemplate\TemplateConst;
use Game\Constant\ConstTemplate\TemplateGiftPack;
use Game\Constant\ConstTemplate\TemplateItem;
use Game\Constant\ConstTemplate\TemplateSkinAttribution;
use Game\Constant\ConstTemplate\TemplateTaskExchange;
use Game\Constant\GameConstantDefine;
use Game\Constant\GameErrorCode;
use Game\Constant\ModelTypeDefine;
use Game\Constant\TemplateDefine;
use Framework\Logic\TemplateHelp;
use Game\Data\CarExteriorRefitData;
use Game\Data\ItemData;
use Game\Method\Mail\ItemMail;
use Game\Method\Money\ItemMoney;
use Game\Method\Player\PlayerBuffLogic;
use Game\Model\Car\CarExteriorRefitModel;
use Game\Model\ItemModel;
use Game\Model\MoneyModel;
use Game\Model\Rank\RankModel;
use Game\Protobuf\ConvertItem;
use Tool\LoadTemplate\Util;

trait ItemLogic
{
    use TemplateHelp;
    use EventConditionLogic;
    use PlayerBuffLogic;
    use ItemMail;
    use MoneyLogic;
    use ItemMoney;

    // 通过tplID获取玩家此模板道具拥有数量
    public function getItemNumByTplID(int $tplID):int {
        /**
         * @var ItemModel $itemModel
         */
        $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);
        $itemObjArr = $itemModel->getItemDataByTplID($tplID);
        if(empty($itemObjArr)) {
            return 0;
        }
        $num = 0;
        foreach($itemObjArr as $item) {
            $num += $item->num;
        }
        return $num;
    }

    // 使用道具
    public function useItemByUIDArr(array $itemArr): int
    {
        $code = ClientErrorCode::CLIENT_SUCCESS;
        $this->setLogConsumeItemSource(GameConstantDefine::ITEM_CONSUME_SOURCE_USE_ITEM);
        foreach($itemArr as $uid => $num) {
            if(!$this->useItemByUID($uid, $num,$code)) {
                LogMark::getInstance()->markError(
                    GameErrorCode::DATA_UPDATE_ERROR,
                    '[ItemLogic] use item by uid arr error!',
                    ['uid'=>$uid, 'num'=>$num]
                );
                return $code;
            }
        }
        /**
         * @var ItemModel $itemModel
         */
        $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);
        $itemModel->updateItemPacket();
        return $code;
    }

    /**
     * 获取单个限时道具
     * @param int $tplID
     * @param int $itemNum
     * @param int $expireTime 0： 永久 unix: 到期时间
     * @param int $oldItemId
     * @param bool $isShow
     * @return bool
     */
    public function gainItemExpire(int $tplID, int $itemNum,int $expireTime, $oldItemId = 0, bool &$isShow = true): bool
    {
        $itemCfg = $this->getTitle(TemplateDefine::TYPE_ITEM, $tplID);
        if(is_null($itemCfg)) {
            LogMark::getInstance()->markError(
                GameErrorCode::SEARCH_CONFIG_INFO_FAILED,
                '[ItemLogic] get item config error!'
            );
            return false;
        }
        if (array_key_exists($itemCfg[TemplateItem::ItemType], TemplateItem::gainFuncName)) {
            $funcName = TemplateItem::gainFuncName[$itemCfg[TemplateItem::ItemType]];
            if (!$this->$funcName($tplID, $itemNum, $expireTime, $oldItemId, $isShow)) {
                LogMark::getInstance()->markError(
                    GameErrorCode::ITEM_SAVE_FAILED,
                    '[ItemLogic] add item too funcName(gain) error!',
                    ['funcName' => $funcName, 'tplID' => $tplID, 'itemNum' => $itemNum, 'expire' => $expireTime]
                );
                return false;
            }
            return true;
        }

        if($itemCfg[TemplateItem::ItemType] == TemplateItem::ITEM_TYPE_CAR_REFIT_UNIVERSAL) {
            if (!$this->ItemAddCarRefitUniversal($itemCfg, $itemNum)) {
                return true;
            }
        }

        /**
         * @var ItemModel $itemModel
         */
        $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);
        $itemData = $itemModel->getItemDataByTplID($tplID);

        if(empty($itemData)) {
            // TODO: 可堆叠 或者 数量为 1
            if($itemCfg[TemplateItem::IsStackabble] || $itemNum == 1) {
                if(!$this->createItem($tplID, $itemNum, $itemCfg, $expireTime)) {
                    LogMark::getInstance()->markError(
                        GameErrorCode::DATA_CREATE_ERROR,
                        '[ItemLogic] create item error!',
                        ['tplID'=>$tplID, 'num'=>$itemNum]
                    );
                    return false;
                }
                return true;
            }
        }
        // 可堆叠
        if($itemCfg[TemplateItem::IsStackabble]) {
            $item = $this->searchCanAdd($itemData, $expireTime);
            if(!is_null($item)) {
                if(!$this->ItemAdd($itemCfg, $item, $itemNum)) {
                    LogMark::getInstance()->markError(
                        GameErrorCode::DATA_UPDATE_ERROR,
                        '[ItemLogic] add item error!',
                        (array)$item
                    );
                    return false;
                }
            } else {
                if(!$itemModel->createItem($tplID, $itemNum, $expireTime)) {
                    LogMark::getInstance()->markError(
                        GameErrorCode::DATA_CREATE_ERROR,
                        '[ItemLogic] create item error!',
                        ['tplID'=>$tplID, 'num'=>$itemNum, 'expire'=>$expireTime]
                    );
                    return false;
                }
            }
            return true;
        }
        for($i = 0; $i < $itemNum; $i++) {
            if(!$itemModel->createItem($tplID, 1, $expireTime)) {
                LogMark::getInstance()->markError(
                    GameErrorCode::DATA_CREATE_ERROR,
                    '[ItemLogic] for create item error!',
                    ['tplID'=>$tplID, 'num'=>1, 'expire'=>$expireTime]
                );
                return false;
            }
        }
        return true;
    }

    private function itemAddCar(int $tplID, int $itemNum,int $expire, int $oldItemID = 0, bool &$isShow = true): bool {
        /**
         * @var ItemModel $itemModel
         */
        $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);

        $itemCfg = $this->getTitle(TemplateDefine::TYPE_ITEM, $tplID);
        $carID = $itemCfg[TemplateItem::UseId][0];
        $expire = $itemCfg[TemplateItem::UseId][1];
        $carCfg = $this->getTitle(TemplateDefine::TYPE_CAR, $carID);
        if($carCfg == null) {
            LogMark::getInstance()->markError(
                GameErrorCode::SEARCH_CONFIG_INFO_FAILED,
                '[ItemLogic] add item car get config info error!',
                ['carID'=>$carID]
            );
            return false;
        }
        for($i = 0; $i < $itemNum; $i++) {
            if(!$itemModel->itemGainCarByCarID($carID, 1, $expire, $itemCfg[TemplateItem::ItemId])) {
                LogMark::getInstance()->markError(
                    GameErrorCode::DATA_CREATE_ERROR,
                    '[ItemLogic] add item get car error!',
                    ['carID'=>$carID]
                );
                return false;
            }
        }
        return true;
    }

    private function itemAddClothing(int $tplID, int $itemNum, int $expire, int $oldItemID, bool &$isShow): bool
    {
        /**
         * @var ItemModel $itemModel
         */
        $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);
        // 套装处理
        if($this->execCloths($tplID, $itemNum, $expire, $oldItemID, $isShow)) {
            return true;
        }
        $itemData = $itemModel->getItemDataByTplID($tplID);
        if(empty($itemData)) {
            if(!$itemModel->createItem($tplID, 1, $expire)) {
                LogMark::getInstance()->markError(
                    GameErrorCode::DATA_CREATE_ERROR,
                    '[ItemLogic] create itemAddClothing error!',
                    ['tplID'=>$tplID, 'expire'=>$expire]
                );
                return false;
            }

            if(empty($expire)) {
                // 服装
                $itemCfg = $this->getTitle(TemplateDefine::TYPE_ITEM, $tplID);

                if($itemCfg[TemplateItem::ItemType] == TemplateItem::ITEM_TYPE_CLOTH) {
                    $rankType = RankModel::CLOTH;
                    $score = $itemCfg[TemplateItem::ScoreNum];
                    if($score === 0) {
                        return true;
                    }
                    $newScore = $this->changeRankingEventPooled($rankType, $score);
                    /**
                     * @var RankModel $rankModel
                     */
                    $rankModel = ModelManager::getInstance()->getModel(ModelTypeDefine::RANKING);
                    if(!$rankModel->changeRankScore($rankType, $newScore)) {
                        LogMark::getInstance()->markError(
                            GameErrorCode::DATA_UPDATE_ERROR,
                            '[ItemLogic] update rank data [item cloth] error!',
                            ['rankType' => $rankType, 'now score' => $score]
                        );
                    }
                }
            }

            // 进行折算
            if($itemNum > 1) {
                if(!$this->Exchange($oldItemID, $itemNum - 1)) {
                    LogMark::getInstance()->markError(
                        GameErrorCode::DATA_CREATE_ERROR,
                        '[ItemLogic] itemAddClothing exchange item error!'
                    );
                    return false;
                }
            }
            return true;
        }

        $item = $itemData[0];

        if($item->expire == ItemModel::TIME_NORMAL) {
            $isShow = false;
            if($oldItemID == 0) {
                return true;
            }
            if(!$this->Exchange($oldItemID, $itemNum)) {
                LogMark::getInstance()->markError(
                    GameErrorCode::ITEM_SUB_FAILED,
                    '[ItemLogic] exchange itemAddClothing error!'
                );
                return false;
            }
            return true;
        }

        // 修改原有道具时间
        if(!$this->ExchangeChangeItem($item, $itemNum, $expire)) {
            LogMark::getInstance()->markError(
                GameErrorCode::ITEM_SAVE_FAILED,
                '[ItemLogic] exchange change itemAddClothing error!'
            );
            return false;
        }

        return true;
    }

    //道具添加改装件,添加前已判断是否拥有
    private function ItemAddCarRefitUniversal(array $itemCfg, int &$num): bool
    {
        if($itemCfg[TemplateItem::IsStackabble]) {
            return true;
        }
        // 判断是否有此道具
        $haveNum = $this->getItemNumByTplID($itemCfg[TemplateItem::ItemId]);
        if(!empty($haveNum)) {
            $this->Exchange($itemCfg[TemplateItem::ItemId], $num);
            return false;
        }
        // 判断是否已经拥有
        $skinCfg = $this->getTitle(TemplateDefine::TYPE_SKIN_ATTRIBUTION, $itemCfg[TemplateItem::ItemId]);
        if(is_null($skinCfg)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_SEARCH_ERROR,
                '[ItemLogic] item add car refit universal get skin attribution cfg error!',
            );
            return false;
        }
        $carID = $skinCfg[TemplateSkinAttribution::CarId];
        $refitID = $skinCfg[TemplateSkinAttribution::Individual];
        /**
         * @var CarExteriorRefitModel $refitModel
         */
        $refitModel = ModelManager::getInstance()->getModel(ModelTypeDefine::CAR_EXTERIOR_REFIT);
        $res = $refitModel->getERDataByRefitID($refitID);
        if(!empty($res)) {
            foreach($res as $key => $val) {
                /**
                 * @var CarExteriorRefitData $erData
                 */
                $erData = $refitModel->getExteriorRefitData($key);
                if($erData->carID == $carID) {
                    $this->Exchange($itemCfg[TemplateItem::ItemId], $num);
                    return false;
                }
            }
        }
        if($num > 1) {
            $this->Exchange($itemCfg[TemplateItem::ItemId], $num - 1);
        }
        // 折算
        $num = 1;
        return true;
    }

    // 获取套装
    public function execCloths(int $tplID, int $itemNum, int $expire, int $oldID,bool $isShow): bool {
        $itemCfg = $this->getTitle(TemplateDefine::TYPE_ITEM, $tplID);
        if($itemCfg[TemplateItem::ShowType] == TemplateItem::SHOW_TYPE_CLOTHING_SUIT) {
            foreach($itemCfg[TemplateItem::ClothList] as $k => $item) {
                $this->itemAddClothing($item, $itemNum, $expire, $oldID, $isShow);
            }
            return true;
        }
        return false;
    }

    // 获取单个道具
    public function gainItem(int $tplID, int $itemNum): bool
    {
        /**
         * @var MoneyModel $moneyModel
         */
        $moneyModel = ModelManager::getInstance()->getModel(ModelTypeDefine::MONEY);
        if(in_array($tplID, $moneyModel::TYPE_MONEY_MAP)) {
            if(!$this->addMoney($tplID, $itemNum)) {
                LogMark::getInstance()->markError(
                    GameErrorCode::DATA_UPDATE_ERROR,
                    '[ItemLogic] gain item money error!',
                    ['tplID'=>$tplID, 'itemNum'=>$itemNum]
                );
                return false;
            }
            return true;
        }
        if(!$this->gainItemLogic($tplID, $itemNum)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_CREATE_ERROR,
                '[ItemLogic] gain item error!',
                ['tplID'=>$tplID, 'itemNum'=>$itemNum]
            );
            return false;
        }

        /**
         * @var ItemModel $itemModel
         */
        $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);
        $itemModel->updateItemPacket();
        return true;
    }

    // 获取多个道具
    public function gainItemArr(array $itemArr): bool
    {
        $this->checkItemArrMaxNum($itemArr);
        foreach ($itemArr as $tplID => $num) {
            if(!$this->gainItemLogic($tplID, $num)) {
                LogMark::getInstance()->markError(
                    GameErrorCode::DATA_CREATE_ERROR,
                    '[ItemLogic] gain item error!',
                    ['tplID'=>$tplID, 'itemNum'=>$num]
                );
                return false;
            }
        }

        /**
         * @var ItemModel $itemModel
         */
        $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);
        $itemModel->updateItemPacket();
        return true;
    }

    public function checkItemArrMaxNum(array &$itemList) {
        $mailData = [];
        foreach ($itemList as $itemID => $itemNum) {
            $itemCfg = $this->getTitle(TemplateDefine::TYPE_ITEM, $itemID);
            $itemMax = $itemCfg[TemplateItem::MaxNumb];
            if($itemCfg[TemplateItem::ItemType] == TemplateItem::ITEM_TYPE_MONEY) {
                $num = $this->getMoneyNum($itemID);
            } else {
                $num = $this->getItemNumByTplID($itemID);
            }
            if(($num + $itemNum) > $itemMax) {
                $mailData[$itemID] = $num + $itemNum - $itemMax;
                $itemList[$itemID] = $itemMax - $num;
            }
        }
        if(!empty($mailData)) {
            //发送邮件
            $mailTplId = $this->getTerm(TemplateDefine::TYPE_CONST,
                TemplateConst::Const_Item_Max_Mail_ID,
                TemplateConst::ConstNum);
            $this->itemCreatePlayerMail($mailTplId, Utils::getServerTimestamp(), $mailData);
        }
    }

    public function checkGmItemArrMaxNum($itemID, &$itemNum) {
        $itemCfg = $this->getTitle(TemplateDefine::TYPE_ITEM, $itemID);
        $itemMax = $itemCfg[TemplateItem::MaxNumb];
        if($itemCfg[TemplateItem::ItemType] == TemplateItem::ITEM_TYPE_MONEY) {
            $num = $this->getMoneyNum($itemID);
        } else {
            $num = $this->getItemNumByTplID($itemID);
        }
        if(($num + $itemNum) > $itemMax) {
            $itemNum = 0;
            return ;
        }
    }

    //
    private function gainItemLogic(int $tplID, int $itemNum): bool {
        /**
         * @var MoneyModel $moneyModel
         */
        $moneyModel = ModelManager::getInstance()->getModel(ModelTypeDefine::MONEY);
        if(in_array($tplID, $moneyModel::TYPE_MONEY_MAP)) {
            if(!$this->addMoney($tplID, $itemNum)) {
                LogMark::getInstance()->markError(
                    GameErrorCode::DATA_UPDATE_ERROR,
                    '[ItemLogic] gain item money error!',
                    ['tplID'=>$tplID, 'itemNum'=>$itemNum]
                );
                return false;
            }
            return true;
        }
        $itemCfg = $this->getTitle(TemplateDefine::TYPE_ITEM, $tplID);
        if($itemCfg == null) {
            LogMark::getInstance()->markError(
                GameErrorCode::ITEM_SAVE_FAILED,
                '[ItemLogic] item id error!',
                ['tplID'=>$tplID]
            );
            return false;
        }
        $expireTime = $this->computeExpireTime($itemCfg);
        if(!$this->gainItemExpire($tplID, $itemNum, $expireTime)){
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_CREATE_ERROR,
                '[ItemLogic] gain item logic error!',
                ['tplID'=>$tplID, 'num'=>1, 'expire'=>$expireTime]
            );
            return false;
        }
        return true;
    }

    // GM发送普通道具
    public function gmGainItem(int $tplID, int $num): bool {
        $this->checkGmItemArrMaxNum($tplID, $num);
        if(!$this->gainItemLogic($tplID, $num)) {
            LogMark::getInstance()->markError(
                GameErrorCode::ITEM_SAVE_FAILED,
                '[ItemLogic] gm gain item error!',
                ['tplID'=>$tplID, 'num'=>$num]
            );
            return false;
        }
        /**
         * @var ItemModel $itemModel
         */
        $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);
        $itemModel->addPacketExec();
        return true;
    }

    // GM发送限时道具
    public function gmGainExpireItem(int $tplID, int $num, int $expire): bool {
        $this->checkGmItemArrMaxNum($tplID, $num);
        if(!$this->gainItemExpire($tplID, $num, $expire)) {
            LogMark::getInstance()->markError(
                GameErrorCode::ITEM_SAVE_FAILED,
                '[ItemLogic] gm gain expire item error!',
                ['tplID'=>$tplID, 'num'=>$num, 'expire'=>$expire]
            );
            return false;
        }
        /**
         * @var ItemModel $itemModel
         */
        $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);
        $itemModel->addPacketExec();
        return true;
    }

    // 创建一个没有的道具
    private function createItem(int $tplID, int $itemNum, $itemCfg, $expire = 0):bool
    {
        if(empty($expire)) {
            $expireTime = $this->computeExpireTime($itemCfg);
        } else {
            $expireTime = $expire;
        }
        /**
         * @var ItemModel $itemModel
         */
        $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);
        if(!$itemModel->createItem($tplID, $itemNum, $expireTime)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_CREATE_ERROR,
                '[ItemLogic] create item error!',
                ['tplID'=>$tplID, 'itemNum'=>$itemNum,
                    'expire'=>$expireTime]
            );
            return false;
        }

        if($expire !== 0) {
            return true;
        }

        // 车改装件
        if($itemCfg[TemplateItem::ItemType] == TemplateItem::ITEM_TYPE_CAR_REFIT) {
            $rankType = RankModel::CAR_REFIT;
            $score = $itemCfg[TemplateItem::ScoreNum];
            if($score === 0) {
                return true;
            }

            $newScore = $this->changeRankingEventPooled($rankType, $score);
            $rankModel = ModelManager::getInstance()->getModel(ModelTypeDefine::RANKING);
            if(!$rankModel->changeRankScore($rankType, $newScore)) {
                LogMark::getInstance()->markError(
                    GameErrorCode::DATA_UPDATE_ERROR,
                    '[ItemLogic] update rank data [item car refit] error!',
                    ['rankType' => $rankType, 'now score' => $score]
                );
            }

        }

        return true;
    }

    private function changeRankingEventPooled($type, $score,int $mapID = 0, $seasonID = 0): int
    {
        return $this->addEventConditionValue($this->getRankingKey($type, $seasonID, $mapID), $score);
    }

    private function computeExpireTime(array $itemCfg):int
    {
        $timeType = $itemCfg[TemplateItem::TimeLimitType];
        $timeLimit = $itemCfg[TemplateItem::TimeLimit];
        $expireTime = 0;
        if ($timeType == TemplateItem::TIME_LIMIT_TYPE_TIME_ADD) {
            $expireTime = Utils::getServerTimestamp() + $timeLimit;
        } elseif($timeType == TemplateItem::TIME_LIMIT_TYPE_ACTIVITY_END) {
            // TODO: 活动表未知， 此处后续应该替换为相应活动结束时间
            $activityData = explode("*", $timeLimit);
            $type = $activityData[0];        // 活动类型   1 兑换 2 目标 3 节日
            $activityID = $activityData[1];  // 活动ID
            $activityDate = $activityData[2] * 86400;  // 活动类型

            switch ($type) {
                case 1:
                    $cfg = $this->getTitle(TemplateDefine::TYPE_TASK_EXCHANGE, $activityID);
                    $expireTime = $cfg[TemplateTaskExchange::EndTime] + $activityDate;
                    break;
                case 2:
                    $cfg = $this->getTitle(TemplateDefine::TYPE_TASK_TARGET, $activityID);
                    $expireTime = $cfg[TemplateTaskExchange::EndTime] + $activityDate;
                    break;
                case 3:
                    $cfg = $this->getTitle(TemplateDefine::TYPE_TASK_FESTIVAL, $activityID);
                    $expireTime = $cfg[TemplateTaskExchange::EndTime] + $activityDate;
                    break;
            }
        }
        return $expireTime;
    }

    private function Exchange(int $tplID, int $itemNum):bool
    {
        $this->setLogAddItemSource(GameConstantDefine::ITEM_ADD_SOURCE_CONVERTED);
        $itemCfg = $this->getTitle(TemplateDefine::TYPE_ITEM, $tplID);

        if($itemCfg[TemplateItem::ConvertedId] != ItemModel::TYPE_CONVERTED_NORMAL) {

            if($itemCfg[TemplateItem::ConvertedCount] == 0) {
                return true;
            }
            if(!$this->gainItem($itemCfg[TemplateItem::ConvertedId], $itemCfg[TemplateItem::ConvertedCount]
                * $itemNum)) {
                LogMark::getInstance()->markError(
                    GameErrorCode::ITEM_SAVE_FAILED,
                    '[ItemLogic] item exchange item error!',
                    $itemCfg
                );
                return false;
            }
        }
        return true;
    }

    // 兑换道具之修改原道具信息
    public function ExchangeChangeItem(ItemData $item, int $itemNum, int $expire): bool {
        /**
         * @var ItemModel $itemModel
         */
        $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);
        if($item->expire == ItemModel::TIME_NORMAL) {
            return true;
        }
        if($expire == ItemModel::TIME_NORMAL) {
            $item->expire = ItemModel::TIME_NORMAL;
        } else {
            $time = $item->expire + ($expire - Utils::getServerTimestamp()) * $itemNum;
            $item->expire = $time;
        }
        if(!$item->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::ITEM_SAVE_FAILED,
                '[ItemLogic] item exchange change save item error!',
                (array)$item);
            return false;
        }

        $itemModel->addItemPacket($item->uid, $item->tplID, $item->expire, $item->num);
        return true;
    }

    private function ItemAdd(array $itemCfg,itemData $item,int $itemNum): bool
    {
        /**
         * @var ItemModel $itemModel
         * 只是数量累加 后续拓展时间
         */
        $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);
        $addType = $itemCfg[TemplateItem::TimeLimitType];
        switch ($addType)
        {
            case ItemModel::ADD_TYPE_NUM:
            case ItemModel::ADD_TYPE_ACTIVITY:
                if(!$itemModel->addItem($item, $itemNum)) {
                    LogMark::getInstance()->markError(
                        GameErrorCode::ITEM_SAVE_FAILED,
                        '[ItemLogic] add item error!',
                        (array)$item
                    );
                    return false;
                }
                break;
            case ItemModel::ADD_TYPE_TIME:
                $time = $this->computeExpireTime($itemCfg) * $itemNum + $item->expire;
                if(!$itemModel->updateItemExpireTime($item, $time)){
                    LogMark::getInstance()->markError(
                        GameErrorCode::DATA_UPDATE_ERROR,
                        '[ItemLogic] update item expire error!', (array)$item
                    );
                    return false;
                }
                break;
        }
        return true;
    }

    private function searchCanAdd(array $data, int $expireTime): ?itemData
    {
        foreach($data as $item) {
            if($item->expire == $expireTime) {
                return $item;
            }
        }
        return null;
    }

    // 通过tid扣除道具
    public function subItemByTID(int $tplID, int $num): bool
    {
        /**
         * @var MoneyModel $moneyModel
         */
        $moneyModel = ModelManager::getInstance()->getModel(ModelTypeDefine::MONEY);
        if(in_array($tplID, $moneyModel::TYPE_MONEY_MAP)) {
            if(!$this->subMoney($tplID, $num)) {
                LogMark::getInstance()->markError(
                    GameErrorCode::DATA_UPDATE_ERROR,
                    '[itemLogic] sub item money error!',
                    ['tplID'=>$tplID, 'itemNum'=>$num]
                );
                return false;
            }
            return true;
        }
        /**
         * @var ItemModel $itemModel
         */
        $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);
        $tplData = $itemModel->getItemDataByTplID($tplID);

        $itemList = $this->checkCanSubByTID($tplData, $num);
        if(empty($itemList)) {
            LogMark::getInstance()->markWarn(
                GameErrorCode::ITEM_SUB_FAILED,
                '[ItemLogic] sub item by tid error!'
            );
            return false;
        }
        foreach ( $itemList as $uid => $expire)
        {
            $item = $itemModel->getItemData($uid);
            if(is_null($item)) {
                LogMark::getInstance()->markError(
                    GameErrorCode::ITEM_SEARCH_INFO_FAILED,
                    '[ItemLogic] sub item by tid - search item error!'
                );
                return false;
            }
            $tmp_num = $num - $item->num;
            if($tmp_num >= 0 ){
                if(!$itemModel->delItemByUID($item)) {
                    LogMark::getInstance()->markError(
                        GameErrorCode::DATA_DELETE_ERROR,
                        '[ItemLogic] del item by uid error!', ['uid'=>$uid]
                    );
                    return false;
                }
            } else {
                if(!$itemModel->subItem($item, $num)) {
                    LogMark::getInstance()->markError(
                        GameErrorCode::ITEM_SUB_FAILED,
                        '[ItemLogic] item sub num error!',
                        ['uid'=>$uid, 'num'=>$num]
                    );
                    return false;
                }
            }
        }
        $itemModel->updateItemPacket();
        return true;
    }

    private function checkCanSubByTID(array $data, int $num):array {
        if(empty($data)) {
            return [];
        }
        $ret = array();
        $sum = 0;
        foreach($data as $item){
            $ret[$item->uid] = $item->expire;
            $sum += $item->num;
        }
        if($sum < $num) {
            return [];
        }
        asort($ret);
        return $ret;
    }

    private function useItemByUID(int $uid, int $num, int &$code): bool
    {
        /**
         * @var ItemModel $itemModel
         */
        $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);
        $item = $itemModel->getItemData($uid);
        if (is_null($item)) {
            LogMark::getInstance()->markError(
                GameErrorCode::ITEM_SEARCH_INFO_FAILED,
                '[ItemLogic] search item info error!',
                ['uid'=>$uid]
            );
            $code = ClientErrorCode::ITEM_NOT_FOUND;
            return false;
        }
        //检查有效期
        if ($item->expire != ItemModel::TIME_NORMAL && Utils::getServerTimestamp() >= $item->expire) {
            $code = ClientErrorCode::ITEM_TIME_EXPIRE;
            return false;
        }

        $itemCfg = $this->getTitle(TemplateDefine::TYPE_ITEM, $item->tplID);
        if(is_null($itemCfg) || $itemCfg[TemplateItem::UseType] == TemplateItem::USE_TYPE_NONE) {
            LogMark::getInstance()->markError(
                GameErrorCode::ITEM_VERIFY_CHECK_FAILED,
                '[ItemLogic] use item (search item cfg || item not use) error!',
                ['uid'=>$uid]
            );
            $code = ClientErrorCode::ITEM_IS_INVALID;
            return false;
        }

        if($item->num < $num) {
            LogMark::getInstance()->markError(
                GameErrorCode::ITEM_VERIFY_CHECK_FAILED,
                '[ItemLogic] use item num can not error!',
                ['num'=>$num]
            );
            $code = ClientErrorCode::ITEM_NOT_ENOUGH;
            return false;
        }

        if(!$this->subItemByUID($uid, $num)) {
            LogMark::getInstance()->markError(
                GameErrorCode::ITEM_SUB_FAILED,
                '[ItemLogic] use item sub item error!',
                ['uid'=>$uid, 'num'=>$num]
            );
            $code = ClientErrorCode::ITEM_NOT_ENOUGH;
            return false;
        }

        $funcName = TemplateItem::useFuncName[$itemCfg[TemplateItem::UseType]];

        // 使用返回显示
        if (!$this->$funcName($itemCfg, $num)) {
            LogMark::getInstance()->markError(
                GameErrorCode::ITEM_SAVE_FAILED,
                '[ItemLogic] use item too funcName(useType) error!',
                ['num'=>$num]
            );
            $code = ClientErrorCode::ITEM_USE_FAIL;
            return false;
        }
        $itemModel->sendUseItemPacket($item->tplID);
        return true;
    }

    /**
     * 通过道具打开礼包
     * @param array $itemCfg
     * @param int $num
     * @param bool $sendMail
     * @return bool
     */
    public function useItemOpenGift(array $itemCfg, int $num): bool {
        $giftID = $itemCfg[TemplateItem::UseId];
        $itemArr = $this->getGiftPackItemArr($giftID, $num);
        if(empty($itemArr)) {
            return true;
        }
        $this->setLogAddItemSource(GameConstantDefine::ITEM_ADD_SOURCE_OPEN_GIFT);
        if(!$this->gainItemArr($itemArr)) {
            return false;
        }
        /**
         * @var ItemModel $itemModel
         */
        $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);
        foreach($itemArr as $id => $n) {
            $itemModel->addUseItemPacket($id, $n);
        }
        return true;
    }

    /**
     * 返回开礼包的道具列表 [id=>num, id=>num ...]
     * @param int $giftID
     * @param int $num
     * @return array
     */
    private function getGiftPackItemArr(int $giftID, int $num): array {
        $items = [];
        $giftPackInfo = $this->getTitle(TemplateDefine::TYPE_GIFT_PACK, $giftID);
        if( $giftPackInfo == null ) {
            LogMark::getInstance()->markError(
                GameErrorCode::ITEM_SEARCH_INFO_FAILED,
                '[ItemLogic] use item open gift get cfg error!',
                ['giftID'=>$giftID]
            );
            return  [];
        }

        for($i = 0; $i < $num; $i++) {
            // 开一次礼包
            switch ($giftPackInfo[TemplateGiftPack::UseType]) {
                case TemplateGiftPack::USE_TYPE_1:
                    $giftArr = $giftPackInfo[TemplateGiftPack::Item];
                    $itemNum = $giftPackInfo[TemplateGiftPack::Numb];
                    // 配置错误 配置抽取数量 小于配置 道具数量
                    if(count($giftArr) < $itemNum) {
                        break;
                    }
                    for($k = 0; $k < $itemNum; $k ++) {
                        $index = $this->giftOpenIndex($giftArr);
                        isset($items[$giftArr[$index][Util::ID]]) ?
                            $items[$giftArr[$index][Util::ID]] +=  $giftArr[$index][Util::NUM]
                            : $items[$giftArr[$index][Util::ID]] =  $giftArr[$index][Util::NUM];
                        unset($giftArr[$index]);
                    }
                    break;
                case TemplateGiftPack::USE_TYPE_2:
                    foreach ($giftPackInfo[TemplateGiftPack::Item] as $item) {
                        isset($items[$item[Util::ID]]) ? $items[$item[Util::ID]] +=  $item[Util::NUM]
                            : $items[$item[Util::ID]] =  $item[Util::NUM];
                    }
                    break;
            }
        }
        return $items;
    }

    private  function giftOpenIndex(array &$items): int {
        $rateSum = 0;
        foreach ($items as $item) {
            $rateSum += $item[Util::RATE];
        }
        if(empty($items) || $rateSum === 0) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_SEARCH_ERROR,
                '[itemLogic] open gift get cfg error!'
            );
        }
        $randNum = random_int(1, $rateSum);
        $tmpNum = 0;
        $index = 0;
        foreach ($items as $k => $v) {
            $min = $tmpNum;
            $tmpNum += $v[Util::RATE];
            $max = $tmpNum;
            if($randNum > $min && $randNum <= $max) {
                $index = $k;
                return $index;
            }
        }
        return $index;
    }

    private function useItemExchange(array $pItemCfg, int $num): bool {
        $tplID = $pItemCfg[TemplateItem::UseId][0];
        $time = $pItemCfg[TemplateItem::UseId][1];
        if($pItemCfg[TemplateItem::UseId][1] != 0) {
            $time = $pItemCfg['UseId'][1] + Utils::getServerTimestamp();
        }
        if(!$this->gainItemExpire($tplID, $num, $time)) {
            LogMark::getInstance()->markError(
                GameErrorCode::ITEM_SAVE_FAILED,
                '[ItemLogic] use item exchange error!',
                ['tplID'=>$tplID, 'num'=>$num, 'expire'=>$time]
            );
            return false;
        }
        /**
         * @var ItemModel $itemModel
         */
        $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);
        $itemModel->addUseItemPacket($tplID, $num, $time);
        return true;
    }

    private function useItemClothing(array $pItemCfg, int $num): bool {
        $oldItemID = $pItemCfg[TemplateItem::ItemId];
        $tplID = $pItemCfg[TemplateItem::UseId][0];
        $time = $pItemCfg[TemplateItem::UseId][1];
        if($pItemCfg[TemplateItem::UseId][1] != 0) {
            if($pItemCfg[TemplateItem::IsStackabble]) {
                $time = $pItemCfg[TemplateItem::UseId][1] * $num + Utils::getServerTimestamp();
                $num = 1;
            } else {
                $time = $pItemCfg[TemplateItem::UseId][1] + Utils::getServerTimestamp();
            }
        }
        $itemCfg = $this->getTitle(TemplateDefine::TYPE_ITEM, $tplID);
        if($itemCfg == null) {
            LogMark::getInstance()->markError(
                GameErrorCode::ITEM_SEARCH_INFO_FAILED,
                '[ItemLogic] use item clothing get config info error!',
                ['tplID'=>$tplID]
            );
            return false;
        }
        $isShow = true;
        if(!$this->gainItemExpire($tplID, $num, $time, $oldItemID, $isShow)) {
            LogMark::getInstance()->markError(
                GameErrorCode::ITEM_SAVE_FAILED,
                '[ItemLogic] use item clothing add itemClothing error!'
            );
            return false;
        }
        if($isShow) {
            /**
             * @var ItemModel $itemModel
             */
            $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);
            $itemModel->addUseItemPacket($tplID, $num, $time);
        }
        return true;
    }

    private function useItemCarCard(array $pItemCfg, int $num): bool {
        $tplID = $pItemCfg[TemplateItem::UseId][0];
        $time = $pItemCfg[TemplateItem::UseId][1];
        if($pItemCfg[TemplateItem::UseId][1] != 0) {
            $time = $pItemCfg[TemplateItem::UseId][1]+ Utils::getServerTimestamp();
        }

        if(!$this->itemAddCar($pItemCfg[TemplateItem::ItemId], $num, $time)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_CREATE_ERROR,
                '[ItemLogic] use item get card error!',
                ['tplID'=>$tplID]
            );
            return false;
        }
        $carItemID = $this->getTitle(TemplateDefine::TYPE_CAR, $tplID)[TemplateCar::ItemId];
        /**
         * @var ItemModel $itemModel
         */
        $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);
        $itemModel->addUseItemPacket($carItemID, $num, $time);
        return true;
    }

    private function useItemActiveCar(array $pItemCfg, int $num): bool {
        $carID = $pItemCfg[TemplateItem::UseId][0];
        $expire = $pItemCfg[TemplateItem::UseId][1];
        $carCfg = $this->getTitle(TemplateDefine::TYPE_CAR, $carID);
        if($carCfg == null) {
            LogMark::getInstance()->markError(
                GameErrorCode::SEARCH_CONFIG_INFO_FAILED,
                '[ItemLogic] use item car get config info error!',
                ['carID'=>$carID]
            );
            return false;
        }
        if(!$this->itemGainCar($carID, 1, $expire, $pItemCfg['ItemId'])) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_CREATE_ERROR,
                '[ItemLogic] use item get car error!',
                ['carID'=>$carID]
            );
            return false;
        }
        return true;
    }

    //使用道具改变数值
    private function useItemUpdateValue($itemConfig, $num)
    {
        [$buffName, $time] = $itemConfig[TemplateItem::UseId];
        $addTime = $time * $num;
        return $this->addPlayerBuff($buffName, $addTime);
    }

    // 通过uid扣除道具
    private function subItemByUID(int $uid, int $itemNum): bool
    {
        /**
         * @var ItemModel $itemModel
         */
        $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);
        $item = $itemModel->getItemData($uid);

        if(is_null($item)) {
            LogMark::getInstance()->markError(
                GameErrorCode::ITEM_SEARCH_INFO_FAILED,
                '[ItemLogic] sub item search item obj error!'
            );
            return false;
        }

        $num = $item->num - $itemNum;
        if($num > 0) {
            if(!$itemModel->subItem($item, $itemNum)) {
                LogMark::getInstance()->markError(
                    GameErrorCode::ITEM_SUB_FAILED,
                    '[ItemLogic] sub item error!'
                );
                return false;
            }
        } else if ($num == 0) {
            if(!$itemModel->delItemByUID($item)) {
                LogMark::getInstance()->markError(
                    GameErrorCode::DATA_DELETE_ERROR,
                    '[ItemLogic] del item by uid error!'
                );
                return false;
            }
        } else {
            LogMark::getInstance()->markError(
                GameErrorCode::ITEM_VERIFY_CHECK_FAILED,
                '[ItemLogic] sub item num can not!'
            );
            return false;
        }
        return true;
    }

    //检查要穿的服装是否合法--过期,类型检查
    public function checkClothIsInvalid(array $cloth): int
    {
        /**
         * @var ItemModel $itemModel
         */
        $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);
        foreach ($cloth as $clothId) {
            if ($clothId == 0) {
                continue;
            }
            //检查道具类型
            $itemCfg = $this->getTitle(TemplateDefine::TYPE_ITEM, $clothId);
            if(is_null($itemCfg)) {
                LogMark::getInstance()->markError(
                    GameErrorCode::ITEM_VERIFY_CHECK_FAILED,
                    '[ItemLogic] not found item config',
                    ["itemId" => $clothId]
                );
                return ClientErrorCode::ITEM_IS_INVALID;
            }
            if ($itemCfg[TemplateItem::ItemType] != TemplateItem::ITEM_TYPE_CLOTH) {
                return ClientErrorCode::ITEM_TYPE_ERROR_NOT_CLOTH;
            }
            $items = $itemModel->getItemDataByTplID($clothId);
            //检查服装
            if (empty($items)) {
                LogMark::getInstance()->markDebug(
                    '[ItemLogic] search item info error!',
                    ['uid'=>$clothId]
                );
                return ClientErrorCode::ITEM_NOT_FOUND;
            }
            //检查有效期
            foreach ($items as $item) {
                if ($item->expire != ItemModel::TIME_NORMAL && Utils::getServerTimestamp() >= $item->expire) {
                    return ClientErrorCode::ITEM_TIME_EXPIRE;
                }
            }
        }
        return ClientErrorCode::CLIENT_SUCCESS;
    }

    //折算过期道具
    public function convertExpireItem(array $itemList): array
    {
        $itemDataList = array();
        /**
         * @var ItemModel $itemModel
         */
        $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);
        //检查道具
        foreach ($itemList as $itemUid => $num) {
            $item = $itemModel->getItemData($itemUid);
            if (is_null($item)) {
                LogMark::getInstance()->markError(
                    GameErrorCode::ITEM_SEARCH_INFO_FAILED,
                    '[ItemLogic] search item info error!',
                    ['uid'=>$itemUid]
                );
                return [ClientErrorCode::ITEM_NOT_FOUND, []];
            }
            //检查有效期
            if ($item->expire == ItemModel::TIME_NORMAL || Utils::getServerTimestamp() < $item->expire) {
                return [ClientErrorCode::ITEM_NOT_EXPIRE, []];
            }
            $itemDataList[$itemUid] = $item;
        }
        $itemArray = array();
        //兑换道具
        foreach ($itemDataList as $itemUid => $item) {
            $itemConfig = $this->getTitle(TemplateDefine::TYPE_ITEM, $item->tplID);
            if (is_null($itemConfig)) {
                //配置不存在 直接删除
                $item->deleteItem();
                $itemModel->addItemPacket($item->uid, $item->tplID, $item->expire, 0);
                continue;
            }
            //检查兑换
            $this->convertItem($itemConfig, $item->num, $itemArray);
            //删除道具
            $item->deleteItem();
            $itemModel->addItemPacket($item->uid, $item->tplID, $item->expire, 0);
        }
        //折算出的道具需要通知
        $convertItemList = array();
        //加钱加道具
        foreach ($itemArray as $itemId => $num) {
            $this->gainItem($itemId, $num);
            $convertItem = new ConvertItem();
            $convertItem->setItemId($itemId);
            $convertItem->setNum($num);
            $convertItemList[] = $convertItem;
        }
        $itemModel->updateItemPacket();
        return [ClientErrorCode::CLIENT_SUCCESS, $convertItemList];
    }

    //折算道具
    private function convertItem(array $itemConfig, int $itemNum, array &$itemArray)
    {
        //没有对应的折算道具
        if ($itemConfig[TemplateItem::ConvertedId] == 0) {
            return;
        }
        $id = $itemConfig[TemplateItem::ConvertedId];
        $num = $itemConfig[TemplateItem::ConvertedCount] * $itemNum;
        isset($itemArray[$id]) ? $itemArray[$id] += $num : $itemArray[$id] = $num;
    }

    //打点日志
    public function setLogTaskId(int $taskId)
    {
        /**
         * @var ItemModel $itemModel
         */
        $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);
        $itemModel->setLogTaskId($taskId);
        /**
         * @var MoneyModel $money
         */
        $money = ModelManager::getInstance()->getModel(ModelTypeDefine::MONEY);
        $money->setLogTaskId($taskId);
    }

    public function clearTaskId()
    {
        /**
         * @var ItemModel $itemModel
         */
        $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);
        $itemModel->clearTaskId();
        /**
         * @var MoneyModel $money
         */
        $money = ModelManager::getInstance()->getModel(ModelTypeDefine::MONEY);
        $money->clearTaskId();
    }

    public function setLogAddItemSource(int $source)
    {
        /**
         * @var ItemModel $itemModel
         */
        $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);
        $itemModel->setLogAddItemSource($source);
        /**
         * @var MoneyModel $money
         */
        $money = ModelManager::getInstance()->getModel(ModelTypeDefine::MONEY);
        $money->setLogAddItemSource($source);
    }

    public function clearLogAddItemSource()
    {
        /**
         * @var ItemModel $itemModel
         */
        $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);
        $itemModel->clearLogAddItemSource();
        /**
         * @var MoneyModel $money
         */
        $money = ModelManager::getInstance()->getModel(ModelTypeDefine::MONEY);
        $money->clearLogAddItemSource();
    }

    public function setLogConsumeItemSource(int $source)
    {
        /**
         * @var ItemModel $itemModel
         */
        $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);
        $itemModel->setLogConsumeItemSource($source);
        /**
         * @var MoneyModel $money
         */
        $money = ModelManager::getInstance()->getModel(ModelTypeDefine::MONEY);
        $money->setLogConsumeItemSource($source);
    }

    public function clearLogConsumeItemSource()
    {
        /**
         * @var ItemModel $itemModel
         */
        $itemModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ITEM);
        $itemModel->clearLogConsumeItemSource();
        /**
         * @var MoneyModel $money
         */
        $money = ModelManager::getInstance()->getModel(ModelTypeDefine::MONEY);
        $money->clearLogConsumeItemSource();
    }
}
