<?php
/**
 * 房间逻辑类
 */

namespace Game\Logic\Room;

use Framework\Lib\Utils;
use Framework\Log\LogMark;
use Framework\MVC\ModelManager;
use Framework\DB\Handler\PlayerDBHandler;
use Game\Constant\ClientErrorCode;
use Game\Constant\ConstTemplate\TemplateCompetitionMode;
use Game\Constant\ConstTemplate\TemplateConst;
use Game\Constant\DBTableDefine;
use Game\Constant\GameConstantDefine;
use Game\Constant\GameErrorCode;
use Game\Constant\ModelTypeDefine;
use Game\Constant\TemplateDefine;
use Framework\Logic\TemplateHelp;
use Game\Data\AccountData;
use Game\Data\PaiWei\PaiweiLevelData;
use Game\Data\RoomData;
use Game\Logic\AccountLogic;
use Game\Logic\Activity\ScuffleLogic;
use Game\Model\RoomModel;
use Game\Protobuf\IMRoom;

trait RoomLogic {

    use PlayerDBHandler;
    use TemplateHelp;
    use AccountLogic;
    use ScuffleLogic;
    public int $playerID;

    // 创建房间
    public function createRoom(int $mapID, int $modeType, $roomName,
                               int $roomState = RoomModel::ROOM_STATE_PRIVATE): bool {
        /**
         * @var RoomModel $roomModel
         */
        $roomModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ROOM);

        $mapCfg = $this->getTitle(TemplateDefine::TYPE_PVP_MAP, $mapID);
        if(is_null($mapCfg)) {
            logMark::getInstance()->markError(
                GameErrorCode::SEARCH_CONFIG_INFO_FAILED,
                '[logic] create room get map cfg by mapID error!',
                ['mapID' => $mapID]
            );
            $roomModel->GCJoinRoom(ClientErrorCode::ERROR_ROOM_MAP_NOT_EXIST);
            $roomModel->GCSendJoinRoom();
            return false;
        }

        $modeCfg = $this->getTitle(TemplateDefine::TYPE_COMPETITION_MODE, $modeType);
        if(is_null($modeCfg)) {
            logMark::getInstance()->markError(
                GameErrorCode::SEARCH_CONFIG_INFO_FAILED,
                '[logic] create customize room get mode cfg by modeType error!',
                ['modeType' => $modeType]
            );
            $roomModel->GCJoinRoom(ClientErrorCode::ERROR_ROOM_MODE_NOT_EXIST);
            $roomModel->GCSendJoinRoom();
            return false;
        }

        if(!$this->modeTypeIsOpen($modeCfg)) {
            logMark::getInstance()->markError(
                GameErrorCode::ROOM_MODE_TYPE_NOT_OPEN,
                '[logic] modeType not open error!',
                ['modeType' => $modeType]
            );
            $roomModel->GCJoinRoom(ClientErrorCode::ERROR_ROOM_MODE_TYPE_NOT_OPEN);
            $roomModel->GCSendJoinRoom();
            return false;
        }

        //大乱斗 检查活动是否开启
        if (($modeType == GameConstantDefine::MATCH_MODE_SCUFFLE) && !$this->checkScuffleIsOpen()) {
            logMark::getInstance()->markError(
                GameErrorCode::ROOM_MODE_TYPE_NOT_OPEN,
                '[RoomLogic] scuffle not open',
                    ['modeType' => $modeType]
            );
            $roomModel->GCJoinRoom(ClientErrorCode::SCUFFLE_NOT_OPEN);
            $roomModel->GCSendJoinRoom();
            return false;
        }

        if(!$roomModel->createRoom($mapID, $modeType, $roomName, $roomState)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_CREATE_ERROR,
                '[logic] create room error!',
                ['mapID' => $mapID, 'modeType' => $modeType]
            );
            $roomModel->GCJoinRoom(ClientErrorCode::ERROR_CREATE_ROOM_FAILED);
            $roomModel->GCSendJoinRoom();
            return false;
        }
        $roomModel->GCSendJoinRoom();
//        $this->upRoleStatus(RoleExtData::Room);
        return true;
    }

    // 快速进入房间
    public function fastJoinRoom(int $modeType, string $roomName):bool {
        /**
         * @var RoomModel $roomModel
         */
        $roomModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ROOM);

        $modeCfg = $this->getTitle(TemplateDefine::TYPE_COMPETITION_MODE, $modeType);
        if(is_null($modeCfg)) {
            logMark::getInstance()->markError(
                GameErrorCode::SEARCH_CONFIG_INFO_FAILED,
                '[logic] fast joinRoom get mod cfg by modeType error!',
                ['modeType' => $modeType]
            );
            $roomModel->GCJoinRoom(ClientErrorCode::ERROR_ROOM_MODE_NOT_EXIST);
            $roomModel->GCSendJoinRoom();
            return false;
        }

        if(!$this->modeTypeIsOpen($modeCfg)) {
            logMark::getInstance()->markError(
                GameErrorCode::ROOM_MODE_TYPE_NOT_OPEN,
                '[logic] modeType not open error!',
                ['modeType' => $modeType]
            );
            $roomModel->GCJoinRoom(ClientErrorCode::ERROR_ROOM_MODE_TYPE_NOT_OPEN);
            $roomModel->GCSendJoinRoom();
            return false;
        }

        // 房间最大人数
        $roomMax = $modeCfg[TemplateCompetitionMode::RoomMax];
        $roomList = $this->getRoomList($modeType, $roomMax);
        if(empty($roomList)) {
            return $this->fastJoinCreateRoom($modeType, $roomName);
        }
        foreach ($roomList as $roomID => $s) {
            // 加入房间逻辑
            if(!$this->fastPlayerJoinRoom($roomID, false)) {
                continue;
            }
            $roomModel->GCSendJoinRoom();
            $roomModel->GCSendAddJoinRoom();
            return true;
        }
//        $roomModel->GCJoinRoom(ClientErrorCode::ERROR_ROOM_JOIN_ROOM_FAILED);
//        $roomModel->GCSendJoinRoom();
        $this->upRoleStatus(GameConstantDefine::PLAYER_STATUS_ROOM, $roomModel->playerId);
        return $this->fastJoinCreateRoom($modeType, $roomName);
    }

    public function fastJoinCreateRoom($modeType, $roomName): bool {
        /**
         * @var RoomModel $roomModel
         */
        $roomModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ROOM);
        $mapID = RoomModel::MAP_RANDOM;
        if(!$this->createRoom($mapID, $modeType, $roomName, RoomModel::ROOM_STATE_PUBLIC)) {
            logMark::getInstance()->markError(
                GameErrorCode::DATA_CREATE_ERROR,
                '[logic] search not found too rand mapID create room error!',
                ['modeType' => $modeType]
            );
            $roomModel->GCJoinRoom(ClientErrorCode::ERROR_ROOM_OPERATE_FAILED);
            $roomModel->GCSendJoinRoom();
            return false;
        }
        return true;
    }

    // 快速匹配房间内加入一个玩家
    public function fastPlayerJoinRoom(int $roomID, bool $isErrLog = true):bool {
        /**
         * @var RoomModel $roomModel
         */
        $roomModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ROOM);

        $room = $roomModel->newRoom($roomID);
        if(!$room->searchRoomByRoomID()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[logic] join room error!', (array)$room
            );
            return false;
        }

        if(!$roomModel->playerJoinRoom($room)) {
            return false;
        }
        return true;
    }

    // 判断模式是否开启
    public function modeTypeIsOpen(array $modeCfg): bool
    {
        return $modeCfg[TemplateCompetitionMode::OpenIf] == RoomModel::MODE_OPEN;
    }

    private function getRoomList(int $modeType, int $roomMax): array {
        /**
         * @var RoomModel $roomModel
         */
        $roomModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ROOM);

        $startScore = $modeType * 10000 + RoomModel::ROOM_STATE_PUBLIC * 1000 + RoomModel::ROOM_GAME_READY * 100 + 1;
        $endScore   = $modeType * 10000 + RoomModel::ROOM_STATE_PUBLIC * 1000 + RoomModel::ROOM_GAME_READY * 100 + $roomMax - 1;
        return $roomModel->getRoomListByModeAndScore($startScore, $endScore);
    }

    public function IMFindRoomList(int $page, array &$rooms): bool {
        $indexEnd = $this->getPageLimit($page);

        $end = Utils::getServerTimestamp();
        $now = $end + RoomModel::CHECK_ROOM_TIME;
        if (false === $this->searchRank(
                DBTableDefine::TABLE_ROOM_TIME, 0, 1, $now, $ret)){
            LogMark::getInstance()->markError(GameErrorCode::DATA_SEARCH_ERROR,
                "[RoomLogic] search IM room error!",);
            return false;
        }

        if (is_array($ret) && count($ret) > 0) {
            foreach ($ret as $roomID => $time) {
                if($this->checkSearchRoomLimit($roomID, $rooms)) {
                    --$indexEnd;
                }
                if($indexEnd <= 0) {
                    break;
                }
            }
        }
        return true;
    }

    // 聊天检查房间规则
    private function checkSearchRoomLimit(int $roomID, array &$rooms):bool {
        $room = new RoomData($roomID);
        if(!$room->searchRoomByRoomID()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_SEARCH_ERROR,
                '[room logic] search room info error!'
            );
            return false;
        }
        // 公开的房间
        if($room->roomState != RoomModel::ROOM_STATE_PUBLIC) {
            return false;
        }
        $roomMax = $this->getTitle(TemplateDefine::TYPE_COMPETITION_MODE, $room->modeType)
        [TemplateCompetitionMode::RoomMax];
        $playerNum = count($room->playerInfo);
        if($playerNum >= $roomMax) {
            return false;
        }
        // 模式限制
        if(!in_array($room->modeType, GameConstantDefine::IM_FIND_ROOM_TYPE_MAP)) {
            return false;
        }

        // 未开始游戏
        if($room->playType != RoomModel::ROOM_GAME_READY) {
            return false;
        }
        $player = [];
        $time = 0;
        foreach($room->playerInfo as $id => $p) {
            if($time === 0) {
                $player = $p;
            } else {
                if($p[RoomData::JOIN_TIME] < $time) {
                    $player = $p;
                    $time = $p[RoomData::JOIN_TIME];
                }
            }
        }
        $imRoom = new IMRoom();
        $imRoom->setRoomID($room->roomID);
        $imRoom->setNickName($player[RoomData::NICK_NAME]);
        $imRoom->setHead($player[RoomData::HEAD]);
        $imRoom->setModeType($room->modeType);
        $imRoom->setMapID($room->mapID);
        $imRoom->setPlayerNum($playerNum);
        $rooms[] = $imRoom;
        return true;
    }
    
    public function getPageLimit($page):int {
        // 发放绑定奖励道具
        $count = $this->getTerm(TemplateDefine::TYPE_CONST,
            TemplateConst::Const_IM_Room_Count_Max, TemplateConst::ConstNum);
        return $count * $page;
    }

    // 通过roomID 加入房间中
    public function joinRoomByRoomID(int $roomID, int $invitedPlayer = 0):bool {
        /**
         * @var RoomModel $roomModel
         */
        $roomModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ROOM);
        $this->playerID = $roomModel->playerId;
        $room = $roomModel->newRoom($roomID);
        if(!$room->searchRoomByRoomID()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[logic] join room search  error!', (array)$room
            );
            $roomModel->GCJoinRoom(ClientErrorCode::ERROR_ROOM_NOT_EXIST);
            $roomModel->GCSendJoinRoom();
            return false;
        }

        // 私密不可进入
        if($invitedPlayer == 0 && $room->roomState == RoomModel::ROOM_STATE_PRIVATE) {
            $roomModel->GCJoinRoom(ClientErrorCode::ERROR_ROOM_STATE_IS_PRIVATE);
            $roomModel->GCSendJoinRoom();
            return false;
        }

        if($room->playType == RoomModel::ROOM_GAME_MATCH_OR_PLAY) {
            LogMark::getInstance()->markInfo(
                '[model] join room bug room playType is play or match!', (array)$room
            );
            $roomModel->GCJoinRoom(ClientErrorCode::ERROR_ROOM_IS_PLAY);
            $roomModel->GCSendJoinRoom();
            return false;
        }

        if(!$roomModel->playerJoinRoom($room, $invitedPlayer)) {
            $roomModel->GCSendJoinRoom();
            return false;
        }
        $roomModel->GCSendJoinRoom();
        $roomModel->GCSendAddJoinRoom();
        $this->upRoleStatus(GameConstantDefine::PLAYER_STATUS_ROOM, $roomModel->playerId);
        return true;
    }

    // 房主踢人
    public function removePlayerByRoomID(int $roomID, int $playerID): bool {
        /**
         * @var RoomModel $roomModel
         */
        $roomModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ROOM);
        $this->playerID = $roomModel->playerId;
        $room = $roomModel->newRoom($roomID);
        $oldRoom = $roomModel->newRoom($roomID);
        $oldRoom->searchRoomByRoomID();

        if(!$room->searchRoomByRoomID()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[logic] remove player search room  error!', (array)$room
            );
            $roomModel->GCRemovePlayer(ClientErrorCode::ERROR_ROOM_NOT_EXIST);
            $roomModel->GCSendRemovePlayer();
            return false;
        }

        $playerList = $room->playerInfo;

        array_multisort(array_column($playerList, RoomData::JOIN_TIME), SORT_ASC, $playerList);
        if($playerList[0][RoomData::PLAYER_ID] != $this->playerID) {
            $roomModel->GCRemovePlayer(ClientErrorCode::ERROR_ROOM_NOT_OWNER_IS_CHANGE);
            $roomModel->GCSendRemovePlayer();
            return false;
        }

        if(!$roomModel->removeRoomPlayer($room, $playerID)) {
            $roomModel->GCSendRemovePlayer();
            return false;
        }

        $roomModel->GCSendRemovePlayer();
        $room->searchRoomByRoomID();
        $roomModel->GCSendAddRemovePlayer($oldRoom->playerInfo);
        $this->upRoleStatus(GameConstantDefine::PLAYER_STATUS_ONLINE, $playerID);
        return true;
    }


    // 退出房间
    public function exitRoom(int $roomID): bool {
        /**
         * @var RoomModel $roomModel
         */
        $roomModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ROOM);
        $this->playerID = $roomModel->playerId;
        $room = $roomModel->newRoom($roomID);
        if(!$room->searchRoomByRoomID()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[logic] exit room search room info error!', (array)$room
            );
            $roomModel->GCExitRoom(ClientErrorCode::ERROR_ROOM_NOT_EXIST, $roomID);
            $roomModel->GCSendExitRoom();
            return false;
        }

        if(!$roomModel->exitRoom($room)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[logic] exit room  error!', (array)$room
            );
            $roomModel->GCSendExitRoom();
            return false;
        }
        $roomModel->GCSendExitRoom();
        $roomModel->GCSendAddExitRoom($room->playerInfo, $roomID);
        $this->upRoleStatus(GameConstantDefine::PLAYER_STATUS_ONLINE, $roomModel->playerId);
        return true;
    }

    // 获取所有超时的房间列表（配置的固定时长5分钟）
    public function cliGetTimeOutRoomList(&$ret): bool
    {
        /**
         * @var RoomModel $roomModel
         */
        ModelManager::getInstance()->setPlayerId(0);
        return ModelManager::getInstance()->getModel(ModelTypeDefine::ROOM)->getCheckRoomByScore($ret);
    }

    // 通过roomID 获取room信息
    public function cliGetRoomInfoByRoomID(int $roomID): ?RoomData {
        $room = new RoomData($roomID);
        if(!$room->searchRoomByRoomID()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_SEARCH_ERROR,
                '[RoomLogic] cli search room info by room id error!', (array)$room
            );
            return null;
        }
        return $room;
    }

    // CLI 检测离线退出房间
    public function cliCheckOffLineExitRoom($roomID, int $playerID):bool {
        // 检测玩家是否在房间内
        $oldRoomID = $this->searchPlayerInfo($playerID)[AccountData::DB_ROOM_ID];
        /**
         * @var RoomModel $roomModel
         */
        ModelManager::getInstance()->setPlayerId(0);
        $roomModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ROOM);

        $room = $roomModel->checkPlayerInRoom($roomID);
        if(!is_null($room)) {
            if(!$roomModel->exitRoom($room, $playerID)) {
                LogMark::getInstance()->markError(
                    GameErrorCode::DATA_UPDATE_ERROR,
                    '[roomModel] cli check exit old room error!', (array)$room
                );
            }
            // 重置roomID
            $this->savePlayerRoomID(AccountData::DB_ROOM_ID_DEFAULT, $playerID);
        }

        if($roomID != $oldRoomID) {
            $oldRoom = $roomModel->checkPlayerInRoom($oldRoomID);
            if(!is_null($oldRoom)) {
                if(!$roomModel->exitRoom($oldRoom, $playerID)) {
                    LogMark::getInstance()->markError(
                        GameErrorCode::DATA_UPDATE_ERROR,
                        '[roomModel] cli check exit old room error!', (array)$oldRoom
                    );
                }
                // 重置roomID
                $this->savePlayerRoomID(AccountData::DB_ROOM_ID_DEFAULT, $playerID);
            }
        }
        return true;
    }

    //团队排位更新玩家选择位置
    public function syncPlayerPosition(PaiweiLevelData $paiWeiData)
    {
        //查找房间
        $roomId = $this->getAccountProperty($this->playerId, RoomData::DB_KEY_MAP[RoomData::ROOM_ID]);
        if (is_null($roomId)) {
            return;
        }
        /**
         * @var RoomModel $roomModel
         */
        $roomModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ROOM);
        $room = $roomModel->newRoom($roomId);
        if(!$room->searchRoomByRoomID()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomLogic] search room error!',
                (array)$room
            );
            return;
        }
        //查找位置
        $position = json_decode($paiWeiData->positionOrder, true)[0];
        //通知位置变化
        if ($roomModel->updateRoomPlayerPosition($room, $position)) {
            //发送消息
            $roomModel->addPacketGCUpdateRoomPlayerMessage($room->playerInfo);
        }
    }
}