<?php

/**
 * 处理从客户端收到的消息
 * Created On 2021/11/1
 * @author yuanb yuanbo0x@gmail.com
 */

namespace Framework\Network;

use Exception;
use Game\Constant\ClientErrorCode;
use Game\Constant\PacketDefineI;
use Game\Constant\TestParams;
use Game\GameLive;
use Game\Protobuf\GCModuleMsgDisable;
use Library\Common\Singletons;
use Library\Crypt;
use Framework\Define\ErrorDefine;
use Framework\Define\IControllerDefine;
use Framework\Log\LogMark;
use Framework\MVC\ControllerBase;
use Framework\Protobuf\SinglePacket;
use Framework\Protobuf\WebRequestMessage;
use Game\Protobuf\PacketId;
use Tool\InitServer\Config;
use Game\Logic\CommonConstLogic;

/**
 * 接受消息处理类
 */
class ReceiveMessage
{
    use Singletons;
    use Crypt;
    use CommonConstLogic;

    /**
     * @var WebRequestMessage $requestMessage 收到的消息包
     */
    private WebRequestMessage $requestMessage;

    /**
     * @var IControllerDefine $controllerDefine 控制器字典
     */
    private IControllerDefine $controllerDefine;

    /**
     * @var ICheckRequest
     */
    private ICheckRequest $checkRequest;


    /**
     * @var int $index 消息序列号
     */
    private int $index = 0;

    private int $playerId;

    private string $sessionId;

    //上次请求缓存信息
    private int $lastIndex = 0;
    private ?string $lastSessionId = null;
    private ?string $packetCache = null;

    // 服务器信息
    private array $serverInfo = [];

    /**
     * 模块初始化控制器字典
     * @param IControllerDefine $define 控制器字典
     * @param ICheckRequest $check
     */
    public function init(IControllerDefine $define, ICheckRequest $check)
    {
        $this->controllerDefine = $define;
        $this->checkRequest = $check;
    }

    public function checkQuest(): int
    {
        return $this->parsing();
    }

    public function run(): int
    {
        if ($this->playerId != 0) {
            if ($this->checkSession() == false) {
                return ClientErrorCode::LOGIN_REPEATED_DEVICE;
            }
            if ($this->checkReconnect()) {
                return ErrorDefine::SUCCESS;
            }
        }

        $this->serverInfo = $this->getCommonConstData();

        foreach ($this->requestMessage->getPacketList() as $packet) {
            if($this->index > 1 && $packet->getId() === PacketId::User_Login_Send) {
                $this->sendLoginCache($packet);
                return ErrorDefine::SUCCESS;
            }
            $this->dealPacket($packet);
        }
        return ErrorDefine::SUCCESS;
    }

    private function initPackHead()
    {
        $this->index = $this->requestMessage->getIndex();
        $this->playerId = $this->requestMessage->getPlayerId();
        TestParams::$playerID = $this->playerId;
        $this->sessionId = $this->requestMessage->getSession();
        SendMessage::getInstance()->setIndex($this->index);
    }

    /**
     * 获取客户端消息解析成消息包
     * @return int 处理结果
     */
    public function parsing(): int
    {
        $source_data = file_get_contents('php://input');
        if ($source_data == "") {
            LogMark::getInstance()->markError(
                ErrorDefine::REQUEST_PARAM_ERROR,
                "receive client data null"
            );
            return ErrorDefine::REQUEST_PARAM_ERROR;
        }
        $decrypt_data = "";
        if (!$this->decrypt($source_data, $decrypt_data)) {
            LogMark::getInstance()->markError(
                ErrorDefine::REQUEST_CRYPT_ERROR,
                "decrypt client data error",
                array("ClientData" => $source_data)
            );
            return ErrorDefine::REQUEST_CRYPT_ERROR;
        }
        $this->requestMessage = new WebRequestMessage();
        try {
            $this->requestMessage->mergeFromString($decrypt_data);
        } catch (Exception $e) {
            LogMark::getInstance()->markError(
                ErrorDefine::REQUEST_PARAM_ERROR,
                "covert to proto packet error",
                array("ClientData" => $source_data, "Exception" => $e->getMessage())
            );
            return ErrorDefine::REQUEST_PARAM_ERROR;
        }
        $this->initPackHead();
        return ErrorDefine::SUCCESS;
    }

    /**
     * 处理单个网络消息
     * @param SinglePacket $packet 单个网络消息包
     * @return int 处理结果
     */
    public function dealPacket(SinglePacket $packet): int
    {
        $packet_id = $packet->getId();
        TestParams::$packetID[] = $packet_id;
        // 检测消息是否封禁
        if(!$this->CheckPacketIDModule($packet_id)) {
            return ErrorDefine::MODULE_DISABLE;
        }
        //日志忽略消息列表
        $ignorePacketList = array(
            PacketId::CG_HeartBeat,     //普通心跳
        );
        if (!in_array($packet_id, $ignorePacketList)) {
            LogMark::getInstance()->markDebug(
                "start deal packet",
                array("packetId" => $packet_id)
            );
        }
        $controller_name = $this->controllerDefine->getControllerName($packet_id);
        if ($controller_name == "") {
            LogMark::getInstance()->markError(
                ErrorDefine::PACKET_ID_ERROR,
                "create controller fail",
                array("packetId" => $packet_id)
            );
            return ErrorDefine::PACKET_ID_ERROR;
        }
        /**
         * @var ControllerBase $controller
         */
        $controller = new $controller_name();
        $controller->init($this->playerId);
        $str = base64_decode($packet->getData());
        $ret = $controller->parsing($str, $packet_id);
        if ($ret != ErrorDefine::SUCCESS) {
            return $ret;
        }
        return $controller->run();
    }

    private function CheckPacketIDModule(int $packet_id):bool {
        if(isset($this->serverInfo[Config::SERVER_MODULE_KEY]) && !empty($this->serverInfo[Config::SERVER_MODULE_KEY])) {
            $module = json_decode($this->serverInfo[Config::SERVER_MODULE_KEY], true);
            $packetIDS = [];
            foreach ($module as $id) {
                foreach (PacketDefineI::PACKET_LIMIT_MAP[$id] as $i) {
                    $packetIDS[] = $i;
                }
            }
            if(in_array($packet_id, $packetIDS)) {
                $msg = new GCModuleMsgDisable();
                $msg->setPacketId($packet_id);
                SendMessage::getInstance()->sendClient(PacketId::GC_ModuleMsgDisable, $msg);
                return false;
            }
        }
        return true;
    }

    /**
     * 校验session是否合法
     * @return bool true通过有验证
     */
    public function checkSession(): bool
    {
        return $this->checkRequest->checkSessionValid($this->sessionId);
    }

    /**
     * 检查是否重连包
     * @return bool true重连包
     */
    public function checkReconnect(): bool
    {
        return $this->checkRequest->checkReconnect($this->index);
    }

    /**
     * 登录直接返回缓存包
     * @return bool true重连包
     */
    public function sendLoginCache($packet): bool
    {
        return $this->checkRequest->sendLoginPacketCache($packet);
    }

    /**
     * 获取包序列
     * @return int 包序列
     */
    public function getIndex(): int
    {
        return $this->index;
    }

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