<?php
/**
 * 房间内信息修改类
 */
namespace Game\Logic\Room;

use Framework\Log\LogMark;
use Framework\MVC\ModelManager;
use Framework\Network\SendMessage;
use Framework\Logic\PacketCacheLogic;
use Framework\Logic\TemplateHelp;
use Game\Constant\ClientErrorCode;
use Game\Constant\ConstTemplate\TemplateCompetitionMode;
use Game\Constant\GameConstantDefine;
use Game\Constant\GameErrorCode;
use Game\Constant\ModelTypeDefine;
use Game\Constant\TemplateDefine;
use Game\Data\AccountData;
use Game\Data\RoomData;
use Game\Logic\Activity\ScuffleLogic;
use Game\Logic\CarExteriorRefitLogic;
use Game\Logic\CarLogic;
use Game\Logic\GenerateAiLogic;
use Game\Model\RoomModel;
use Game\Protobuf\ClothData;
use Game\Protobuf\GCRankJoinScene;
use Game\Protobuf\GCRankReadyState;
use Game\Protobuf\GCRankTeamRecord;
use Game\Protobuf\GCUpdateRoomPlayerCloth;
use Game\Protobuf\PacketId;

trait RoomInfoLogic {
    use TemplateHelp;
    use CarLogic;
    use PacketCacheLogic;
    use GenerateAiLogic;
    use ScuffleLogic;
    use CarExteriorRefitLogic;

    public int $playerID;
    // 房主更换地图
    public function updateRoomMap(int $roomID, int $mapID): bool {
        /**
         * @var RoomModel $roomModel
         */
        $roomModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ROOM);
        $this->playerID = $roomModel->getPlayerID();

        $room = $roomModel->newRoom($roomID);
        if(!$room->searchRoomByRoomID()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomInfoLogic] change map search room error!', (array)$room
            );
            $roomModel->GCUpdateRoomMap(ClientErrorCode::ERROR_ROOM_NOT_EXIST);
            return false;
        }

        $playerList = $room->playerInfo;
        if(!$this->isOwner($playerList)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[roomInfoLogic] not permission change map!', (array)$room
            );
            $roomModel->GCUpdateRoomMap(ClientErrorCode::ERROR_NOT_PERMISSION);
            return false;
        }

        if($room->mapID == $mapID) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomInfoLogic] not repeat select map!', (array)$room
            );
            $roomModel->GCUpdateRoomMap(ClientErrorCode::ERROR_ROOM_NOT_REPEAT_CHANGE);
            return false;
        }

        $mapCfg = $this->getTitle(TemplateDefine::TYPE_PVP_MAP, $mapID);
        if(is_null($mapCfg)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomInfoLogic] map not exist!', ['mapID' => $mapID]
            );
            $roomModel->GCUpdateRoomMap(ClientErrorCode::ERROR_ROOM_MAP_NOT_EXIST);
            return false;
        }

        // TODO: 地图是否开启预留

        if(!$roomModel->saveRoomMap($room, $mapID)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomInfoLogic] update room map error!', (array)$room
            );
            $roomModel->GCUpdateRoomMap(ClientErrorCode::ERROR_ROOM_OPERATE_FAILED);
            return false;
        }

        foreach($room->playerInfo as $id => $p) {
            $roomModel->GCAddUpdateRoomMap(ClientErrorCode::CLIENT_SUCCESS, $mapID, $id);
        }
        return true;
    }

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

    // 房主更换模式
    public function updateRoomMode(int $roomID, int $mode): bool {
        /**
         * @var RoomModel $roomModel
         */
        $roomModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ROOM);
        $this->playerID = $roomModel->getPlayerID();

        $modeCfg = $this->getTitle(TemplateDefine::TYPE_COMPETITION_MODE, $mode);
        if(is_null($modeCfg)) {
            logMark::getInstance()->markError(
                GameErrorCode::SEARCH_CONFIG_INFO_FAILED,
                '[RoomInfoLogic] update room mode search mode cfg error!',
                ['modeType' => $mode]
            );
            $roomModel->GCUpdateRoomMode(ClientErrorCode::ERROR_ROOM_MODE_NOT_EXIST);
            return false;
        }

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

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

        $room = $roomModel->newRoom($roomID);
        if(!$room->searchRoomByRoomID()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomInfoLogic] change mode search room error!', (array)$room
            );
            $roomModel->GCUpdateRoomMode(ClientErrorCode::ERROR_ROOM_NOT_EXIST);
            return false;
        }

        $playerList = $room->playerInfo;
        if(!$this->isOwner($playerList)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[roomInfoLogic] not permission change mode!', (array)$room
            );
            $roomModel->GCUpdateRoomMap(ClientErrorCode::ERROR_NOT_PERMISSION);
            return false;
        }

        if($room->modeType == $mode) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomInfoLogic] not repeat select mode!', (array)$room
            );
            $roomModel->GCUpdateRoomMode(ClientErrorCode::ERROR_ROOM_NOT_REPEAT_CHANGE);
            return false;
        }

        if(!$roomModel->saveRoomMode($room, $mode)) {
            logMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomInfoLogic] owner change mode error!',
                ['roomID' => $roomID, 'mode' => $mode]
            );
            $roomModel->GCUpdateRoomMode(ClientErrorCode::ERROR_ROOM_OPERATE_FAILED);
            return false;
        }

        foreach($room->playerInfo as $id => $p) {
            $roomModel->GCAddUpdateRoomMode(ClientErrorCode::CLIENT_SUCCESS, $mode, $id);
        }
        return true;
    }

    // 房主更换房间名称
    public function updateRoomName(int $roomID, string $name): bool {
        /**
         * @var RoomModel $roomModel
         */
        $roomModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ROOM);
        $this->playerID = $roomModel->getPlayerID();

        $room = $roomModel->newRoom($roomID);
        if(!$room->searchRoomByRoomID()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomInfoLogic] change name search room error!', (array)$room
            );
            $roomModel->GCUpdateRoomName(ClientErrorCode::ERROR_ROOM_NOT_EXIST);
            return false;
        }

        $playerList = $room->playerInfo;
        if(!$this->isOwner($playerList)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[roomInfoLogic] not permission change room name!', (array)$room
            );
            $roomModel->GCUpdateRoomMap(ClientErrorCode::ERROR_NOT_PERMISSION);
            return false;
        }

        if($room->roomName == $name) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomInfoLogic] not repeat select name!', (array)$room
            );
            $roomModel->GCUpdateRoomName(ClientErrorCode::ERROR_ROOM_NOT_REPEAT_CHANGE);
            return false;
        }

        if(!$roomModel->saveRoomName($room, $name)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomInfoLogic] update room name error!', (array)$room
            );
            $roomModel->GCUpdateRoomName(ClientErrorCode::ERROR_ROOM_OPERATE_FAILED);
            return false;
        }

        foreach($room->playerInfo as $id => $p) {
            $roomModel->GCAddUpdateRoomName(ClientErrorCode::CLIENT_SUCCESS, $name, $id);
        }
        return true;
    }

    // 房主更换房间状态
    public function updateRoomState(int $roomID, int $state): bool {
        /**
         * @var RoomModel $roomModel
         */
        $roomModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ROOM);
        $this->playerID = $roomModel->getPlayerID();

        $room = $roomModel->newRoom($roomID);
        if(!$room->searchRoomByRoomID()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomInfoLogic] change room state search room error!', (array)$room
            );
            $roomModel->GCUpdateRoomState(ClientErrorCode::ERROR_ROOM_NOT_EXIST);
            return false;
        }

        $playerList = $room->playerInfo;
        if(!$this->isOwner($playerList)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[roomInfoLogic] not permission change room state!', (array)$room
            );
            $roomModel->GCUpdateRoomMap(ClientErrorCode::ERROR_NOT_PERMISSION);
            return false;
        }

        if($room->roomState == $state) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomInfoLogic] not repeat select name!', (array)$room
            );
            $roomModel->GCUpdateRoomState(ClientErrorCode::ERROR_ROOM_NOT_REPEAT_CHANGE);
            return false;
        }

        if(!$roomModel->saveRoomState($room, $state)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomInfoLogic] update room state error!', (array)$room
            );
            $roomModel->GCUpdateRoomState(ClientErrorCode::ERROR_ROOM_OPERATE_FAILED);
            return false;
        }

        foreach($room->playerInfo as $id => $p) {
            $roomModel->GCAddUpdateRoomState(ClientErrorCode::CLIENT_SUCCESS, $state, $id);
        }
        return true;
    }

    // 判断是否是房主
    public function isOwner(array $playerList): bool {
        array_multisort(array_column($playerList, RoomData::JOIN_TIME), SORT_ASC, $playerList);
        return $playerList[0][RoomData::PLAYER_ID] == $this->playerID;
    }

    // 玩家换车
    public function updatePlayerCar(int $roomID, $carID, $carUID): bool {
        /**
         * @var RoomModel $roomModel
         */
        $roomModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ROOM);
        $this->playerID = $roomModel->getPlayerID();

        $room = $roomModel->newRoom($roomID);
        if(!$room->searchRoomByRoomID()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomInfoLogic] change player car search room error!', (array)$room
            );
            $roomModel->GCChangePlayerCar(ClientErrorCode::ERROR_ROOM_NOT_EXIST);
            return false;
        }

        if(!$roomModel->savePlayerCar($room, $carID, $carUID)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomInfoLogic] update player car error!', (array)$room
            );
            $roomModel->GCChangePlayerCar(ClientErrorCode::ERROR_ROOM_OPERATE_FAILED);
            return false;
        }
        $code = ClientErrorCode::CLIENT_SUCCESS;
        if(!$roomModel->replacePlayerCar($carUID, $code)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomInfoLogic] player car replace error!',
                ['tplID' => $carID, 'carUID' => $carUID]
            );
            $roomModel->GCChangePlayerCar($code);
            return false;
        }

//        $roomModel->GCChangePlayerCar(ClientErrorCode::CLIENT_SUCCESS);
        foreach($room->playerInfo as $id => $p) {
//            if($p[RoomData::PLAYER_ID] == $this->playerID) {
//                continue;
//            }
            $roomModel->GCSendAddChangePlayerCar($id);
        }
        return true;
    }

    // 团队排位准备阶段玩家换车
    public function rankReplaceCar(int $roomID, $carID, $carUID, $players): bool {
        /**
         * @var RoomModel $roomModel
         */
        $roomModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ROOM);
        $this->playerID = $roomModel->getPlayerID();

        $room = $roomModel->newRoom($roomID);
        if(!$room->searchRoomByRoomID()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomInfoLogic] change player car search room error!', (array)$room
            );
            $roomModel->GCChangePlayerCar(ClientErrorCode::ERROR_ROOM_NOT_EXIST);
            return false;
        }

        if(!$roomModel->saveTeamRankCar($room, $carID, $carUID)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomInfoLogic] team rank update player car error!', (array)$room
            );
            $roomModel->GCChangePlayerCar(ClientErrorCode::ERROR_ROOM_OPERATE_FAILED);
            return false;
        }
        $code = ClientErrorCode::CLIENT_SUCCESS;
        if(!$roomModel->replacePlayerCar($carUID, $code)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomInfoLogic] player car replace error!',
                ['tplID' => $carID, 'carUID' => $carUID]
            );
            $roomModel->GCChangePlayerCar($code);
            return false;
        }
        LogMark::getInstance()->markInfo(
            "[PVPFlowLog][52][RoomInfoLogic] rankReplaceCar"
        );
        $roomModel->GCRankChangeCar(ClientErrorCode::CLIENT_SUCCESS);
        LogMark::getInstance()->markInfo(
            "[PVPFlowLog][53][RoomInfoLogic] rankReplaceCar sync other player"
        );
        foreach($players as $pid) {
            if ($this->checkIsAIPlayer($pid)) {
                continue;
            }
            $roomModel->GCSendRankCar($pid);
        }

        return true;
    }

    // 团队排位准备阶段玩家换车改装
    public function rankReplaceCarExteriorRefit(int $roomID, $players): bool {
        /**
         * @var RoomModel $roomModel
         */
        $roomModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ROOM);
        $this->playerID = $roomModel->getPlayerID();

        $room = $roomModel->newRoom($roomID);
        if(!$room->searchRoomByRoomID()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomInfoLogic] change player car exterior refit search room error!', (array)$room
            );
            $roomModel->GCRankChangeCarER(ClientErrorCode::ERROR_ROOM_NOT_EXIST);
            return false;
        }

        $carUID = $this->getAccountAttribute(AccountData::DB_DEFAULT_CAR);
        $carExteriorRefit = $this->getCarExteriorRefitFormatData($carUID);
        $room->playerInfo[$this->playerID][RoomData::EXTERIOR_REFIT] =  $carExteriorRefit;
        $room->saveDB();

        $roomModel->GCAddRankChangeCarER(ClientErrorCode::CLIENT_SUCCESS, $this->playerID, $carExteriorRefit);

        foreach($players as $pid) {
            if ($this->checkIsAIPlayer($pid)) {
                continue;
            }
            $roomModel->GCSendRankCarER($pid);
        }
        return true;
    }

    // 玩家修改状态
    public function updatePlayerState(int $roomID, $state): bool {
        /**
         * @var RoomModel $roomModel
         */
        $roomModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ROOM);
        $this->playerID = $roomModel->getPlayerID();

        $room = $roomModel->newRoom($roomID);
        if(!$room->searchRoomByRoomID()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomInfoLogic] change player state search room error!', (array)$room
            );
            $roomModel->GCChangePlayerState(ClientErrorCode::ERROR_ROOM_NOT_EXIST);
            return false;
        }

        if(!$roomModel->savePlayerState($room, $state)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomInfoLogic] update player state error!', (array)$room
            );
            $roomModel->GCChangePlayerState(ClientErrorCode::ERROR_ROOM_OPERATE_FAILED);
            return false;
        }
        foreach($room->playerInfo as $id => $p) {
            $roomModel->GCAddChangePlayerState(ClientErrorCode::CLIENT_SUCCESS, $this->playerID, $state, $id);
        }
        return true;
    }

    //匹配失败,修改房间玩家状态
    public function changeRoomPlayerToReady(int $roomId): bool
    {
        /**
         * @var RoomModel $roomModel
         */
        $roomModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ROOM);
        $room = $roomModel->newRoom($roomId);
        if(!$room->searchRoomByRoomID()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomInfoLogic] change player state search room error!', (array)$room
            );
            $roomModel->GCChangePlayerState(ClientErrorCode::ERROR_ROOM_NOT_EXIST);
            return false;
        }
        $playerArr = array();
        foreach ($room->playerInfo as $pid => $player) {
            $player[RoomData::READY_STATE] = RoomModel::ROOM_GAME_READY;
            $playerArr[$pid] = $player;
        }
        $room->playerInfo = $playerArr;
        $room->saveDB();
        foreach($room->playerInfo as $id => $p) {
            $roomModel->GCAddChangePlayerState(ClientErrorCode::CLIENT_SUCCESS, $this->playerId, RoomModel::ROOM_GAME_READY, $id);
        }
        return true;
    }

    // 修改团队排位玩家状态并同步给其他人
    public function changePlayerState(int $state, $players, int $AIId): bool {
        LogMark::getInstance()->markInfo(
            "[PVPFlowLog][50][RoomInfoLogic] changePlayerState"
        );
        /**
         * @var RoomModel $roomModel
         */
        $roomModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ROOM);
        if ($AIId == 0) {
            //玩家修改状态
            $AIId = $roomModel->getPlayerID();
        }

        $sendMsg = new GCRankReadyState();
        $sendMsg->setCode(ClientErrorCode::CLIENT_SUCCESS);
        $sendMsg->setState($state);
        $sendMsg->setPlayerID($AIId);

        // 给玩家自己发送
        SendMessage::getInstance()->sendClient(PacketId::GC_RankReadyState, $sendMsg);
        LogMark::getInstance()->markInfo(
            "[PVPFlowLog][51][RoomInfoLogic] changePlayerState sync to other player"
        );
        // 给其他玩家同步状态
        foreach ($players as $pid) {
            if ($this->checkIsAIPlayer($pid)) {
                continue;
            }
            $this->addPacket(PacketId::GC_RankReadyState, $sendMsg, $pid);
        }
        return true;
    }

    // 团队排位进入游戏同步
    public function enterScene($players): bool {
        LogMark::getInstance()->markInfo(
            "[PVPFlowLog][54][RoomInfoLogic] enterScene"
        );
        /**
         * @var RoomModel $roomModel
         */
        $roomModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ROOM);
        $this->playerID = $roomModel->getPlayerID();

        $sendMsg = new GCRankJoinScene();
        $sendMsg->setCode(ClientErrorCode::CLIENT_SUCCESS);

        // 给玩家自己发送
        SendMessage::getInstance()->sendClient(PacketId::GC_RankJoinScene, $sendMsg);
        LogMark::getInstance()->markInfo(
            "[PVPFlowLog][55][RoomInfoLogic] enterScene sync to other player"
        );

        // 给其他玩家同步进入游戏
        foreach ($players as $pid) {
            if ($this->checkIsAIPlayer($pid)) {
                continue;
            }
            $this->addPacket(PacketId::GC_RankJoinScene, $sendMsg, $pid);
        }
        return true;
    }

    //团队排位赛,同队内展示战绩
    public function broadcastRankTeamRecord(int $playerId, string $msg, $players): bool
    {
        $sendMessage = new GCRankTeamRecord();
        $sendMessage->setMessage($msg);
        $sendMessage->setPlayerId($playerId);

        // 给其他玩家同步进入游戏
        foreach ($players as $pid) {
            if ($this->checkIsAIPlayer($pid)) {
                continue;
            }
            $this->addPacket(PacketId::GC_RankTeamRecord, $sendMessage, $pid);
        }
        return true;
    }

    // 玩家换服装,服装在其他模块检查
    public function updateRoomPlayerCloth(int $roomID, int $gender, ClothData $cloth): int
    {
        /**
         * @var RoomModel $roomModel
         */
        $roomModel = ModelManager::getInstance()->getModel(ModelTypeDefine::ROOM);
        $this->playerID = $roomModel->getPlayerID();

        $room = $roomModel->newRoom($roomID);
        if(!$room->searchRoomByRoomID()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomInfoLogic] change player car search room error!', (array)$room
            );
            return ClientErrorCode::ERROR_ROOM_NOT_EXIST;
        }

        if(!$roomModel->savePlayerCloth($room)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomInfoLogic] update player car error!', (array)$room
            );
            return ClientErrorCode::ERROR_ROOM_OPERATE_FAILED;
        }
        //广播给其他人消息
        $message = new GCUpdateRoomPlayerCloth();
        $message->setCode(ClientErrorCode::CLIENT_SUCCESS);
        $message->setPlayerID($this->playerID);
        $message->setGender($gender);
        $message->setCloth($cloth);
        foreach($room->playerInfo as $id => $p) {
            if($p[RoomData::PLAYER_ID] == $this->playerID) {
                continue;
            }
            $this->addPacket(PacketId::GC_UpdateRoomPlayerCloth, $message, $id);
        }
        return ClientErrorCode::CLIENT_SUCCESS;
    }
}
