<?php

/**
 * PvpPack.php
 * 文件描述
 * Created On 2022/4/26
 * @author yuanb yuanbo0x@gmail.com
 */

namespace Server\Model\PVP;

use Game\Constant\ClientErrorCode;
use Game\GameLive;
use Game\Logic\Pvp\PvpSettlementLogic;
use Game\Model\Pvp\PvpBaseModel;
use Game\Model\RoomCustomizeModel;
use Game\Operation\EventLog\EventLog_Manager;
use Game\Operation\PlatformManager;
use Server\Model\IServerBase;
use Framework\Define\ErrorDefine;
use Framework\Log\LogMark;
use Framework\MVC\ModelManager;
use Framework\Lib\Utils;
use Game\Config\GameConfig;
use Game\Constant\DBTableDefine;
use Framework\DB\Handler\MatchingDBHandler;
use Game\Constant\GameConstantDefine;
use Game\Constant\GameErrorCode;
use Game\Constant\ModelTypeDefine;
use Game\Data\PvpPacket\PvpMsgData;
use Game\Data\RaceResultData;
use Game\Logic\Activity\ScuffleLogic;
use Game\Logic\GenerateAiLogic;
use Game\Logic\Pvp\MatchAILogic;
use Game\Logic\Pvp\PvpLogic;
use Game\Logic\Pvp\PvpMatchLogic;
use Game\Model\Pvp\PacketMatchingResultToPlayer;
use Game\Model\Pvp\PacketRaceCancelMatch;
use Game\Protobuf\MatchPlayer;
use Game\Protobuf\MGCancelMatch;
use Game\Protobuf\MRGMatchResult;
use Exception;
use Game\Protobuf\RGSettlement;
use Game\Protobuf\SettlementPlayer;

class PvpPack implements IServerBase
{

    use MatchingDBHandler;
    use MatchAILogic;
    use PvpMatchLogic;
    use GenerateAiLogic;
    use PvpLogic;
    use PvpSettlementLogic;
    use ScuffleLogic;

    public array $matchingRes = array();
    private array $positionList = array(1, 2, 3, 4, 5, 6);
    private int $logTime;   //打点日志时间

    public function init(): bool
    {
        LogMark::getInstance()->markInfo("pvp pack init success!");
        $this->logTime = Utils::getServerTimestamp();
        return true;
    }

    public function close()
    {
        unset($this->matchingRes);
        $this->matchingRes = array();
        PlatformManager::getInstance()->close();
        LogMark::getInstance()->markInfo("pvp pack close success!");
    }

    public function run($nowTime)
    {
        $this->printLog2File();
        $this->getRDBData();
        if(empty($this->matchingRes)) {
            return ;
        }
        foreach($this->matchingRes as $msg) {
            $packetInfo = explode(GameConstantDefine::DBPacketAnalyseKey_1, $msg);
            [$msgID, $msgData] = $packetInfo;
            if (!is_array($packetInfo) || count($packetInfo) != 2 ){
                LogMark::getInstance()->markError(
                    GameErrorCode::PVP_PACKET_ERROR,
                    "[PvpPack] dealPacket error",
                    array(
                        "packet msg: " => $msg)
                );
                return;
            }
            LogMark::getInstance()->setPlayerId(0);
            ModelManager::getInstance()->setPlayerId(0);
            ModelManager::getInstance()->clearModelList();
            switch ($msgID)
            {
                case GameConstantDefine::MatchingResultToPlayer:
                    $this->dealMatchingResult($msgData);
                    break;
                case GameConstantDefine::RaceResultToPlayer:
                    $this->dealRaceResult($msgData);
                    break;
                case GameConstantDefine::CancelMatch:
                    $this->dealCancelMatch($msgData);
                    break;
                default:
            }
        }
        $this->close();
    }

    public function getRDBData():bool {
        if(!$this->searchMultiQueue(DBTableDefine::TABLE_PVP,
            GameConfig::getInstance()->SERVER_ID(), 0, 19, $this->matchingRes)){
            return false;
        }

        if(empty($this->matchingRes)) {
            return true;
        }

        LogMark::getInstance()->markInfo(
            '[pvp pack] search pvp msg show console:',
            $this->matchingRes
        );

        if(!$this->delMultiQueue(DBTableDefine::TABLE_PVP, GameConfig::getInstance()->SERVER_ID(), 20, -1)){
            return false;
        }
        return true;
    }

    //处理匹配完成消息
    public function dealMatchingResult(string $msgData):bool {
        LogMark::getInstance()->markInfo(
            '[PvpPack] deal matching result start!',
        );

        $msgDataTmp = base64_decode($msgData);
        $matchRes = new MRGMatchResult();
        try {
            $matchRes->mergeFromString($msgDataTmp);
        } catch (Exception $e) {
            LogMark::getInstance()->markError(
                ErrorDefine::ERROR_INTERNAL,
                "[PVPFlowLog][PvpPack]merge matching data error!",
                array("Exception" => $e->getMessage())
            );
            return false;
        }
        LogMark::getInstance()->markInfo(
            "[PVPFlowLog][9][PvpPack] dealMatchingResult: ". $matchRes->serializeToJsonString()
        );
        $playerList = [];
        $fillAiPlayerList = []; //需要补充AI的玩家
        // 匹配到一起之后，整局游戏唯一id
        $matchUID = $this->makeMatchID();
        foreach($matchRes->getPlayer() as $msg) {
            /**
             * @var MatchPlayer $msg
             */
            LogMark::getInstance()->setPlayerId($msg->getPlayerID());
            LogMark::getInstance()->markInfo(
                "[PVPFlowLog][9][PvpPack] start dealMatchingResult: ". $matchRes->serializeToJsonString()
            );
            //检查玩家显示数据
            $playerShowData = $this->getPlayerShowData($msg->getPlayerShow());
            if (is_null($playerShowData)) {
                LogMark::getInstance()->markError(
                    ErrorDefine::ERROR_INTERNAL,
                    "[PVPFlowLog][PvpPack] player show json string error",
                );
                $this->sendErrorMessage($msg->getPlayerID(), ClientErrorCode::PVP_JOIN_MATCH_ERROR);
                return false;
            }
            //检查玩家赛车数据
            $carShowData = $this->getCarShowData($msg->getCarShow());
            if (is_null($carShowData)) {
                LogMark::getInstance()->markError(
                    ErrorDefine::ERROR_INTERNAL,
                    "[PVPFlowLog][PvpPack] car show json string error",
                );
                $this->sendErrorMessage($msg->getPlayerID(), ClientErrorCode::PVP_JOIN_MATCH_ERROR);
                return false;
            }
            //修复groupId
            $this->fixGroupId($matchRes->getMatchType(), $msg);
            $paramList = $this->analysisMatchingParam($matchRes->getMatchType(), $msg->getGroupID(), $msg->getParam());
            if (is_null($paramList)) {
                LogMark::getInstance()->markError(
                    ErrorDefine::ERROR_INTERNAL,
                    "[PVPFlowLog][PvpPack] analysisMatchingParam error",
                    array(
                        "matchType" => $matchRes->getMatchType(),
                        "groupId" => $msg->getGroupID(),
                        "param" => $msg->getParam()
                    )
                );
                $this->sendErrorMessage($msg->getPlayerID(), ClientErrorCode::PVP_JOIN_MATCH_ERROR);
                return false;
            }
            ModelManager::getInstance()->setPlayerId($msg->getPlayerID());
            //检查joinId
            $myJoinId = $this->getJoinIdByPlayerId($msg->getPlayerID());
            ModelManager::getInstance()->clearModelList();
            if ($msg->getJoinID() == 0 || $msg->getJoinID() != $myJoinId) {
                LogMark::getInstance()->markError(
                    GameErrorCode::PVP_PACKET_ERROR,
                    "[PVPFlowLog][PvpPack][dealMatchingResult] JoinId not equal",
                    array(
                        "packetJoinId" => $msg->getJoinID(),
                        "MyJoinId" => $myJoinId
                    )
                );
                $this->sendErrorMessage($msg->getPlayerID(), ClientErrorCode::PVP_MATCHING_JOIN_ID_ERROR);
                return false;
            }

            $data = [
                PvpMsgData::MATCH_UID  => $matchUID,
                PvpMsgData::PLAYER_ID  => $msg->getPlayerID(),
                PvpMsgData::SERVER_ID  => $msg->getServerID(),
                PvpMsgData::JOIN_ID    => $msg->getJoinID(),
                PvpMsgData::GROUP_ID   => $msg->getGroupID(),
                PvpMsgData::POSITION => $paramList[0],
                PvpMsgData::PLAYER_SHOW => $playerShowData,
                PvpMsgData::CAR_SHOW => $carShowData,
                PvpMsgData::ROLE_TYPE => $msg->getRoleType(),   //1 房间内 2 观战
                PvpMsgData::PARAM => $msg->getParam(),
            ];
            $playerList[] = $data;
            // 排除观战
            if($msg->getRoleType() != RoomCustomizeModel::WATCH_ROOM) {
                $fillAiPlayerList[] = $data;
            }
        }
        $msgInfo = [
            PvpMsgData::MSG_ID      => GameConstantDefine::MatchingResultToPlayer,
            PvpMsgData::IP          => $matchRes->getIp(),
            PvpMsgData::PORT        => $matchRes->getPort(),
            PvpMsgData::MAP_ID      => $matchRes->getMapID(),
            PvpMsgData::MATCH_TYPE  => $matchRes->getMatchType(),
            PvpMsgData::PLAYER_LIST => $playerList,
            //加入匹配AI
            PvpMsgData::AI_INFO => $this->addMatchAI($matchRes->getMatchType(), $fillAiPlayerList, $matchRes->getMapID()),
        ];

        LogMark::getInstance()->markInfo(
            '[PvpPack] matching info',
            $msgInfo
        );
        //创建大乱斗房间
        if ($matchRes->getMatchType() == GameConstantDefine::MATCH_MODE_SCUFFLE) {
            [$scufflePlayerList, $scuffleAIList] = $this->createScuffleRoom($msgInfo, $matchUID);
            $msgInfo[PvpMsgData::PLAYER_LIST] = $scufflePlayerList;
            $msgInfo[PvpMsgData::AI_INFO] = $scuffleAIList;
        }
        foreach($playerList as $item) {
            ModelManager::getInstance()->clearModelList();
            ModelManager::getInstance()->setPlayerId($item[PvpMsgData::PLAYER_ID]);
            $dealMatching = new PacketMatchingResultToPlayer();
            $dealMatching->setPlayerId($item[PvpMsgData::PLAYER_ID]);
            $dealMatching->dealMatchingResult($msgInfo, $item, $matchUID);
        }
        LogMark::getInstance()->markInfo(
            "[PVPFlowLog][11][PvpPack] deal MatchingResult over"
        );
        LogMark::getInstance()->setPlayerId(0);
        $this->positionList = array(1, 2, 3, 4, 5, 6);
        return true;
    }

    //处理比赛结算消息
    public function dealRaceResult(string $msgData): bool {
        LogMark::getInstance()->markInfo(
            '[PvpPack] deal race result start!',
        );
        $msgDataTmp = base64_decode($msgData);
        $settlement = new RGSettlement();
        try {
            $settlement->mergeFromString($msgDataTmp);
        } catch (Exception $e) {
            LogMark::getInstance()->markError(
                ErrorDefine::ERROR_INTERNAL,
                "[PVPFlowLog][PvpPack]merge matching data error!",
                array("Exception" => $e->getMessage())
            );
            return false;
        }
        LogMark::getInstance()->markInfo(
            "[PVPFlowLog][PvpPack] dealRaceResult: ". $settlement->serializeToJsonString()
        );
        $playerList = [];
        $mapId = null;
        foreach($settlement->getPlayer() as $player) {
            //整理结算数据
            $raceResult = new RaceResultData();
            $raceResult->_playerId = $player->getPlayerID();
            $raceResult->_carId = $player->getCarTplID();
            $raceResult->_rank = $player->getRank();
            $raceResult->_costTime = $player->getCostTime();
            $raceResult->_groupId = $player->getGroupID();
            $raceResult->_joinId = $player->getJoinID();
            $raceResult->_modeType = $settlement->getMatchType();
            //解析结算参数
            $this->analysisSettlementParam($player->getSettlementParam(), $raceResult);
            $playerList[$raceResult->_playerId] = $raceResult;
            /**
             * @var SettlementPlayer $player
             */
            //检查是否为AI
            if (!$this->checkIsAIPlayer($player->getPlayerID())) {
                LogMark::getInstance()->setPlayerId($player->getPlayerID());
                LogMark::getInstance()->markInfo(
                    "[PVPFlowLog][18][PvpPack] start dealRaceResult: ". $settlement->serializeToJsonString()
                );
                ModelManager::getInstance()->setPlayerId($player->getPlayerID());
                //检查joinId
                $myJoinId = $this->getJoinIdByPlayerId($player->getPlayerID());
                if ($player->getJoinID() == 0 || $player->getJoinID() != $myJoinId) {
                    LogMark::getInstance()->markError(
                        GameErrorCode::PVP_PACKET_ERROR,
                        "[PVPFlowLog][PvpPack][dealRaceResult] JoinId not equal",
                        array(
                            "packetJoinId" => $player->getJoinID(),
                            "MyJoinId" => $myJoinId
                        )
                    );
                    $this->sendErrorMessage($player->getJoinID(), ClientErrorCode::PVP_MATCHING_JOIN_ID_ERROR);
                    ModelManager::getInstance()->clearModelList();
                    //加入忽略
                    $playerList[$raceResult->_playerId]->ignore = true;
                    continue;
                }
                if (is_null($mapId)) {
                    //查询mapId
                    $record = $this->getRecordByJoinId($myJoinId);
                    if (is_null($record)) {
                        LogMark::getInstance()->markError(
                            GameErrorCode::PVP_PACKET_ERROR,
                            "[PVPFlowLog][PvpPack] record not found",
                            array(
                                "MyJoinId" => $myJoinId
                            )
                        );
                        ModelManager::getInstance()->clearModelList();
                        continue;
                    }
                    $mapId = $record->mapID;
                    ModelManager::getInstance()->clearModelList();
                }
            }
        }
        if (is_null($mapId)) {
            LogMark::getInstance()->markError(
                GameErrorCode::PVP_PACKET_ERROR,
                "[PVPFlowLog][PvpPack] mapId not found",
            );
            return false;
        }
        //整理破记录信息
        $breakingRecordInfo = $this->tidySettlementParams($playerList, $settlement->getMatchType(), $mapId);
        $msgInfo = [
            PvpMsgData::MSG_ID                 => GameConstantDefine::RaceResultToPlayer,
            PvpMsgData::SETTLEMENT_MATCH_TYPE  => $settlement->getMatchType(),
            PvpMsgData::SETTLEMENT_PLAYER_LIST => $playerList,
        ];
        LogMark::getInstance()->setPlayerId(0);
        LogMark::getInstance()->markInfo(
            '[PvpPack] race result info',
            $msgInfo
        );
        foreach($playerList as $playerId => $raceResult) {
            //检查是否为AI
            if ($this->checkIsAIPlayer($playerId)) {
                continue;
            }
            if ($raceResult->ignore) {
                continue;
            }
            GameLive::getInstance()->InitProtagonistPlayerId($playerId);
            ModelManager::getInstance()->clearModelList();
            ModelManager::getInstance()->setPlayerId($playerId);
            LogMark::getInstance()->setPlayerId($playerId);
            $modeModel = ModelManager::getInstance()->getModel(ModelTypeDefine::MODE_MAP[$settlement->getMatchType()]);
            $modeModel->setPlayerId($playerId);
            try {
                $modeModel->matchSettlement($playerList, $breakingRecordInfo);
            } catch (Exception $e) {
                LogMark::getInstance()->markError(
                    GameErrorCode::MATCH_SETTLEMENT_ERROR,
                    "[PVPFlowLog][PvpPack]matchSettlement exception",
                    array($e)
                );
            }
            ModelManager::getInstance()->setPlayerId(0);
            ModelManager::getInstance()->clearModelList();
        }
        LogMark::getInstance()->markInfo(
            "[PVPFlowLog][PvpPack] dealRaceResult over"
        );
        LogMark::getInstance()->setPlayerId(0);
        return true;
    }

    //处理取消比赛消息
    public function dealCancelMatch(string $msgData): bool {
        LogMark::getInstance()->markInfo(
            '[PVPFlowLog][44][PvpPack] deal cancel result start!',
        );
        $msgDataTmp = base64_decode($msgData);
        $cancelMatch = new MGCancelMatch();
        try {
            $cancelMatch->mergeFromString($msgDataTmp);
        } catch (Exception $e) {
            LogMark::getInstance()->markError(
                ErrorDefine::ERROR_INTERNAL,
                "[PVPFlowLog][PvpPack]merge cancel match data error!",
                array("Exception" => $e->getMessage())
            );
            return false;
        }
        LogMark::getInstance()->markInfo(
            "[PVPFlowLog][44][PvpPack] dealCancelMatch: ". $cancelMatch->serializeToJsonString()
        );
        $code = $cancelMatch->getCode();
        $joinID = $cancelMatch->getJoinID();
        $playerList = $cancelMatch->getPlayerID();
        if($code != 0) {
            LogMark::getInstance()->markError(
                ErrorDefine::ERROR_INTERNAL,
                "[PVPFlowLog][PvpPack]cancel match but server res error!",
                array("joinID" => $joinID, "code" => $code)
            );
            return false;
        }

        $roomId = null;
        // 给每个玩家发送取消匹配消息
        foreach($playerList as $playerId) {
            ModelManager::getInstance()->clearModelList();
            ModelManager::getInstance()->setPlayerId($playerId);
            $cancel = new PacketRaceCancelMatch();
            $cancel->setPlayerId($playerId);
            $tmpRoomId = $cancel->dealCancelMatching($code, $joinID);
            if (!is_null($tmpRoomId) && $roomId != $tmpRoomId) {
                $roomId = $tmpRoomId;
            }
        }
        //修改房间状态
        $cancel = new PacketRaceCancelMatch();
        if(!$cancel->changeRoomState($roomId)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[PVPFlowLog][PvpBaseModel][CancelMatch] cancel match change room state error!'
            );
            return false;
        }
        return true;
    }

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

    //修复groupId,个人练习,排位赛,groupId置为0;团队赛匹配结果groupId为0,1,需要+1
    private function fixGroupId(int $matchType, MatchPlayer $msg)
    {
        switch ($matchType)
        {
            case GameConstantDefine::MATCH_MODE_SINGLE_PRACTISE:
            case GameConstantDefine::MATCH_MODE_SINGLE_PAI_WEI:
                $msg->setGroupID(0);
                break;
            case GameConstantDefine::MATCH_MODE_TEAM_PRACTISE:
            case GameConstantDefine::MATCH_MODE_TEAM_PAI_WEI:
            case GameConstantDefine::MATCH_MODE_SCUFFLE:
                $msg->setGroupID($msg->getGroupID() + 1);
                break;
        }
    }

    //解析匹配成功Param
    //根据MatchType类型不同,参数不同.MatchType为排位赛时,代表位置,排位赛用.个人排位1-6,组队排位1,2竞速位 3,4干扰位 5,6辅助位
    private function analysisMatchingParam(int $matchType, int $groupId, string $string): ?array
    {
        $result = array(
            0           //位置,练习赛为0
        );
        switch ($matchType)
        {
            case GameConstantDefine::MATCH_MODE_SINGLE_PAI_WEI:
                //个人排位
                $result[0] = array_shift($this->positionList);
                return $result;
            case GameConstantDefine::MATCH_MODE_TEAM_PAI_WEI:
                //组队排位
                //    LocationEnum_Speed = 0,
                //    LocationEnum_Interference = 1,
                //    LocationEnum_Assist = 2,
                $positionArray = array(
                    1 => array(
                        0 => 1,
                        1 => 3,
                        2 => 5,
                    ),
                    2 => array(
                        0 => 2,
                        1 => 4,
                        2 => 6,
                    ),
                );
                if (!isset($positionArray[$groupId])) {
                    return null;
                }
                if (!isset($positionArray[$groupId][(int)$string])) {
                    return null;
                }
                $result[0] = $positionArray[$groupId][(int)$string];
                return $result;
            default:
                return $result;
        }
    }

    //发送失败消息--方便定位错误
    private function sendErrorMessage(int $playerId, int $code)
    {
        ModelManager::getInstance()->setPlayerId($playerId);
        /**
         * @var PvpBaseModel $pvpBaseModel
         */
        $pvpBaseModel = ModelManager::getInstance()->getModel(ModelTypeDefine::PVP_BASE);
        $pvpBaseModel->sendFailMessage($playerId, $code);
        LogMark::getInstance()->markError(
            ErrorDefine::PVE_ERROR,
            "[PvpPack] pvp error",
            array(
                "PlayerId" => $playerId,
                "Code" => $code
            )
        );
        ModelManager::getInstance()->clearModelList();
    }

    //输出打点日志到文件
    private function printLog2File()
    {
        if (Utils::getServerTimestamp() - $this->logTime >= GameConstantDefine::POINT_LOG_GAP_TIME) {
            PlatformManager::getInstance()->close();
            $this->logTime = Utils::getServerTimestamp();
        }
    }
}
