<?php
/**
 * 自定义房间
 */
namespace Game\Model;

use Framework\Lib\Utils;
use Framework\Log\LogMark;
use Framework\Network\SendMessage;
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\TemplateDefine;
use Game\Data\RoomCustomizeData;
use Game\Logic\Pvp\PvpMatchLogic;
use Game\Protobuf\ClothData;
use Game\Protobuf\CRoomPlayer;
use Game\Protobuf\FindCRoomInfo;
use Game\Protobuf\GCChangeCRoomPlayerCar;
use Game\Protobuf\GCChangeCRoomPlayerState;
use Game\Protobuf\GCCRoomInfo;
use Game\Protobuf\GCExitCRoom;
use Game\Protobuf\GCGetCustomize;
use Game\Protobuf\GCJoinCRoom;
use Game\Protobuf\GCOwnerCRemovePlayer;
use Game\Protobuf\GCPasswordIsShow;
use Game\Protobuf\GCSyncCRoomPlayerInfo;
use Game\Protobuf\GCUpdateCRoomAI;
use Game\Protobuf\GCUpdateCRoomMapID;
use Game\Protobuf\GCUpdateCRoomModeType;
use Game\Protobuf\GCUpdateCRoomName;
use Game\Protobuf\GCUpdateCRoomPWD;
use Game\Protobuf\GCUpdateCRoomState;
use Game\Protobuf\GCUpdatePlayerTeam;
use Game\Protobuf\PacketId;
use Framework\Logic\PacketCacheLogic;
use Game\Data\AccountData;

class RoomCustomizeModel {
    use PlayerDBHandler;
    use PacketCacheLogic;
    use PvpMatchLogic;

    public const MODE_OPEN               = 0;  // 模式开启
    public const ROOM_STATE_PRIVATE      = 1;  // 私人
    public const ROOM_STATE_PUBLIC       = 2;  // 公开
    public const ROOM_GAME_READY         = 1;  // 准备中 （可加入）
    public const ROOM_GAME_MATCH_OR_PLAY = 2;  // 匹配中 & 游戏中 (不可加入)

    public const ROOM_PLAY_NOT_READY     = 1;  // 玩家未准备
    public const ROOM_PLAY_READY_REAL    = 2;  // 玩家准备中
    public const ROOM_PLAY_READY         = 3;  // 玩家已准备

    public const AI_POSITION             = 11; // 创建房间玩家位置


    // mode
    public const CUSTOMIZE_1 = 8;   // 单人模式
    public const CUSTOMIZE_2 = 9;   // 团队模式
    public const CUSTOMIZE_3 = 10;  // 练图模式

    // role 角色 房间内/观战
    public const IN_ROOM = 1;
    public const WATCH_ROOM = 2;
    public const WATCH_MAX = 3;
    public const PAGE_COUNT = 10;

    // 进入房间方式
    public const JOIN_FIND = 1;  // 搜索房间进入
    public const JOIN_LIST = 2;  // 房间列表进入

    public const ROOM_NAME_SUFFIX        = "的房间"; // 房间名称后缀
    public const ROOM_ID_MIN             = 1000;
    public const ROOM_ID_MAX             = 9999;
    public const COMMON_CONST_ROOM_ID    = 'RoomCustomizeID';
    public const CHECK_ROOM_TIME         = 30;  // 检测大于五分钟的房间进行处理
    public const CHECK_ROOM_PLAYER_TIME  = 120; // 检测有玩家 大于三十分钟

    public const TEAM_MAP = [   // 玩家所在战队与位置 11 一战队 1号位
        11,
        12,
        13,
        21,
        22,
        23,
    ];

    public const ROOM_LUA_PLAYER_EXISTS      = 5001;  // 玩家已存在
    public const ROOM_LUA_ROOM_NOT_EXISTS    = 5002;  // 房间不存在
    public const ROOM_LUA_ROOM_IS_MAX        = 5003;  // 房间已满员
    public const ROOM_LUA_PLAYER_NOT_EXISTS  = 5004;  // 玩家不存在
    public const ROOM_LUA_ROOM_EXISTS        = 5005;  // 房间已存在

    public  int                    $playerId;
    public  GCJoinCRoom            $joinRoomMsg;
    public  GCOwnerCRemovePlayer   $removePlayer;
    public  GCExitCRoom            $exitRoom;
    public  GCChangeCRoomPlayerCar $updateCar;
    private GCUpdatePlayerTeam     $updateTeam;

    public function __construct() {
        $this->joinRoomMsg   = new GCJoinCRoom();
        $this->removePlayer  = new GCOwnerCRemovePlayer();
        $this->exitRoom      = new GCExitCRoom();
        $this->updateCar     = new GCChangeCRoomPlayerCar();
        $this->updateTeam    = new GCUpdatePlayerTeam();
    }

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

    public function getPlayerID(): int {
        return $this->playerId;
    }

    public function onLoginExec() {
        if(!$this->searchOldRoomExit($this->playerId)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_CREATE_ERROR,
                '[roomModel] login exit customize room error!'
            );
        }
        // 玩家roomID重置
        $this->savePlayerRoomCustomizeID(AccountData::DB_CUSTOMIZE_ROOM_ID_DEFAULT);
    }

    // 创建房间
    public function createRoom(int $mapID, int $modeType, string $roomName, string $password): bool {
        //需要显示的玩家信息和赛车信息
        $showPlayer = $this->getMatchShowPlayer($this->playerId);
        $showCar = $this->getMatchShowCar($showPlayer[RoomCustomizeData::CAR_TPL_ID]);
        if (empty($showCar)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_CREATE_ERROR,
                '[roomModel] create customize room error!,not found default car',
                array(
                    "carId" => $showPlayer[AccountData::DB_DEFAULT_CAT_TPL]
                )
            );
            $this->GCJoinRoom(ClientErrorCode::ERROR_ROOM_JOIN_ERROR);
            return false;
        }
        $playerInfo = [
            $this->playerId => [
                RoomCustomizeData::PLAYER_ID   => (string)$this->playerId,
                RoomCustomizeData::CAR_TPL_ID  => $showPlayer[RoomCustomizeData::CAR_TPL_ID],
                RoomCustomizeData::JOIN_TIME   => Utils::getServerTimestamp(),
                RoomCustomizeData::READY_STATE => self::ROOM_PLAY_READY,
                //赛车信息
                RoomCustomizeData::EXTERIOR_REFIT => $showCar[RoomCustomizeData::EXTERIOR_REFIT],
                RoomCustomizeData::PROFICIENCY_LV => $showCar[RoomCustomizeData::PROFICIENCY_LV],
                RoomCustomizeData::PROFICIENCY => $showCar[RoomCustomizeData::PROFICIENCY],
                RoomCustomizeData::CAR_LV => $showCar[RoomCustomizeData::CAR_LV],
                //玩家信息
                RoomCustomizeData::GENDER => $showPlayer[RoomCustomizeData::GENDER],
                RoomCustomizeData::CLOTH => $showPlayer[RoomCustomizeData::CLOTH],
                RoomCustomizeData::HEAD => $showPlayer[RoomCustomizeData::HEAD],
                RoomCustomizeData::NICK_NAME => $showPlayer[RoomCustomizeData::NICK_NAME],
                RoomCustomizeData::POSITION => $showPlayer[RoomCustomizeData::POSITION],
                // 位置
                RoomCustomizeData::TEAM => self::AI_POSITION,
                RoomCustomizeData::ROLE => self::IN_ROOM,
            ]
        ];
        empty($password) ? $havePassword = false : $havePassword = true;
        $modeType === self::CUSTOMIZE_2 ? $ai = true : $ai = false;

        $room = new RoomCustomizeData(
            $this->makeRoomID(),
            $this->playerId,
            $roomName,
            self::ROOM_STATE_PRIVATE,
            $mapID,
            $modeType,
            $playerInfo,
            self::ROOM_GAME_READY,
//            self::WATCH_NUM,
            $havePassword,
            $password,
            $ai,
        );

        $checkRoomTime = $this->getTerm(TemplateDefine::TYPE_CONST,
            TemplateConst::Const_CRoom_Timeout1, "Const_num");

        $ret = $room->createCustomizeRoomDB($this->playerId, $showPlayer[RoomCustomizeData::CAR_TPL_ID],
            $checkRoomTime);

        if($ret == self::ROOM_LUA_ROOM_EXISTS) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_CREATE_ERROR,
                '[roomModel] create room bug room exists error!', (array)$room
            );
            $this->GCJoinRoom(ClientErrorCode::ERROR_ROOM_EXISTS);
            return false;
        }

        if($ret != 0) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_CREATE_ERROR,
                '[roomModel] create room error!', (array)$room
            );
            $this->GCJoinRoom(ClientErrorCode::ERROR_ROOM_JOIN_ERROR);
            return false;
        }

        // 检测room并退出
        if(!$this->searchOldRoomExit($this->playerId)) {
            $this->GCJoinRoom(ClientErrorCode::ERROR_EXIT_OLD_ROOM_FAILED);
            return false;
        }

        // 保存加入的roomID
        $this->savePlayerRoomCustomizeID($room->roomID);

        $this->GCJoinRoom(ClientErrorCode::CLIENT_SUCCESS, $room);

        $this->upRoleStatus(GameConstantDefine::PLAYER_STATUS_ROOM, $this->playerId);
        return true;
    }

    public function playerJoinRoom(RoomCustomizeData $room, int $invitedPlayer = 0): bool {
        //需要显示的玩家信息和赛车信息
        $showPlayer = $this->getMatchShowPlayer($this->playerId);
        $showCar = $this->getMatchShowCar($showPlayer[RoomCustomizeData::CAR_TPL_ID]);
        if (empty($showCar)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_CREATE_ERROR,
                '[roomModel] playerJoinRoom error!,not found default car',
                array(
                    "carId" => $showPlayer[AccountData::DB_DEFAULT_CAT_TPL]
                )
            );
            $this->GCJoinRoom(ClientErrorCode::ERROR_ROOM_JOIN_ERROR);
            return false;
        }

        $roomMax = $this->getTitle(TemplateDefine::TYPE_COMPETITION_MODE, $room->modeType)
        [TemplateCompetitionMode::RoomMax];
        $teamMap = self::TEAM_MAP;
        foreach($room->playerInfo as $player) {
            foreach (self::TEAM_MAP as $k => $i) {
                if($i == $player[RoomCustomizeData::TEAM])
                    unset($teamMap[$k]);
            }
        }
        foreach($teamMap as $t) {
            $team = $t;
            break;
        }
        $checkRoomTime = $this->getTerm(TemplateDefine::TYPE_CONST,
            TemplateConst::Const_CRoom_Timeout2, "Const_num");
        $ret = $room->joinCustomizeRoomDB($roomMax, $this->playerId, $showPlayer, $showCar, $team, $room->roomID
            , self::IN_ROOM, $checkRoomTime);

        // 房间不存在
        if($ret == self::ROOM_LUA_ROOM_NOT_EXISTS) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_SEARCH_ERROR,
                '[model] room not exists error!', (array)$room
            );
            $this->GCJoinRoom(ClientErrorCode::ERROR_ROOM_NOT_EXIST);
            return false;
        }
        // 已经在房间中 同步给其他人 进入时间刷新
        if($ret == self::ROOM_LUA_PLAYER_EXISTS) {
            LogMark::getInstance()->markInfo(
                '[model] player exists in room error!'
            );
            foreach($room->playerInfo as $pid => $item) {
                if($pid == $this->playerId) {
                    $item[RoomCustomizeData::JOIN_TIME] = Utils::getServerTimestamp();
                }
            }
            $room->saveDB();
            $this->GCJoinRoom(ClientErrorCode::ERROR_ROOM_PLAYER_IS_EXIST, $room);
            $this->GCSendAddJoinRoom();
            return false;
        }

        if($ret == self::ROOM_LUA_ROOM_IS_MAX) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[model] room num is max !', (array)$room
            );
            $this->GCJoinRoom(ClientErrorCode::ERROR_ROOM_IS_MAX_NOT_JOIN);
            return false;
        }

        if($ret != 0) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[model] update room error!', (array)$room
            );
            $this->GCJoinRoom(ClientErrorCode::ERROR_ROOM_JOIN_ERROR);
            return false;
        }

        // 检测room并退出
        if(!$this->searchOldRoomExit($this->playerId)) {
            $this->GCJoinRoom(ClientErrorCode::ERROR_EXIT_OLD_ROOM_FAILED);
            return false;
        }

        // 保存加入的roomID
        $this->savePlayerRoomCustomizeID($room->roomID);

        $room->searchRoomByRoomID();
        //记录邀请信息
//        $this->addInvitedPlayer($room, $invitedPlayer);
        // 通知客户端加入成功
        $this->GCJoinRoom(ClientErrorCode::CLIENT_SUCCESS, $room);
        $this->upRoleStatus(GameConstantDefine::PLAYER_STATUS_ROOM, $this->playerId);
        return true;
    }

    // 加入观战
    public function playerJoinWatchRoom(RoomCustomizeData $room): bool {

        $checkRoomTime = $this->getTerm(TemplateDefine::TYPE_CONST,
            TemplateConst::Const_CRoom_Timeout2, "Const_num");

        $ret = $room->joinWatchCustomizeRoomDB( $this->playerId, $room->roomID, self::WATCH_ROOM,
            self::WATCH_MAX, $checkRoomTime);

        // 房间不存在
        if($ret == self::ROOM_LUA_ROOM_NOT_EXISTS) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_SEARCH_ERROR,
                '[model] room not exists error!', (array)$room
            );
            $this->GCJoinRoom(ClientErrorCode::ERROR_ROOM_NOT_EXIST);
            return false;
        }
        // 已经在房间中 同步给其他人 进入时间刷新
        if($ret == self::ROOM_LUA_PLAYER_EXISTS) {
            LogMark::getInstance()->markInfo(
                '[model] player exists in room error!'
            );
            foreach($room->playerInfo as $pid => $item) {
                if($pid == $this->playerId) {
                    $item[RoomCustomizeData::JOIN_TIME] = Utils::getServerTimestamp();
                }
            }
            $room->saveDB();
            $this->GCJoinRoom(ClientErrorCode::ERROR_ROOM_PLAYER_IS_EXIST, $room);
            $this->GCSendAddJoinRoom();
            return false;
        }

        if($ret == self::ROOM_LUA_ROOM_IS_MAX) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[model] room num is max !', (array)$room
            );
            $this->GCJoinRoom(ClientErrorCode::ERROR_CUSTOMIZE_WATCH_MAX);
            return false;
        }

        if($ret != 0) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[model] update room error!', (array)$room
            );
            $this->GCJoinRoom(ClientErrorCode::ERROR_ROOM_JOIN_ERROR);
            return false;
        }
        $room->searchRoomByRoomID();
        $room->saveDB();
        // 通知客户端加入成功
        $this->GCJoinRoom(ClientErrorCode::CLIENT_SUCCESS, $room);
        $this->upRoleStatus(GameConstantDefine::PLAYER_STATUS_ROOM, $this->playerId);
        return true;
    }

    public function searchOldRoomExit($playerID = 0):bool {
        // 检测玩家是否在房间内
        if(isset($this->searchPlayerInfo($playerID)[AccountData::DB_ROOM_CUSTOMIZE_ID])) {
            $oldRoomID = $this->searchPlayerInfo($playerID)[AccountData::DB_ROOM_CUSTOMIZE_ID];
            $oldRoom = $this->checkPlayerInRoom($oldRoomID);
            if(!is_null($oldRoom)) {
                if(!$this->exitRoom($oldRoom)) {
                    LogMark::getInstance()->markError(
                        GameErrorCode::DATA_UPDATE_ERROR,
                        '[roomModel] exit old customize room  error!', (array)$oldRoom
                    );
                }
            }
        }
        return true;
    }

    public function checkPlayerInRoom(int $roomID):?RoomCustomizeData {
        if($roomID != AccountData::DB_CUSTOMIZE_ROOM_ID_DEFAULT) {
            // 退出房间
            $oldRoom = $this->newRoom($roomID);
            if(!$oldRoom->searchRoomByRoomID()) {
                return null;
            }
            return $oldRoom;
        }
        return null;
    }

    public function exitRoom(RoomCustomizeData $room, $playerID = 0):bool {

        if($playerID != 0) {
            $this->playerId = $playerID;
        }
        $role = 1;
        foreach ($room->playerInfo as $p => $data) {
            if($p == $this->playerId) {
                $role = $data[RoomCustomizeData::ROLE];
            }
        }

        $ret = $room->exitCustomizeRoomDB($room->roomID, $this->playerId, $role);

        if($ret == self::ROOM_LUA_ROOM_NOT_EXISTS) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[model] customize room not exists error!', (array)$room
            );
            $this->GCExitRoom(ClientErrorCode::ERROR_ROOM_NOT_EXIST, $room->roomID);
            return false;
        }
        if($ret == self::ROOM_LUA_PLAYER_NOT_EXISTS) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[model] customize player not exists error!', (array)$room
            );
            $this->GCExitRoom(ClientErrorCode::ERROR_ROOM_PLAYER_NOT_EXIST, $room->roomID);
            return false;
        }

        if($ret != 0) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[model] exit room error!', (array)$room
            );
            $this->GCExitRoom(ClientErrorCode::ERROR_ROOM_OPERATE_FAILED, $room->roomID);
            return false;
        }

        // 玩家roomID重置
        $this->savePlayerRoomCustomizeID(AccountData::DB_CUSTOMIZE_ROOM_ID_DEFAULT);        // 通知客户端退出成功
        $this->GCExitRoom(ClientErrorCode::CLIENT_SUCCESS, $room->roomID, $this->playerId);
        // 检查移除邀请信息
//        $this->removeInvitedPlayer($room, $this->playerId);
        $this->GCSendExitRoom();
        $this->GCSendAddExitRoom($room->playerInfo, $room->roomID);
        return true;
    }

    public function removeRoomPlayer(RoomCustomizeData $room, int $playerID): bool {
        if(!isset($room->playerInfo[$playerID])) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_SEARCH_ERROR,
                '[model] remove player but customize room not exist!', (array)$room
            );
            $this->GCRemovePlayer(ClientErrorCode::ERROR_ROOM_PLAYER_NOT_EXIST);
            return false;
        }
        unset($room->playerInfo[$playerID]);
        if(!$room->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[model] remove player save error!', (array)$room
            );
            $this->GCRemovePlayer(ClientErrorCode::ERROR_ROOM_OPERATE_FAILED);
            return false;
        }
        // 玩家roomID重置
        $this->savePlayerRoomCustomizeID(AccountData::DB_CUSTOMIZE_ROOM_ID_DEFAULT, $playerID);
        $this->GCRemovePlayer(ClientErrorCode::CLIENT_SUCCESS, $playerID);
        // 检查移除邀请信息
//        $this->removeInvitedPlayer($room, $playerID);
        return true;
    }

    public function getRoomListByPage(int $page): bool {
        $start = ($page - 1) * self::PAGE_COUNT;
        $end = $page * self::PAGE_COUNT - 1;
        $result = $this->getRoomListByModeAndScore();
        $rooms = array_keys($result);
        $gcCustomizeList = new GCGetCustomize();
        $roomInfos = [];
        for($i = $start; $i <= $end; $i ++) {
            if(isset($rooms[$i])) {
                $roomInfo = new FindCRoomInfo();
                $roomID = $rooms[$i];
                $roomData = new RoomCustomizeData($roomID);
                $roomData->searchRoomByRoomID();
                $room = new GCCRoomInfo();
                $room->setRoomID($roomData->roomID);
                $room->setRoomName($roomData->roomName);
                $room->setMapID($roomData->mapID);
                $room->setModeType($roomData->modeType);
                $room->setRoomState($roomData->roomState);
                $room->setAI($roomData->ai);
                $room->setHavePassword($roomData->havePassword);
                $room->setPlayType($roomData->playType);
                $room->setPassword($roomData->password);
                $room->setPwdIsShow($roomData->isShowPwd);
                $roomInfo->setRoom($room);
                $players = array();
                foreach($roomData->playerInfo as $pid => $info) {
                    $msgPlayer = new CRoomPlayer();
                    $msgPlayer->setHead($info[RoomCustomizeData::HEAD]);
                    $msgPlayer->setPlayerID($info[RoomCustomizeData::PLAYER_ID]);
                    $msgPlayer->setRole($info[RoomCustomizeData::ROLE]);
                    $players[] = $msgPlayer;
                }
                $roomInfo->setPlayers($players);
                $roomInfos[] = $roomInfo;
            }
        }
        $gcCustomizeList->setCode(0);
        $gcCustomizeList->setPage($page);
        $gcCustomizeList->setData($roomInfos);
        SendMessage::getInstance()->sendClient(PacketId::GC_GetCustomize, $gcCustomizeList);
        return true;
    }

    // 通过分数范围查找房间号
    public function getRoomListByModeAndScore():array {
        $result = array();
        if(!$this->searchRank(DBTableDefine::TABLE_ROOM_CUSTOMIZE,0,
            1000000, 2000000, $result)) {
            return array();
        }
        return $result;
    }

    //获取当前离线的玩家
    public function getCheckRoomByScore(&$ret): bool
    {
        $end = Utils::getServerTimestamp();
        $start = 1;
        if (false === $this->searchRank(
                DBTableDefine::TABLE_ROOM_CUSTOMIZE_TIME,
                0,
                $start,
                $end,
                $ret
            )){
            LogMark::getInstance()->markError(
                GameErrorCode::ROOM_SEARCH_OFFLINE_ERROR,
                "[RoomModel] search customize room off-line error!",
            );
            return false;
        }
        return true;
    }

    // 创建房间id
    private function makeRoomID():int {
        $roomID = 0;
        $this->autoAddTermByRoomID(
            $roomID,
            DBTableDefine::TABLE_COMMON_CONST,
            0,
            self::COMMON_CONST_ROOM_ID,
            1
        );
        if($roomID < self::ROOM_ID_MIN || $roomID > self::ROOM_ID_MAX) {
            $this->updateTitle(
                DBTableDefine::TABLE_COMMON_CONST,
                0,
                0,
                [self::COMMON_CONST_ROOM_ID => self::ROOM_ID_MIN]
            );
            $roomID = self::ROOM_ID_MIN;
        }

        // roomID 判断是否已经存在 存在则查询出房间 退出所有玩家
        $room = $this->newRoom($roomID);
        if($room->searchRoomByRoomID()) {
            $playerList = $room->playerInfo;
            if(!empty($playerList)) {
                foreach($playerList as $pid => $info) {
                    $this->searchOldRoomExit($pid);
                    $this->savePlayerRoomCustomizeID(AccountData::DB_CUSTOMIZE_ROOM_ID_DEFAULT, $pid);
                }
            }
        }
        return $roomID;
    }

    public function newRoom(int $roomID): RoomCustomizeData {
        return new RoomCustomizeData($roomID);
    }

    // 保存地图
    public function saveRoomMap(RoomCustomizeData $room, int $mapID): bool {
        $room->setMapID($mapID);
        if(!$room->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[model] update room map error!', (array)$room
            );
            return false;
        }
        return true;
    }

    // 房主更换模式
    public function saveRoomMode(RoomCustomizeData $room, int $mode): bool {
        $room->setModeType($mode);
        if(!$room->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[model] update room mode error!', (array)$room
            );
            return false;
        }
        return true;
    }

    // 房主更换房间名称
    public function saveRoomName(RoomCustomizeData $room, string $name): bool {
        $room->setRoomName($name);
        if(!$room->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[model] update room name error!', (array)$room
            );
            return false;
        }
        return true;
    }

    // 修改ai设置
    public function saveRoomAI(RoomCustomizeData $room, bool $ai): bool {
        $room->setAI($ai);
        if(!$room->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[model] update room ai error!', (array)$room
            );
            return false;
        }
        return true;
    }

    // 修改ai设置
    public function saveRoomPassword(RoomCustomizeData $room, string $passward): bool {
        $room->setPassword($passward);
        empty($passward) ? $room->setHavePassword(false) : $room->setHavePassword(true);
        if(!$room->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[model] update room password error!', (array)$room
            );
            return false;
        }
        return true;
    }

    // 房主更换房间状态 私人/公开
    public function saveRoomState(RoomCustomizeData $room, int $state): bool {
        $room->setRoomState($state);
        if(!$room->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[model] update room state error!', (array)$room
            );
            return false;
        }

        return true;
    }

    // 房主更换密码可见
    public function saveRoomPwdIsShow(RoomCustomizeData $room, bool $state): bool {
        $room->setIsShowPwd($state);
        if(!$room->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[model] update room pwd is show error!', (array)$room
            );
            return false;
        }

        return true;
    }

    // 玩家换车
    public function savePlayerCar(RoomCustomizeData $room, int $carID, int $carUID): bool {
        $showCar = $this->getMatchShowCar($carID);
        if (empty($showCar)) {
            LogMark::getInstance()->markDebug(
                '[RoomModel] player car not found!',
                array(
                    "carId" => $carID
                )
            );
            return false;
        }
        $players = $room->playerInfo;
        if(!isset($players[$this->playerId])) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[model] save car but room not found player info!'
            );
            return false;
        }
        $room->setPlayerCar($players[$this->playerId], $carID, $showCar);
        $room->playerInfo = $players;
        if(!$room->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[model] update player car error!', (array)$room
            );
            return false;
        }
        $this->GCAddChangePlayerCar(ClientErrorCode::CLIENT_SUCCESS, $this->playerId, $carID, $showCar);
        return true;
    }


    // 玩家更换状态
    public function savePlayerState(RoomCustomizeData $room, int $state): bool {
        $players = $room->playerInfo;
        if(!isset($players[$this->playerId])) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[model] save state but room not found player info!'
            );
            return false;
        }
        $room->setPlayerState($players[$this->playerId], $state);
        $room->playerInfo = $players;
        if(!$room->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[model] update player state error!', (array)$room
            );
            return false;
        }
        return true;
    }

    // 玩家更换位置
    public function savePlayerTeam(RoomCustomizeData $room, int $team): bool {
        $players = $room->playerInfo;
        if(!isset($players[$this->playerId])) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[model] save team but room not found player info!'
            );
            return false;
        }
        $room->setPlayerTeam($players[$this->playerId], $team);
        $room->playerInfo = $players;
        if(!$room->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[model] update player team error!', (array)$room
            );
            return false;
        }
        return true;
    }


    //--------------------------------------- GC MSG ----------------------------------------------------------//

    public function GCJoinRoom(int $code, RoomCustomizeData $roomData = null) {
        $this->joinRoomMsg->setCode($code);
        if(!is_null($roomData)) {
            $room = new GCCRoomInfo();
            $players = [];   // 单条玩家数据
            $room->setRoomID($roomData->roomID)
                ->setRoomName($roomData->roomName)
                ->setMapID($roomData->mapID)
                ->setModeType($roomData->modeType)
                ->setRoomState($roomData->roomState)
                ->setPlayType($roomData->playType)
                ->setAI($roomData->ai)
                ->setPassword($roomData->password)
                ->setPwdIsShow($roomData->isShowPwd)
                ->setHavePassword($roomData->havePassword)
                ->setChatID($roomData->chatID);
            $this->joinRoomMsg->setRoom($room);
            foreach($roomData->playerInfo as $id => $data) {
                $tmp = new CRoomPlayer();
                $clothData = new ClothData();
                $clothData->mergeFromJsonString($data[RoomCustomizeData::CLOTH]);
                $tmp->setPlayerID($data[RoomCustomizeData::PLAYER_ID])
                    ->setCarTplID($data[RoomCustomizeData::CAR_TPL_ID])
                    ->setJoinTime($data[RoomCustomizeData::JOIN_TIME])
                    ->setState($data[RoomCustomizeData::READY_STATE])
                    ->setHead($data[RoomCustomizeData::HEAD])
                    ->setNickName($data[RoomCustomizeData::NICK_NAME])
                    ->setGender($data[RoomCustomizeData::GENDER])
                    ->setExteriorRefit($data[RoomCustomizeData::EXTERIOR_REFIT])
                    ->setCloth($clothData)
                    ->setProficiencyLv($data[RoomCustomizeData::PROFICIENCY_LV])
                    ->setProficiency($data[RoomCustomizeData::PROFICIENCY])
                    ->setTeam($data[RoomCustomizeData::TEAM])
                    ->setRole($data[RoomCustomizeData::ROLE])
                    ->setCarLv($data[RoomCustomizeData::CAR_LV]);
                $players[] = $tmp;
            }
            $this->joinRoomMsg->setPlayers($players);
            //修改玩家的状态
        }
    }

    public function GCSendJoinRoom() {
        if(!empty($this->joinRoomMsg)) {
            SendMessage::getInstance()->sendClient(PacketId::GC_JoinCRoom, $this->joinRoomMsg);
        }
    }

    public function GCSendAddJoinRoom() {
        $players = $this->joinRoomMsg->getPlayers();
        $syncPlayer = new GCSyncCRoomPlayerInfo();
        $syncPlayer->setCode(ClientErrorCode::CLIENT_SUCCESS);

        foreach($players as $p) {
            if($p->getPlayerID() == $this->playerId) {
                $syncPlayer->setPlayer($p);
            }
        }
        foreach ($players as $p) {
            $this->addPacket(PacketId::GC_SyncCRoomPlayerInfo,  $syncPlayer, $p->getPlayerID());
        }
    }

    public function GCRemovePlayer(int $code, int $playerID = 0) {
        $this->removePlayer->setCode($code);
        if($playerID != 0) {
            $this->removePlayer->setPlayerID($playerID);
        }
    }

    public function GCSendRemovePlayer() {
        if(!empty($this->removePlayer)) {
            SendMessage::getInstance()->sendClient(PacketId::GC_OwnerCRemovePlayer, $this->removePlayer);
        }
    }

    public function GCSendAddRemovePlayer(array $players) {
        $this->removePlayer->setCode(ClientErrorCode::CLIENT_SUCCESS);
        if(!empty($players)) {
            foreach ($players as $id => $p) {
                if($id == $this->playerId) {
                    continue;
                }
                $this->removePlayer->setPlayerID($this->removePlayer->getPlayerID());
                $this->addPacket(PacketId::GC_OwnerCRemovePlayer,  $this->removePlayer, $id);
            }
        }
    }

    public function GCUpdateRoomMap(int $code) {
        $roomMsg = new GCUpdateCRoomMapID();
        $roomMsg->setCode($code);
        SendMessage::getInstance()->sendClient(PacketId::GC_UpdateCRoomMapID, $roomMsg);
    }

    public function GCAddUpdateRoomMap(int $code, int $mapID, int $playerID) {
        $roomMsg = new GCUpdateCRoomMapID();
        $roomMsg->setCode($code);
        $roomMsg->setMapID($mapID);
        $this->addPacket(PacketId::GC_UpdateCRoomMapID, $roomMsg, $playerID);
    }

    public function GCUpdateRoomAI(int $code) {
        $roomMsg = new GCUpdateCRoomAI();
        $roomMsg->setCode($code);
        SendMessage::getInstance()->sendClient(PacketId::GC_UpdateCRoomAI, $roomMsg);
    }

    public function GCAddUpdateRoomAI(int $code, bool $ai, int $playerID) {
        $roomMsg = new GCUpdateCRoomAI();
        $roomMsg->setCode($code);
        $roomMsg->setAI($ai);
        $this->addPacket(PacketId::GC_UpdateCRoomAI, $roomMsg, $playerID);
    }

    public function GCUpdateRoomPassword(int $code) {
        $roomMsg = new GCUpdateCRoomPWD();
        $roomMsg->setCode($code);
        SendMessage::getInstance()->sendClient(PacketId::GC_UpdateCRoomPWD, $roomMsg);
    }

    public function GCAddUpdateRoomPassword(int $code, string $password, int $playerID) {
        $roomMsg = new GCUpdateCRoomPWD();
        $roomMsg->setCode($code);
        $roomMsg->setPassword($password);
        $this->addPacket(PacketId::GC_UpdateCRoomPWD, $roomMsg, $playerID);
    }

    public function GCUpdateRoomMode(int $code, $mode = 0) {
        $roomMsg = new GCUpdateCRoomModeType();
        $roomMsg->setCode($code);
        $roomMsg->setModeType($mode);
        SendMessage::getInstance()->sendClient(PacketId::GC_UpdateCRoomModeType, $roomMsg);
    }

    public function GCAddUpdateRoomMode(int $code, int $mode, int $playerID) {
        $roomMsg = new GCUpdateCRoomModeType();
        $roomMsg->setCode($code);
        $roomMsg->setModeType($mode);
        $this->addPacket(PacketId::GC_UpdateCRoomModeType, $roomMsg, $playerID);
    }

    public function GCUpdateRoomName(int $code) {
        $roomMsg = new GCUpdateCRoomName();
        $roomMsg->setCode($code);
        SendMessage::getInstance()->sendClient(PacketId::GC_UpdateCRoomName, $roomMsg);
    }

    public function GCAddUpdateRoomName(int $code, string $name, int $playerID) {
        $roomMsg = new GCUpdateCRoomName();
        $roomMsg->setCode($code);
        $roomMsg->setRoomName($name);
        $this->addPacket(PacketId::GC_UpdateCRoomName, $roomMsg, $playerID);
    }

    public function GCUpdateRoomState(int $code) {
        $roomMsg = new GCUpdateCRoomState();
        $roomMsg->setCode($code);
        SendMessage::getInstance()->sendClient(PacketId::GC_UpdateCRoomState, $roomMsg);
    }

    public function GCAddUpdateRoomState(int $code, int $state, int $playerID) {
        $roomMsg = new GCUpdateCRoomState();
        $roomMsg->setCode($code);
        $roomMsg->setState($state);
        $this->addPacket(PacketId::GC_UpdateCRoomState, $roomMsg, $playerID);
    }

    public function GCUpdateRoomIsShowPwd(int $code) {
        $roomMsg = new GCPasswordIsShow();
        $roomMsg->setCode($code);
        SendMessage::getInstance()->sendClient(PacketId::GC_PassWordIsShow, $roomMsg);
    }

    public function GCAddUpdateRoomIsShowPwd(int $code, int $state, int $playerID) {
        $roomMsg = new GCPasswordIsShow();
        $roomMsg->setCode($code);
        $roomMsg->setIsShow($state);
        $this->addPacket(PacketId::GC_PassWordIsShow, $roomMsg, $playerID);
    }

    public function GCChangePlayerCar(int $code) {
        $roomMsg = new GCChangeCRoomPlayerCar();
        $roomMsg->setCode($code);
        SendMessage::getInstance()->sendClient(PacketId::GC_ChangeCRoomPlayerCar, $roomMsg);
    }

    public function GCAddChangePlayerCar(int $code, int $changePlayerID,int $carID, array $showCar) {
        $this->updateCar->setCode($code);
        $this->updateCar->setPlayerID($changePlayerID);
        $this->updateCar->setCarTplID($carID);
        $this->updateCar->setExteriorRefit($showCar[RoomCustomizeData::EXTERIOR_REFIT]);
        $this->updateCar->setProficiencyLv($showCar[RoomCustomizeData::PROFICIENCY_LV]);
        $this->updateCar->setProficiency($showCar[RoomCustomizeData::PROFICIENCY]);
        $this->updateCar->setCarLv($showCar[RoomCustomizeData::CAR_LV]);
    }

    public function GCChangePlayerTeam(int $code) {
        $roomMsg = new GCUpdatePlayerTeam();
        $roomMsg->setCode($code);
        SendMessage::getInstance()->sendClient(PacketId::GC_UpdatePlayerTeam, $roomMsg);
    }

    public function GCAddChangePlayerTeam(int $code, int $changePlayerID,int $team, int $playerID) {
        $roomMsg = new GCUpdatePlayerTeam();
        $roomMsg->setCode($code);
        $roomMsg->setPlayerID($changePlayerID);
        $roomMsg->setTeam($team);
        $this->addPacket(PacketId::GC_UpdatePlayerTeam, $roomMsg, $playerID);
    }

    public function GCSendAddChangePlayerCar(int $playerID) {
        if(!empty($this->updateCar)) {
            $this->addPacket(PacketId::GC_ChangeCRoomPlayerCar, $this->updateCar, $playerID);
        }
    }

    public function GCChangePlayerState(int $code) {
        $roomMsg = new GCChangeCRoomPlayerState();
        $roomMsg->setCode($code);
        SendMessage::getInstance()->sendClient(PacketId::GC_ChangeCRoomPlayerState, $roomMsg);
    }

    public function GCAddChangePlayerState(int $code, int $changePlayerID, int $state, int $playerID) {
        $roomMsg = new GCChangeCRoomPlayerState();
        $roomMsg->setCode($code);
        $roomMsg->setPlayerID($changePlayerID);
        $roomMsg->setState($state);
        $this->addPacket(PacketId::GC_ChangeCRoomPlayerState, $roomMsg, $playerID);
    }

    public function GCExitRoom(int $code, int $roomID = 0, int $exitID = 0) {
        $this->exitRoom->setCode($code);
        $this->exitRoom->setRoomID($roomID);
        if($exitID != 0) {
            $this->exitRoom->setPlayerID($exitID);
        }
    }

    public function GCSendExitRoom() {
        if(!empty($this->exitRoom)) {
            SendMessage::getInstance()->sendClient(PacketId::GC_ExitCRoom, $this->exitRoom);
        }
    }

    public function GCSendAddExitRoom(array $players, $roomID) {
        $this->exitRoom->setCode(ClientErrorCode::CLIENT_SUCCESS);
        $this->exitRoom->setRoomID($roomID);
        if(!empty($players)) {
            foreach ($players as $id => $p) {
                if($id == $this->playerId) {
                    continue;
                }
                $this->exitRoom->setPlayerID($this->exitRoom->getPlayerID());
                $this->addPacket(PacketId::GC_ExitCRoom,  $this->exitRoom, $id);
            }
        }
    }

    //更新玩家选择位置
    public function updatePlayerTeam(RoomCustomizeData $room, int $team): bool
    {
        $players = $room->playerInfo;
        if(!isset($players[$this->playerId])) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomLogic] customize room not found player info!'
            );
            return false;
        }
        $room->setPlayerTeam($players[$this->playerId], $team);
        $room->playerInfo = $players;
        if(!$room->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomLogic] customize room update player error!',
                (array)$room
            );
            return false;
        }
        $this->addGCUpdateRoomPlayerMessage($this->playerId, $players[$this->playerId]);
        return true;
    }

    //更新玩家数据
    private function addGCUpdateRoomPlayerMessage(int $playerId, array $roomPlayer)
    {
        $this->updateTeam->setPlayerID($playerId);
        $this->updateTeam->setTeam($roomPlayer[RoomCustomizeData::TEAM]);
    }

    public function addPacketUpdatePlayerTeamMessage(array $players)
    {
        foreach ($players as $pid => $pInfo) {
            if($pid == $this->playerId) {
                continue;
            }
            $this->addPacket(PacketId::GC_UpdatePlayerTeam,  $this->updateTeam, $pid);
        }
    }
}