<?php
/**
 * 排行榜
 */
namespace Game\Model\Rank;

use Framework\Lib\Utils;
use Framework\Log\LogMark;
use Framework\MVC\ModelManager;
use Framework\Network\SendMessage;
use Framework\Logic\CommonDataLogic;
use Game\Constant\ClientErrorCode;
use Game\Constant\ConstTemplate\TemplateConst;
use Game\Constant\ConstTemplate\TemplatePVPMap;
use Game\Constant\GameErrorCode;
use Game\Constant\ModelTypeDefine;
use Game\Constant\TemplateDefine;
use Game\Data\AccountData;
use Game\Data\Rank\RankCacheData;
use Game\Data\Rank\RankData;
use Game\Data\Rank\RankInfoData;
use Game\Data\Rank\RankLikesData;

use Game\Logic\EventConditionLogic;
use Game\Logic\SeasonLogic;
use Game\Model\AccountModel;
use Game\Model\Car\CarExteriorRefitModel;

use Game\Protobuf\GCRacingRankData;
use Framework\DB\Handler\CommonDBHandler;
use Framework\Logic\TemplateHelp;
use Game\Protobuf\GCRankLike;
use Game\Protobuf\PacketId;
use Game\Protobuf\RacingRank;

class RankModel {
    use CommonDBHandler;
    use TemplateHelp;
    use EventConditionLogic;
    use SeasonLogic;

    public const RANK_SEASON        = 1;  // 段位榜 赛季分榜
    public const RANK_FULL          = 2;  // 段位榜 全区分榜
    public const CLUB_RANK          = 3;  // 俱乐部 段位分榜
    public const CLUB_ACTIVE        = 4;  // 俱乐部 活跃分榜
    public const CLUB_PERFORMANCE   = 5;  // 俱乐部 性能分榜
    public const MAP_SEASON         = 6;  // 地图榜 赛季分榜
    public const MAP_FULL           = 7;  // 地图榜 全区分榜
    public const RACING_SEASON      = 8;  // 竞速榜 赛季分榜
    public const RACING_FULL        = 9;  // 竞速榜 全区分榜
    public const DISTURBANCE_SEASON = 10; // 干扰榜 赛季分榜
    public const DISTURBANCE_FULL   = 11; // 干扰榜 全区分榜
    public const HELPER_SEASON      = 12; // 辅助榜 赛季分榜
    public const HELPER_FULL        = 13; // 辅助榜 全区分榜
    public const CAR_PERFORMANCE    = 14; // 车辆榜 性能分榜
    public const CAR_REFIT          = 15; // 车辆榜 改装分榜 // 和
    public const CLOTH              = 16; // 服饰榜        // 和
    public const MAP_CHALLENGE = 17;    //地图挑战赛地图时间榜

    public const RANK_TYPE = [
        self::RANK_SEASON, self::RANK_FULL, self::CLUB_RANK, self::CLUB_ACTIVE, self::CLUB_PERFORMANCE,
        self::MAP_SEASON, self::MAP_FULL, self::RACING_SEASON, self::RACING_FULL, self::DISTURBANCE_SEASON,
        self::DISTURBANCE_FULL, self::HELPER_SEASON, self::HELPER_FULL, self::CAR_PERFORMANCE, self::CAR_REFIT,
        self::CLOTH,self::MAP_CHALLENGE,
    ];

    // 地图map
    const RANK_MAP = [self::MAP_SEASON, self::MAP_FULL, self::MAP_CHALLENGE];

    // 赛季map
    const RANK_SEASON_TYPE = [
        self::RANK_SEASON, self::MAP_SEASON, self::RACING_SEASON, self::DISTURBANCE_SEASON, self::HELPER_SEASON
    ];

    // 俱乐部服务器配置map
    const CLUB_MAP = [self::CLUB_RANK, self::CLUB_ACTIVE, self::CLUB_PERFORMANCE];


    // 涉及点赞的map
    const RANK_LIKE = [self::RANK_SEASON, self::RANK_FULL, self::MAP_SEASON, self::MAP_FULL];

    // rank info 类型   做同意处理 可以节省内存空间（多个类型info只存储一份）
    const RANK_INFO_MAP = [
        self::RANK_SEASON        => self::RANK_SEASON,
        self::RANK_FULL          => self::RANK_SEASON,
        self::RACING_SEASON      => self::RANK_SEASON,
        self::RACING_FULL        => self::RANK_SEASON,
        self::DISTURBANCE_SEASON => self::RANK_SEASON,
        self::DISTURBANCE_FULL   => self::RANK_SEASON,
        self::HELPER_SEASON      => self::RANK_SEASON,
        self::HELPER_FULL        => self::RANK_SEASON,
        self::CAR_PERFORMANCE    => self::RANK_SEASON,
        self::CLOTH              => self::RANK_SEASON,
        self::MAP_CHALLENGE => self::RACING_SEASON,

        self::CLUB_RANK          => self::CLUB_RANK,
        self::CLUB_ACTIVE        => self::CLUB_RANK,
        self::CLUB_PERFORMANCE   => self::CLUB_RANK,

        self::MAP_SEASON         => self::MAP_SEASON,
        self::MAP_FULL           => self::MAP_SEASON,
        self::CAR_REFIT          => self::CAR_REFIT,

    ];

    const MAX_RANK_NUM = 200; // 默认rank数量
    const RANK_NUM_REDUNDANCY = 2;  // 所有排行榜冗余条数（记录排行榜中，并返回给客户端）
    const MAX_RANK_CACHE = 1800; // cache 30min

    private int $playerId;
    public GCRacingRankData $racingMsg;
    public GCRankLike $gcLikeMsg;

    public function __construct() {
        $this->racingMsg = new GCRacingRankData();
        $this->gcLikeMsg = new GCRankLike();

    }

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

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

    // 修改排行榜数据   sort  false: asc true: desc
    public function changeRankScore(int $rankType, int $score, int $mapID = 0, bool $sort = false): bool {
        if(!$this->checkParams($rankType, $mapID)) {
            return false;
        }
        $seasonID = $this->getSeasonId();
        if(in_array($rankType, self::RANK_SEASON_TYPE)) {
            if($seasonID == 0) {
                return false;
            }
        } else {
            $seasonID = 0;
        }

        $key = $this->getPlayerId();
        $rankData = new RankData($rankType, $seasonID, $mapID);
        $rankData->key = $key;
        $rankData->score = $score;

        $rankMaxNum = $this->getRankMaxNum($rankType);

        // 排行榜不满直接更新
        $num = 0;
        if(!$rankData->getRankNumber($num)) {
            return false;
        }
        if($num < $rankMaxNum) {
            // 更新数据
            if(!$this->changeRanking($rankData)) {
                return false;
            }
            $rankCacheData = new RankCacheData($rankType, $seasonID, $mapID);
            if(!$rankCacheData->delRankingCacheData()) {
                return false;
            }
            return true;
        }

        // 获取最低值key
        $minArr = [];
        $rankData->start = 0;
        $rankData->end = 0;
        if($sort) {
            if(!$rankData->findRevRankData($minArr)) {
                return false;
            }
        } else {
            if(!$rankData->findRankData($minArr)) {
                return false;
            }
        }

        $minKey = "";
        $min = 0;
        foreach($minArr as $k => $v) {
            $minKey = $k;
            $min = $v;
        }

        // 比排行榜最小值小 无需更新
        if($sort) {
            if ($min < $score) {
                return true;
            }
        } else {
            if ($min >= $score) {
                return true;
            }
        }

        // 更新排行榜
        if(!$this->changeRanking($rankData)) {
            return false;
        }

        $newNum = 0;
        if(!$rankData->getRankNumber($newNum)) {
            return false;
        }

        if($newNum > $rankMaxNum) {
            if(!$this->delRankAndCleanRankingCache($rankData, $minKey)) {
                return false;
            }
        }
        return true;
    }

    public function getRankMaxNum(int $rankType): int {
        switch ($rankType) {
            case self::RANK_SEASON:
                $num = $this->getTerm(TemplateDefine::TYPE_CONST,
                    TemplateConst::Const_Rank_Season_Max, "Const_num");
                break;
            case self::RANK_FULL:
                $num = $this->getTerm(TemplateDefine::TYPE_CONST,
                    TemplateConst::Const_Rank_Full_Max, "Const_num");
                break;
            case self::MAP_SEASON:
                $num = $this->getTerm(TemplateDefine::TYPE_CONST,
                    TemplateConst::Const_Rank_Map_Season_Max, "Const_num");
                break;
            case self::MAP_FULL:
                $num = $this->getTerm(TemplateDefine::TYPE_CONST,
                    TemplateConst::Const_Rank_Map_Full_Max, "Const_num");
                break;
            case self::RACING_SEASON:
                $num = $this->getTerm(TemplateDefine::TYPE_CONST,
                    TemplateConst::Const_Rank_Racing_Season_Max, "Const_num");
                break;
            case self::RACING_FULL:
            $num = $this->getTerm(TemplateDefine::TYPE_CONST,
                TemplateConst::Const_Rank_Racing_Full_Max, "Const_num");
            break;
            case self::DISTURBANCE_SEASON:
                $num = $this->getTerm(TemplateDefine::TYPE_CONST,
                    TemplateConst::Const_Rank_Disturbance_Season_Max, "Const_num");
                break;
            case self::DISTURBANCE_FULL:
            $num = $this->getTerm(TemplateDefine::TYPE_CONST,
                TemplateConst::Const_Rank_Disturbance_Full_Max, "Const_num");
            break;
            case self::HELPER_SEASON:
            $num = $this->getTerm(TemplateDefine::TYPE_CONST,
                TemplateConst::Const_Rank_Helper_Season_Max, "Const_num");
            break;
            case self::HELPER_FULL:
            $num = $this->getTerm(TemplateDefine::TYPE_CONST,
                TemplateConst::Const_Rank_Helper_Full_Max, "Const_num");
            break;
            case self::CAR_PERFORMANCE:
            $num = $this->getTerm(TemplateDefine::TYPE_CONST,
                TemplateConst::Const_Rank_Car_Performance_Max, "Const_num");
            break;
            case self::CAR_REFIT:
            $num = $this->getTerm(TemplateDefine::TYPE_CONST,
                TemplateConst::Const_Rank_Car_Refit_Max, "Const_num");
            break;
            case self::CLOTH:
                $num = $this->getTerm(TemplateDefine::TYPE_CONST,
                    TemplateConst::Const_Rank_Cloth_Max, "Const_num");
                break;
            case self::CLUB_RANK:
            $num = $this->getTerm(TemplateDefine::TYPE_CONST,
                TemplateConst::Const_Rank_Club_Rank_Max, "Const_num");
            break;
            case self::CLUB_ACTIVE:
                $num = $this->getTerm(TemplateDefine::TYPE_CONST,
                    TemplateConst::Const_Rank_Club_Active_Max, "Const_num");
                break;
            case self::CLUB_PERFORMANCE:
                $num = $this->getTerm(TemplateDefine::TYPE_CONST,
                    TemplateConst::Const_Rank_Club_Performance_Max, "Const_num");
                break;
            default:
                $num = self::MAX_RANK_NUM;
        }
        $num += self::RANK_NUM_REDUNDANCY;
        return $num;
    }

    // 删除排行榜 并清空该排行榜缓存数据
    public function delRankAndCleanRankingCache(RankData $rankData, int $minKey):bool {
        $rankData->setKey($minKey);
        if(!$rankData->deleteRankData()) {
            logMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[rank model] del ranking error!',
                (array)$rankData
            );
            return false;
        }
        $rankCacheData = new RankCacheData($rankData->rankType, $rankData->seasonID, $rankData->mapID);
        if(!$rankCacheData->delRankingCacheData()) {
            return false;
        }
        return true;
    }

    // 获取排行榜数据
    public function findRankData(int $rankType, int $mapID, $page): bool {
        if(!$this->checkParams($rankType, $mapID)) {
            return false;
        }
        $seasonID = $this->getSeasonId();
        if(in_array($rankType, self::RANK_SEASON_TYPE)) {
            if($seasonID == 0) {
                return false;
            }
        } else {
            $seasonID = 0;
        }
        $rankData = new RankData($rankType, $seasonID, $mapID);
        $rankCacheData = new RankCacheData($rankType, $seasonID, $mapID);

        $res = "";
        $data = [];
        if(!$rankCacheData->getRankingCacheData($res)) {
            // 查询最新的放入缓存中
            return false;
        }
        if(empty($res)) {
            if(!$this->searchRankingInfo($rankData, $data)) {
                return false;
            }
        }

        switch($rankType) {
            case self::CLUB_RANK:
            case self::CLUB_ACTIVE:
            case self::CLUB_PERFORMANCE:
            break;

            // 竞速 干扰 辅助 段位  地图
            case self::RANK_SEASON:
            case self::RANK_FULL:
            case self::MAP_SEASON:
            case self::MAP_FULL:
            case self::RACING_SEASON:
            case self::RACING_FULL:
            case self::DISTURBANCE_SEASON:
            case self::DISTURBANCE_FULL:
            case self::HELPER_SEASON:
            case self::HELPER_FULL:
            case self::CAR_REFIT:
            case self::CLOTH:
            case self::MAP_CHALLENGE:
                $this->sendRacingMsg($rankType, $mapID, $res, $data, $page);
                break;
            case self::CAR_PERFORMANCE:

        }
        return true;
    }

//    // 获取排行榜数据
//    public function findRankTypeData(int $rankType, int $mapID): array {
//        $res = [];
//        if(!$this->checkParams($rankType, $mapID)) {
//            return $res;
//        }
//        $seasonID = $this->getSeasonId();
//        if(in_array($rankType, self::RANK_SEASON_TYPE)) {
//            if($seasonID == 0) {
//                return $res;
//            }
//        }
//        $cache = "";
//        $rankCacheData = new RankCacheData($rankType, $seasonID, $mapID);
//        if(!$rankCacheData->getRankingCacheData($cache)) {
//            return $res;
//        }
//        if(empty($cache)) {
//            $data = [];
//            $rankData = new RankData($rankType, $seasonID, $mapID);
//            if(!$this->searchRankingInfo($rankData,$data)) {
//                return $res;
//            }
//            return $data;
//        }
//        return json_decode($cache, true);
//    }

    public function getSignData(): array
    {
//        $data['data'][] = [];
//        $data['mineData'] = [];
//        foreach(self::RANK_TYPE as $t) {
//            $mapID = 0;
//            if(in_array($t, self::RANK_MAP)) {
//                $maps = $this->getTable(TemplateDefine::TYPE_PVP_MAP);
//                foreach($maps as $id => $item) {
//                    if($item[TemplatePVPMap::OpenIf] === 'True') {
//                        $mapID = $item[TemplatePVPMap::Id];
//                        $arr = $this->findRankTypeData($t, $mapID);
//                        $data['data'][] = [
//                            'rankType' => $t,
//                            'mapID' => $mapID,
//                            'data' => json_encode($arr)
//                        ];
//                    }
//                }
//            } else {
//                $arr = $this->findRankTypeData($t, $mapID);
//                $data['data'][] = [
//                    'rankType' => $t,
//                    'mapID' => $mapID,
//                    'data' => json_encode($arr)
//                ];
//            }
//
//            // search rank keys
//            $seasonID = 0;
//            $mapID = 0;
//
//            if(in_array($t, self::RANK_SEASON_TYPE)) {
//                $seasonID = $this->getSeasonId();
//            }
//            if(in_array($t, self::RANK_MAP)) {
//                $maps = $this->getTable(TemplateDefine::TYPE_PVP_MAP);
//                foreach($maps as $id => $item) {
//                    if($item[TemplatePVPMap::OpenIf] === 'True') {
//                        $mapID = $item[TemplatePVPMap::Id];
//                        $key = "Rank-$t#$seasonID#$mapID";
//                        $val = $this->getMineRankData($t, $mapID);
//
//                        $likeArr = [];
//                        $likeData = new RankLikesData($t, $seasonID, $mapID);
//                        $likeData->playerID = $this->playerId;
//                        $likeData->getLikeKeys($likeArr);
//                        $data['mineData'][] = [
//                            'rankType' => $key,
//                            'score' => $val,
//                            'likes' => json_encode($likeArr),
//                        ];
//                    }
//                }
//            } else {
//                $likeArr = [];
//                $likeData = new RankLikesData($t, $seasonID, $mapID);
//                $likeData->playerID = $this->playerId;
//                $likeData->getLikeKeys($likeArr);
//                $key = "Rank-$t#$seasonID#$mapID";
//                $val = $this->getMineRankData($t, $mapID);
//                $data['mineData'][] = [
//                    'rankType' => $key,
//                    'score' => $val,
//                    'likes' => json_encode($likeArr),
//                ];
//            }
//        }
//        return $data;
        return [];
    }

    public function loginTimeOutUpdateRank(): void
    {
        foreach(self::RANK_TYPE as $t) {
            $mapID = 0;
            if(in_array($t, self::RANK_MAP)) {
                $maps = $this->getTable(TemplateDefine::TYPE_PVP_MAP);
                foreach($maps as $id => $item) {
                    if($item[TemplatePVPMap::OpenIf] === 'True') {
                        $mapID = $item[TemplatePVPMap::Id];
                        $val = $this->getMineRankData($t, $mapID);
                        if($val > 0) {
                            $this->changeRankScore($t, $val, $mapID);
                        }
                    }
                }
            } else {
                $val = $this->getMineRankData($t, $mapID);
                if($val > 0) {
                    $this->changeRankScore($t, $val, $mapID);
                }
            }
        }
    }

    public function searchRankingInfo(RankData $rankData, &$result): bool {
        $res = [];
        $timeOut = $this->getTerm(TemplateDefine::TYPE_CONST,
            TemplateConst::Const_Rank_Player_Time_Out_Max, "Const_num");

        $minTime = Utils::getServerTimestamp() - $timeOut  * 24 * 60 * 60;
        if(!$rankData->findRankData($res)) {
            logMark::getInstance()->markError(
                GameErrorCode::DATA_SEARCH_ERROR,
                '[rank model] find rank data error!',
                (array)$rankData
            );
            return false;
        }
        $keys = array_keys($res);
        $resRank = [];
        $rankInfoData = new RankInfoData($rankData->rankType, $rankData->seasonID, $rankData->mapID);
        if(!$rankInfoData->getRankInfoData($keys, $resRank)) {
            logMark::getInstance()->markError(
                GameErrorCode::DATA_SEARCH_ERROR,
                '[rank model] find ranking info error!',
                (array)$rankInfoData
            );
            return false;
        }
        $resRankSyncTime = [];
        if(!$rankInfoData->getRankSyncTimeData($keys, $resRankSyncTime)) {
            logMark::getInstance()->markError(
                GameErrorCode::DATA_SEARCH_ERROR,
                '[rank model] find rank sync login time error!',
            );
            return false;
        }
        foreach($res as $pid => $score) {
            if(is_bool($resRankSyncTime[$pid]) || $resRankSyncTime[$pid] <= $minTime) {
                // del rank
                $this->delRankAndCleanRankingCache($rankData, $pid);
                continue;
            }
            $rankLikesData = new RankLikesData();
            $rankLikesData->bePlayerID = $pid;
            $likeNum = 0;
            if(!$rankLikesData->getRankBeLikedNumber($likeNum)) {
                $line[RankData::BE_LIKED] = 0;
            }
            $line = json_decode($resRank[$pid], true);
            $line[RankData::PLAYER_ID] = $pid;
            $line[RankData::SCORE] = $score;
            $line[RankData::BE_LIKED] = $likeNum;
            $result[] = $line;
        }
        // 排序 为分页做准备
        $keyArr = array_column($result, RankData::SCORE);

        if(in_array($rankData->rankType, self::RANK_MAP)) {
            array_multisort($keyArr, SORT_ASC, $result);
            $resultData = [];     // 承载排序结果
            $resTemp[] = []; // 排序临时用
            $count = count($result);
            $k = 0;
            // 地图榜同分排序
            foreach ($result as $i => $iValue) {
                if($count >= 2) {
                    if(($i === $count - 1)) {
                        $resTemp[$k][] = $iValue;
                        continue;
                    }
                    if((int)$iValue[RankData::SCORE] !== (int)$result[$i+1][RankData::SCORE]) {
                        $resTemp[$k][] = $iValue;
                        ++ $k;
                    } else {
                        $resTemp[$k][] = $iValue;
                    }
                } else {
                    $resTemp[$k][] = $iValue;
                }
            }
            if(count($resTemp) >= 2) {
                foreach($resTemp as $data) {
                    $arr = array_column($data, RankData::SCORE);
                    array_multisort($arr, SORT_DESC,$data);
                    foreach ($data as $v) {
                        $resultData[] = $v;
                    }
                }
            } else {
                $resultData = $resTemp[0];
            }

            $result = $resultData;
        } else {
            array_multisort($keyArr, SORT_DESC, $result);
        }

        if(empty($result)) {
            return true;
        }
        $rankCacheData = new RankCacheData($rankData->rankType, $rankData->seasonID, $rankData->mapID);
        $rankCacheData->setData(json_encode($result));
        if(!$rankCacheData->addRankingCacheData()) {
            logMark::getInstance()->markError(
                GameErrorCode::DATA_SEARCH_ERROR,
                '[rank model] set rank cache error!',
            );
            return false;
        }
        return true;
    }

    public function changeRanking(RankData $rankData): bool {
        // 查询是否有同分的数据
        $searchData = [];
        $rankData->searchRankDataByScore($searchData);

        if(empty($searchData)) {
            // 如果有同分的 则增加0.9来表征sort
            $rankData->score += 0.9;
        } else {
            // 如果有则获取最小值的score 在此基础上构建sort优先级低的score
            $pIDS = array_keys($searchData);
            if(in_array($this->playerId, $pIDS)) {
                return true;
            }

            $rankData->score = (int)$rankData->score + 1/(0.1+1/($searchData[$pIDS[0]] - (int)$rankData->score));
        }
        // 增加此分数
        if(!$rankData->addRankData()) {
            logMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[rank model] change ranking error!',
                (array)$rankData
            );
            return false;
        }
        $rankInfoData = new RankInfoData($rankData->rankType, $rankData->seasonID, $rankData->mapID);
        if(!$this->syncRankInfo($rankInfoData, true)) {
            return false;
        }
        return true;
    }

    public function syncRankInfo(RankInfoData $rankInfo, bool $isUpdate = false): bool {
        /**
         * @var AccountModel $account
         */
        $account = ModelManager::getInstance()->getModel(ModelTypeDefine::ACCOUNT);
        $accountData = $account->getAccountInfo();
        if(is_null($accountData)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_SEARCH_ERROR, 'search account info error!');
            return false;
        }
        $data = [];
        $accountInfo = [];
        $accountData->loadFromDB($this->playerId, $accountInfo);
        $data[AccountData::DB_HEAD] = $accountData->head;
        $data[AccountData::DB_LEVEL] = $accountData->level;
        $data[AccountData::DB_GENDER] = $accountData->gender;
        $data[AccountData::DB_NICK_NAME] = $accountData->nickName;
        $data[AccountData::DB_SKIN_COLOR] = $accountData->skinColor ?? 1;

        if(in_array($rankInfo->rankType, self::RANK_MAP) && $isUpdate) {
            $mineInfo = [];
            $rankInfo->getRankInfoData([$this->playerId], $mineInfo);
            if(is_bool($mineInfo[$this->playerId])) {
                $data[AccountData::DB_DEFAULT_CAT_TPL] = $accountData->defaultCarTpl;
            } else {
                $data[AccountData::DB_DEFAULT_CAT_TPL] =
                    json_decode($mineInfo[$this->playerId], true)[AccountData::DB_DEFAULT_CAT_TPL];
            }
        } else {
            $data[AccountData::DB_DEFAULT_CAT_TPL] = $accountData->defaultCarTpl;
        }

        if($data[AccountData::DB_GENDER] == AccountData::FEMALE) {
            $data[RankData::CLOTHING] = $accountInfo[AccountData::DB_CLOTH_FEMALE];
        } else {
            $data[RankData::CLOTHING] = $accountInfo[AccountData::DB_CLOTH_MALE];
        }
        $defaultCarUID = $accountData->defaultCar;
        /**
         * @var CarExteriorRefitModel $erModel
         */
        $erModel = ModelManager::getInstance()->getModel(ModelTypeDefine::CAR_EXTERIOR_REFIT);

        $refits = [];
        $erModel->getCarExteriorInfo($defaultCarUID, $refits);
        $data[RankData::CAR_EXTERIOR_REFITS] = $refits;

        $rankInfo->setKey($this->playerId);
        $rankInfo->setData(json_encode($data));
        if(!$rankInfo->addRankInfoData()) {
            logMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[rank model] change ranking info error!',
            );
            return false;
        }
        return true;
    }

    // get mine rank data
    public function getMineRankData(int $type, int $mapID): int {
        $seasonID = $this->getSeasonId();
        if(in_array($type, RankModel::RANK_SEASON_TYPE)) {
            if($seasonID == 0) {
                return false;
            }
        } else {
            $seasonID = 0;
        }
        return $this->getSingleEventConditionValue(
            $this->getRankingKey($type, $seasonID, $mapID)
        );
    }

    // 检测输入参数是否合法
    public function checkParams(int $rankType, int $mapID): bool {
        if(!in_array($rankType, self::RANK_TYPE)) {
            return false;
        }
        if(!in_array($rankType, self::RANK_MAP)) {
            // 判断地图是否存在
            $mapCfg = $this->getTitle(TemplateDefine::TYPE_PVP_MAP, $mapID);
            if(is_null($mapCfg)) {
                logMark::getInstance()->markError(
                    GameErrorCode::SEARCH_CONFIG_INFO_FAILED,
                    '[logic] rank check param mapID not found!',
                    ['mapID' => $mapID]
                );
                return false;
            }
        }
        return true;
    }

    // 修改玩家排行榜登录时间
    public function updateLoginSyncTime(): bool {
        $rankInfoData = new RankInfoData();
        $rankInfoData->setKey($this->playerId);
        $rankInfoData->setData(Utils::getServerTimestamp());
        if(!$rankInfoData->addRankSyncTimeData()) {
            logMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[rank model] change rank login time error!',
            );
            return false;
        }
        return true;
    }

    // 玩家点赞
    public function rankLike(int $playerID): bool {
        $rankType = 0;
        $mapID = 0;

        // 判断是否已经点赞
        $rankLikesData = new RankLikesData();
        $rankLikesData->playerID = $this->playerId;
        $rankLikesData->setKey($playerID);
        if(!$rankLikesData->getRankLikeScore($num)) {
            return false;
        }

        if(empty($num)) {
            $rankLikesData->bePlayerID = $playerID;
            if(!$rankLikesData->rankLikeData()) {
                $this->setRankLike($rankType, $mapID, $playerID, ClientErrorCode::ERROR_RANK_LIKE_FAILED);
                return false;
            }
            // send msg
            $this->setRankLike($rankType, $mapID, $playerID, ClientErrorCode::CLIENT_SUCCESS);
            return true;
        }
        // send msg error (已经点过了)
        $this->setRankLike($rankType, $mapID, $playerID, ClientErrorCode::ERROR_RANK_LIKE_REPEATED);
        return false;
    }

    // 获取点赞列表
    public function getRankLike(&$res, $playerID = 0): bool
    {
        if($playerID != 0) {
            $this->playerId = $playerID;
        }
        // 判断是否已经点赞
        $rankLikesData = new RankLikesData();
        $rankLikesData->playerID = $this->playerId;
        if(!$rankLikesData->getLikeKeys($result)) {
            return false;
        }
        $likes = [];
        if(!empty($result)) {
            foreach ($result as $item) {
                $likes[] = $item['playerID'];
            }
        }
        $res = $likes;
        return true;
    }

    // 更新个人信息
    public function syncMineRankInfo(): bool {

        $rankInfoData = new RankInfoData();
        foreach (self::RANK_TYPE as $rankType) {
            $rankInfoData->setRankType($rankType);
            // 赛季
            $seasonID = 0;
            if(in_array($rankType, self::RANK_SEASON_TYPE)) {
                $seasonID = $this->getSeasonId();
            }
            $rankInfoData->setSeasonID($seasonID);

            // 地图 不会走这里 因为地图不更新
            if(in_array($rankType, self::RANK_MAP)) {
                $maps = $this->getTable(TemplateDefine::TYPE_PVP_MAP);
                foreach($maps as $id => $item) {
                    if($item[TemplatePVPMap::OpenIf] === 'True') {
                        $mapID = $item[TemplatePVPMap::Id];
                        $rankInfoData->setMapID($mapID);
                        $this->syncRankInfo($rankInfoData);
                    }
                }
            } else {
                $rankInfoData->setMapID(0);
                $this->syncRankInfo($rankInfoData);
            }
        }
        return true;
    }

    // send msg func
    public function sendRacingMsg(int $rankType, int $mapID, $res, $data, $page) {
        $pageCount = $this->getTerm(TemplateDefine::TYPE_CONST,
            TemplateConst::Const_Rank_Page_Max, "Const_num");
        $start = ($page - 1) * $pageCount;
        $end = $page * $pageCount - 1;
        $this->racingMsg->setRankType($rankType);
        $this->racingMsg->setMapID($mapID);
        $resRankData = [];
        $rankData = [];
        if(!empty($res)) {
            $rankData = json_decode($res, true);
        }
        if(!empty($data)) {
            $rankData = $data;
        }
        $sort = 0;
        foreach($rankData as $s => $info) {
            if($info[RankData::PLAYER_ID] == $this->playerId) {
                $sort = $s + 1;
                $racing = new RacingRank();
                $racing->setPlayerID($info[RankData::PLAYER_ID]);
                $racing->setScore($info[RankData::SCORE]);
                $racing->setBeLiked($info[RankData::BE_LIKED]);
                $racing->setCarExteriorRefits(json_encode($info[RankData::CAR_EXTERIOR_REFITS]));
                $racing->setCloth($info[RankData::CLOTHING]);
                $racing->setHead($info[AccountData::DB_HEAD]);
                $racing->setSkinColor($info[AccountData::DB_SKIN_COLOR] ?? 1);
                $racing->setLevel($info[AccountData::DB_LEVEL]);
                $racing->setGender($info[AccountData::DB_GENDER]);
                $racing->setNickName($info[AccountData::DB_NICK_NAME]);
                $racing->setCarTplID($info[AccountData::DB_DEFAULT_CAT_TPL]);
                $this->racingMsg->setMinRankData($racing);
                break;
            }
        }
        // 自己在排行榜中的排名 不在为0
        $this->racingMsg->setRankSort($sort);

        // 查询自己的排行榜数据并返回
        $score = $this->getMineRankData($rankType, $mapID);
        $this->racingMsg->setRankScore($score);
        for($i = $start; $i <= $end; $i++) {
            if(isset($rankData[$i])) {
                $racing = new RacingRank();
                $racing->setPlayerID($rankData[$i][RankData::PLAYER_ID]);
                $racing->setScore($rankData[$i][RankData::SCORE]);
                $racing->setBeLiked($rankData[$i][RankData::BE_LIKED]);
                $racing->setCarExteriorRefits(json_encode($rankData[$i][RankData::CAR_EXTERIOR_REFITS]));
                $racing->setCloth($rankData[$i][RankData::CLOTHING]);
                $racing->setHead($rankData[$i][AccountData::DB_HEAD]);
                $racing->setSkinColor($rankData[$i][AccountData::DB_SKIN_COLOR] ?? 1);
                $racing->setLevel($rankData[$i][AccountData::DB_LEVEL]);
                $racing->setGender($rankData[$i][AccountData::DB_GENDER]);
                $racing->setNickName($rankData[$i][AccountData::DB_NICK_NAME]);
                $racing->setCarTplID($rankData[$i][AccountData::DB_DEFAULT_CAT_TPL]);
                $resRankData[] = $racing;
            } else {
                break;
            }
        }
        // 我点赞的人
        $likes = [];
        $likeData = new RankLikesData();
        $likeData->playerID = $this->playerId;
        $likeData->getLikeKeys($likes);
        $likeArr = [];
        foreach ($likes as $item) {
            $likeArr[] = $item['playerID'];
        }

        $this->racingMsg->setPage($page);
        $this->racingMsg->setLikes($likeArr);
        $this->racingMsg->setRankData($resRankData);
        SendMessage::getInstance()->sendClient(PacketId::GC_RacingRankData, $this->racingMsg);
    }

    private function setRankLike(int $rankType, int $mapID, int $playerID, int $code) {
        $this->gcLikeMsg->setPlayerID($playerID);
        $this->gcLikeMsg->setCode($code);
    }

    public function sendRankLike() {
        SendMessage::getInstance()->sendClient(PacketId::GC_RankLike, $this->gcLikeMsg);
    }

    //查询地图挑战赛自己排名--0未上榜
    public function getMapChallengeRankNum(int $mapID): int
    {
        $rankType = self::MAP_CHALLENGE;
        if(!$this->checkParams($rankType, $mapID)) {
            return 0;
        }
        $seasonID = $this->getSeasonId();
        if(in_array($rankType, self::RANK_SEASON_TYPE)) {
            if($seasonID == 0) {
                return false;
            }
        } else {
            $seasonID = 0;
        }

        $rankData = new RankData($rankType, $seasonID, $mapID);
        $rankData->findRankData($ret);
        if (array_key_exists($this->playerId, $ret)) {
            return array_search($this->playerId, array_keys($ret)) + 1;
        }
        return 0;
    }
}