<?php

/**
 * ItemModel.php
 * 道具操作
 * Created On 2022/1/21
 */

namespace Game\Model;

use Framework\DB\Handler\PlayerDBPipe;
use Framework\Define\ErrorDefine;
use Framework\Network\SendMessage;
use Framework\Lib\Utils;
use Game\Config\GameConfig;
use Game\Constant\ConstTemplate\TemplateClothing;
use Game\Constant\ConstTemplate\TemplateCreateRole;
use Game\Constant\ConstTemplate\TemplateItem;
use Game\Constant\GameErrorCode;
use Game\Constant\TemplateDefine;
use Framework\Logic\TemplateHelp;
use Framework\DB\Handler\PlayerDBHandler;
use Game\Data\ItemData;
use Game\Constant\DBTableDefine;
use Game\Logic\AccountLogic;
use Game\Logic\CarLogic;
use Game\Logic\ClothLogic;
use Game\Logic\MoneyLogic;
use Game\Model\Rank\RankModel;
use Game\Operation\EventLog\AddItemLog;
use Game\Operation\EventLog\ConsumeItemLog;
use Game\Operation\EventLog\EventLog_Manager;
use Game\Operation\EventLog\EventLogType;
use Game\Protobuf\GCUseItemShow;
use Game\Protobuf\PacketId;
use Game\Protobuf\TimeOutPush;
use Game\Protobuf\UpdateItemSendRecv;
use Game\Protobuf\UpdateItemSingle;
use Framework\Logic\PacketCacheLogic;
use Framework\Log\LogMark;
use Game\Protobuf\UseItemSingle;

class ItemModel
{
    use TemplateHelp;
    use PlayerDBHandler;
    use MoneyLogic;
    use PacketCacheLogic;
    use CarLogic;
    use AccountLogic;
    use ClothLogic;

    public int $playerId;
    private array $dataArr;
    private int $pipeUidIndex = 0;  //批量创建uid的index

    public const TYPE_CONVERTED_NORMAL = 0; // 不折算

    public const TIME_NORMAL   = 0;         // 永久道具，时间为0

    public const ADD_TYPE_NUM  = 0;  //累加类型为数量
    public const ADD_TYPE_TIME = 1;  // 累加类型为时间
    public const ADD_TYPE_ACTIVITY = 2;  // 活动过期时间为结束

    private UpdateItemSendRecv $sendUpdateItem;
    private array              $sendItemList ;
    private array              $useItemList ;
    private GCUseItemShow      $sendUseShow;

    //打点日志
    private int $logTaskId = 0; //获得道具任务id
    private int $logAddItemSource = 0;  //获得道具来源
    private int $logConsumeItemSource = 0;  //消耗道具来源

    public function __construct()
    {
        $this->sendItemList = array();
        $this->useItemList = array();
        $this->sendUpdateItem = new UpdateItemSendRecv();
        $this->sendUseShow = new GCUseItemShow();
    }

    public function setPlayerId($playerId)
    {
        $this->playerId = $playerId;
    }

    // 删除道具
    public function delItemByUID(ItemData $item):bool {
        if(!$item->deleteItem()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_DELETE_ERROR,
                '[ItemModel] delItemByUID error!', (array)$item
            );
            return false;
        }
        $this->addItemPacket($item->uid, $item->tplID, $item->expire, 0);
        //记录打点日志
        $log = EventLog_Manager::getInstance()->getEventLog(EventLogType::ItemConsume);
        if (!is_null($log)) {
            /**
             * @var ConsumeItemLog $log
             */
            $log->itemId = $item->tplID;
            $log->costNum = $item->num;
            $log->newNum = 0;
            $log->source = $this->logConsumeItemSource;
            $log->markLog();
        }
        return true;
    }

    // 扣除道具
    public function subItem(ItemData $item, $num): bool {
        $itemNum = $item->num - $num;
        if ($itemNum < 0) {
            LogMark::getInstance()->markError(
                GameErrorCode::ITEM_SUB_FAILED,
                '[ItemModel] subItem item error!', (array)$item
            );
            return false;
        }
        if ($itemNum == 0) {
            $this->delItemByUID($item);
        }

        $item->setNum($itemNum);
        if(!$item->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::ITEM_SAVE_FAILED,
                '[ItemModel] subItem sub save error!', (array)$item
            );
            return false;
        }
        //记录打点日志
        $log = EventLog_Manager::getInstance()->getEventLog(EventLogType::ItemConsume);
        if (!is_null($log)) {
            /**
             * @var ConsumeItemLog $log
             */
            $log->itemId = $item->tplID;
            $log->costNum = $num;
            $log->newNum = $itemNum;
            $log->source = $this->logConsumeItemSource;
            $log->markLog();
        }
        $this->addItemPacket($item->uid, $item->tplID, $item->expire, $itemNum);
        return true;
    }

    // 添加道具
    public function addItem(ItemData $item, int $num): bool {
        $itemCfg = $this->getTitle(TemplateDefine::TYPE_ITEM, $item->tplID);
        $itemNum = $item->num + $num;
        if($itemNum > $itemCfg[TemplateItem::MaxNumb]) {
            LogMark::getInstance()->markError(
                GameErrorCode::ITEM_SAVE_FAILED,
                '[ItemModel] addItem add is max error!',
                (array)$item
            );
            return false;
        }
        $item->setNum($itemNum);
        $this->addItemPacket($item->uid, $item->tplID, $item->expire, $itemNum);
        if(!$item->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::ITEM_SAVE_FAILED,
                '[ItemModel] addItem add save error!', (array)$item
            );
            return false;
        }
        //记录打点日志
        $log = EventLog_Manager::getInstance()->getEventLog(EventLogType::ItemAdd);
        if (!is_null($log)) {
            /**
             * @var AddItemLog $log
             */
            $log->itemId = $item->tplID;
            $log->addNum = $num;
            $log->newNum = $itemNum;
            $log->expireTime = 0;
            $log->taskId = $this->logTaskId;
            $log->source = $this->logAddItemSource;
            $log->markLog();
        }
        return true;
    }

    // 修改道具失效时间
    public function updateItemExpireTime(ItemData $item, int $expireTime): bool {
        $item->setExpire($expireTime);
        $this->addItemPacket($item->uid, $item->tplID, $expireTime, $item->num);
        if(!$item->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::ITEM_SAVE_FAILED,
                '[ItemModel] updateItemExpireTime item update expireTime save error!', (array)$item
            );
            return false;
        }
        return true;
    }

    // 新建道具
    public function createItem(int $tplID, int $num, $expireTime): bool {
        $item = new ItemData(
            $this->playerId,
            $this->makeItemId(),
            $tplID,
            $num,
            $expireTime,
        );
        $this->addItemPacket($item->uid, $item->tplID, $item->expire, $item->num);
        if(!$item->createItem()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_CREATE_ERROR,
                '[ItemModel] createItem error!', (array)$item
            );
            return false;
        }
        //记录打点日志
        $log = EventLog_Manager::getInstance()->getEventLog(EventLogType::ItemAdd);
        if (!is_null($log)) {
            /**
             * @var AddItemLog $log
             */
            $log->itemId = $item->tplID;
            $log->addNum = $num;
            $log->newNum = $num;
            $log->expireTime = $expireTime;
            $log->taskId = $this->logTaskId;
            $log->source = $this->logAddItemSource;
            $log->markLog();
        }
        return true;
    }

    //新号批量创建道具
    public function pipeCreateItem(array $items)
    {
        //记录打点日志
        $log = EventLog_Manager::getInstance()->getEventLog(EventLogType::ItemAdd);
        foreach ($items as $itemId => $num) {
            $itemData = new ItemData(
                $this->playerId,
                $this->pipeMakeItemId(),
                $itemId,
                $num,
                0,
            );
            $this->addItemPacket($itemData->uid, $itemData->tplID, $itemData->expire, $itemData->num);
            $itemData->registerDB();
            if (!is_null($log)) {
                /**
                 * @var AddItemLog $log
                 */
                $log->itemId = $itemData->tplID;
                $log->addNum = $num;
                $log->newNum = $num;
                $log->expireTime = 0;
                $log->taskId = $this->logTaskId;
                $log->source = $this->logAddItemSource;
                $log->markLog();
            }
        }
    }

    public function getItemDataByTplID(int $tplId): array
    {
        $itemArr = array();
        $result = array();
        if(!$this->getTitleList(DBTableDefine::TABLE_ITEM, $this->playerId, $result)) {
            return [];
        }
        if(empty($result)) {
            return [];
        }
        foreach ($result as $uid=>$t) {
            if($t == $tplId) {
                $item = $this->getItemData($uid);
                if(!is_null($item)) {
                    $itemArr[] = $item;
                }
            }
        }
        return $itemArr;
    }

    public function getItemData(int $uid): ?itemData
    {
        $item = new ItemData($this->playerId, $uid);
        if(!$item->searchItem()) {
            return null;
        }
        return $item;
    }

    /***
     * @return array
     * 获取校验数据,键名升序
     */
    public function getSignData():array
    {
        $itemArr = array();
        $result = array();
        if(!$this->getTitleList(DBTableDefine::TABLE_ITEM, $this->playerId, $result)) {
            return [];
        }
        if(empty($result)) {
            return [];
        }
        foreach ($result as $uid=>$t) {
            $item = $this->getItemData($uid);
            $line = array();
            $line['ExpirationTime'] = $item->expire;
            $line['Id'] = $item->uid;
            $line['ItemId'] = $item->tplID;
            $line['Num'] = $item->num;
            if(!is_null($item)) {
                $itemArr[] = $line;
            }
        }
        return $itemArr;
    }

    // 创建唯一id
    public function makeItemId(): int
    {
        $index = $this->getUidIndex();
        return Utils::makeObjectID(GameConfig::getInstance()->SERVER_ID(), DBTableDefine::TABLE_ITEM, $index);
    }

    private function pipeMakeItemId(): int
    {
        $this->pipeUidIndex++;
        if ($this->pipeUidIndex > 32768) {
            $this->pipeUidIndex = 1;
        }
        return Utils::makeObjectID(GameConfig::getInstance()->SERVER_ID(), DBTableDefine::TABLE_ITEM, $this->pipeUidIndex);
    }

    // 注册玩家加道具
    public function initPlayerItemData(int $roleModel) {
        $roleConfig = $this->getTitle(TemplateDefine::TYPE_CREATE_ROLE, $roleModel);
        if(is_null($roleConfig)) {
            return ;
        }
        //分类添加道具
        //加车
        foreach ($roleConfig[TemplateCreateRole::InitCar] as $itemId => $data) {
            $this->itemGainCarLogic($data[0], 1, $data[1], $itemId);
        }
        //加钱
        $this->addMoneyList($roleConfig[TemplateCreateRole::InitMoney]);
        //批量加道具
        PlayerDBPipe::getInstance()->start($this->playerId);
        $this->pipeCreateItem($roleConfig[TemplateCreateRole::InitItem]);
        PlayerDBPipe::getInstance()->finish();
        //更新服装排行榜
        $this->changeRankScore(
            RankModel::CLOTH,
            $this->changeRankingEventPooled(RankModel::CLOTH, $roleConfig[TemplateCreateRole::ClothScore])
        );
    }

    public function timerExpire(array $expireData): bool
    {
        $removeIds = array();
        $sendMessage = new TimeOutPush();

        if(empty($expireData)) {
            LogMark::getInstance()->markError(
                ErrorDefine::REQUEST_DATA_INVALID,
                '[ItemModel] timerExpire array empty!'
            );
            return false;
        }
        $clothArray = array();
        foreach($expireData as $uid => $exp) {
            $removeIds[] = $uid;
            $item = new ItemData($this->playerId, $uid);
            if (!$item->searchItem()) {
                LogMark::getInstance()->markDebug(
                    "[ItemModel] timerExpire searchItem fail",
                    array(
                        "uid" => $uid
                    )
                );
                continue;
            }
            $itemConfig = $this->getTitle(TemplateDefine::TYPE_ITEM, $item->tplID);
            if (is_null($itemConfig)) {
                //配置不存在,直接删除
                $item->deleteItem();
                $this->addItemPacket($item->uid, $item->tplID, $item->expire, 0);
            }
            //检查是否为服装,服装直接删除
            if ($itemConfig[TemplateItem::ItemType] == TemplateItem::ITEM_TYPE_CLOTH) {
                $clothArray[] = $item->tplID;
                $item->deleteItem();
                $this->addItemPacket($item->uid, $item->tplID, $item->expire, 0);
            }
        }
        //是否有服装过期,换装
        $this->checkResetPlayerClothOnExpire($this->playerId, $clothArray);
        $this->updateItemPacket();

        // 通知道具过期
        $sendMessage->setRemoveId($removeIds);
        SendMessage::getInstance()->sendClient(PacketId::TimeOut_Push_Recv, $sendMessage);
        return true;
    }

    public function itemGainCarByCarID(int $carID, int $level, int $expire, int $itemID):bool {
        if(!$this->itemGainCar($carID, $level, $expire, $itemID)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[ItemModel] itemGainCarByCarID add car by carID error!',
                ['carID' => $carID, 'itemID' => $itemID]
            );
            return false;
        }
        return true;
    }

    public function addItemPacket(int $uid, int $tplID, int $expire, int $num) {
        $sendItemMsg = new UpdateItemSingle();
        $sendItemMsg->setUid($uid);
        $sendItemMsg->setTplId($tplID);
        $sendItemMsg->setExpirationTime($expire);
        $sendItemMsg->setNum($num);
        $this->sendItemList[] = $sendItemMsg;
    }

    public function updateItemPacket()
    {
        if (!empty($this->sendItemList)) {
            $this->sendUpdateItem->setItem($this->sendItemList);
            SendMessage::getInstance()->sendClient(PacketId::Use_Item_Recv, $this->sendUpdateItem);
            $this->clearSendMsg();
        }
    }

    public function addPacketExec()
    {
        if (!empty($this->sendItemList)) {
            $this->sendUpdateItem->setItem($this->sendItemList);
            $this->addPacket(PacketId::Use_Item_Recv, $this->sendUpdateItem);
            $this->clearSendMsg();
        }
    }

    private function clearSendMsg() {
        unset($this->sendItemList);
        $this->sendItemList = array();
    }

    private function clearSendUseMsg() {
        unset($this->useItemList);
        $this->useItemList = array();
    }

    //登录检查
    public function onLoginExec()
    {
        //检查服装是否有过期,过期替换为初始服装
        [$femaleCloth, $maleCloth] = $this->getPlayerClothArray($this->playerId);
        $femaleClothIdx = array();
        $maleClothIdx = array();
        $this->checkClothIsExpire($femaleCloth, $femaleClothIdx);
        $this->checkClothIsExpire($maleCloth, $maleClothIdx);
        $this->resetPlayerClothByIdx($femaleClothIdx, $maleClothIdx, $femaleCloth, $maleCloth);
    }

    //检查服装是否过期
    private function checkClothIsExpire(array $cloth, array &$idxArr)
    {
        foreach ($cloth as $itemId) {
            if ($itemId == 0) {
                continue;
            }
            $items = $this->getItemDataByTplID($itemId);
            //检查服装
            if (empty($items)) {
                LogMark::getInstance()->markDebug(
                    '[ItemModel] checkClothIsExpire search item info error!',
                    ['clothId'=>$itemId]
                );
                $part = $this->getTerm(TemplateDefine::TYPE_CLOTHING, $itemId, TemplateClothing::Part);
                if (!is_null($part)) {
                    $idxArr[$part] = $itemId;
                }
                continue;
            }
            //检查有效期
            foreach ($items as $item) {
                if ($item->expire != ItemModel::TIME_NORMAL && Utils::getServerTimestamp() >= $item->expire) {
                    $part = $this->getTerm(TemplateDefine::TYPE_CLOTHING, $itemId, TemplateClothing::Part);
                    if (!is_null($part)) {
                        $idxArr[$part] = $itemId;
                    }
                }
            }
        }
    }

    public function addUseItemPacket(int $tplID, int $num, int $expire = 0) {
        $sendItemMsg = new UseItemSingle();
        $sendItemMsg->setTplId($tplID);
        $sendItemMsg->setNum($num);
        $sendItemMsg->setExpire($expire);
        $this->useItemList[] = $sendItemMsg;
    }

    public function sendUseItemPacket(int $useTplID) {
        if (!empty($this->useItemList)) {
            $this->sendUseShow->setItems($this->useItemList);
            $this->sendUseShow->setUseTplID($useTplID);
            $this->addPacket(PacketId::GC_UseItemShow, $this->sendUseShow);
            $this->clearSendUseMsg();
        }
    }

    //打点日志
    public function setLogTaskId(int $taskId)
    {
        $this->logTaskId = $taskId;
    }

    public function clearTaskId()
    {
        $this->logTaskId = 0;
    }

    public function setLogAddItemSource(int $source)
    {
        $this->logAddItemSource = $source;
    }

    public function clearLogAddItemSource()
    {
        $this->logAddItemSource = 0;
    }

    public function setLogConsumeItemSource(int $source)
    {
        $this->logConsumeItemSource = $source;
    }

    public function clearLogConsumeItemSource()
    {
        $this->logConsumeItemSource = 0;
    }
}
