<?php

/**
 * Utils.php
 * 文件描述
 * Created On 2022/1/19
 * @author yuanb yuanbo0x@gmail.com
 */

namespace Framework\Lib;

use DateTimeImmutable;
use DateTimeZone;
use Game\Config\GameConfig;
use Game\Constant\DBTableDefine;
use Game\Constant\TestParams;
use Game\Model\Rank\RankModel;

class Utils
{
    public static function makeObjectID($serverId, $modelType, $index): int
    {
        // type 7 , serverId 10 , index 15 , time 32
        $time = self::getServerTimestamp();
        $ret = (($modelType & 0x0000007F) << 25) | (($serverId & 0x000003FF) << 15) | ($index & 0x00007FFF);
        $ret = dechex($ret) . dechex($time);
        $a = gmp_init($ret, 16);
        return gmp_strval($a);
    }

    public static function makeDBKey($tableType, $playerId, $titleId): string
    {
        $db_t = DBTableDefine::DB_TABLE_TYPE[$tableType];
        $tableName = DBTableDefine::TABLE_LIST[$tableType];
        if(isset(TestParams::$stack[$tableName])) {
            TestParams::$stack[$tableName] += 1;
        } else {
            TestParams::$stack[$tableName] = 1;
        }
        $key = "";
        switch ($db_t) {
            case DBTableDefine::DB_TYPE_PLAYER:
                $key = "{" . GameConfig::getInstance()->SERVER_ID() . "-" . $db_t . "-" . $playerId . "}"
                    . "-" . $tableName . "-" . $titleId;
                break;
            case DBTableDefine::DB_TYPE_SERVER:
                $key = GameConfig::getInstance()->SERVER_ID() . "-" . $db_t . "-" . $tableName . "-" . $playerId;
                break;
            case DBTableDefine::DB_TYPE_FULL_SERVER_CLUSTER:
                $key = "{" . $db_t . "-" . $titleId . "}" . "-" . $tableName;
                break;
            case DBTableDefine::DB_TYPE_FULL_SERVER_SINGLE:
                $key = $db_t . "-" . $tableName;
                break;
            case DBTableDefine::DB_TYPE_SERVICE:
                $key = "{" . $titleId . "}" . "-" . $tableName;
                break;
        }
        return $key;
    }

    public static function makeRankDbKey($tableType, $rankType = 0, $seasonID = 0, $mapID = 0, $pid = 0): string
    {
        $db_t = DBTableDefine::DB_TABLE_TYPE[$tableType];
        $tableName = DBTableDefine::TABLE_LIST[$tableType];
        if(isset(TestParams::$stack[$tableName])) {
            TestParams::$stack[$tableName] += 1;
        } else {
            TestParams::$stack[$tableName] = 1;
        }
        $serverID = GameConfig::getInstance()->SERVER_ID();

        if (in_array($rankType, RankModel::CLUB_MAP)) {
            $serverID = GameConfig::getInstance()->RANK_SERVER_ID()[$serverID];
        }
        $key = "";
        switch ($tableType) {
            case DBTableDefine::TABLE_RANKING:
            case DBTableDefine::TABLE_RANKING_INFO:
            case DBTableDefine::TABLE_RANK_SYNC_TIME:
            case DBTableDefine::TABLE_RANK_CACHE:
                $key = "{" . $serverID . "-" . $db_t . "}" . "-" . $tableName
                    . "-" . $rankType . "-" . $seasonID . "-" . $mapID;
                break;
            case DBTableDefine::TABLE_RANK_LIKE:
            case DBTableDefine::TABLE_RANK_BE_LIKED:
                $key = "{" . $serverID . "-" . $db_t . "}" . "-" . $tableName
                    . "-" . $rankType . "-" . $seasonID . "-" . $mapID . "-" . $pid;
                break;
        }
        return $key;
    }

    public static function makeDBIndex($tableType, $playerId): string
    {
        $db_t = DBTableDefine::DB_TABLE_TYPE[$tableType];
        $tableName = DBTableDefine::TABLE_LIST[$tableType];
        if(isset(TestParams::$stack[$tableName])) {
            TestParams::$stack[$tableName] += 1;
        } else {
            TestParams::$stack[$tableName] = 1;
        }
        $key = "";
        switch ($db_t) {
            case DBTableDefine::DB_TYPE_PLAYER:
                $key = "{" . GameConfig::getInstance()->SERVER_ID() . "-" . $db_t . "-" . $playerId . "}"
                    . "-" . $tableName;
                break;
            case DBTableDefine::DB_TYPE_SERVER:
                $key = GameConfig::getInstance()->SERVER_ID() . "-" . $db_t . "-" . $tableName . "-" . $playerId;
                break;
            case DBTableDefine::DB_TYPE_FULL_SERVER_CLUSTER:
                $key = "{" . $db_t . "-" . $playerId . "}" . "-" . $tableName;
                break;
        }
        return $key;
    }

    // titleID 后置key
    public static function makeFullServerKey($tableType, $playerId, $titleID): string
    {
        $db_t = DBTableDefine::DB_TABLE_TYPE[$tableType];
        $tableName = DBTableDefine::TABLE_LIST[$tableType];
        if(isset(TestParams::$stack[$tableName])) {
            TestParams::$stack[$tableName] += 1;
        } else {
            TestParams::$stack[$tableName] = 1;
        }
        $key = "";
        switch ($db_t) {
            case DBTableDefine::DB_TYPE_PLAYER:
                $key = "{" . GameConfig::getInstance()->SERVER_ID() . "-" . $db_t . "-" . $playerId . "}"
                    . "-" . $tableName;
                break;
            case DBTableDefine::DB_TYPE_FULL_SERVER_CLUSTER:
                $key = "{" . $db_t . "-" . $playerId . "}" . "-" . $tableName . "-" . $titleID;
                break;
        }
        return $key;
    }

    public static function makeServerDBKey($tableType, $playerId, $titleId, $serverID): string
    {
        $db_t = DBTableDefine::DB_TABLE_TYPE[$tableType];
        $tableName = DBTableDefine::TABLE_LIST[$tableType];
        if(isset(TestParams::$stack[$tableName])) {
            TestParams::$stack[$tableName] += 1;
        } else {
            TestParams::$stack[$tableName] = 1;
        }
        $key = "";
        switch ($db_t) {
            case DBTableDefine::DB_TYPE_PLAYER:
                $key = "{" . $serverID . "-" . $db_t . "-" . $playerId . "}"
                    . "-" . $tableName . "-" . $titleId;
                break;
            case DBTableDefine::DB_TYPE_SERVER:
                $key = $serverID . "-" . $db_t . "-" . $tableName . "-" . $playerId;
                break;
        }
        return $key;
    }

    /**
     *
     * @param string $param
     * @return bool
     */
    public static function IsJson(string $param): bool
    {
        if (strpos($param, "{") !== false) {
            return true;
        }
        if (strpos($param, "[") !== false) {
            return true;
        }
        return false;
    }

    /**
     * 生成随机字符串, 包括字母,数字
     * @param int $num 生成长度
     * @return string
     */
    public static function RandomChar(int $num): string
    {
        if ($num <= 0) {
            return "";
        }
        $chars = [];
        $result = "";
        for ($i = 0; $i < $num * 3; $i++) {
            $chars[$tmp1] = $tmp1 = random_int(0, 9);     //数字
            $chars[$tmp2] = $tmp2 = random_int(65, 90);   //大写字母
            $chars[$tmp3] = $tmp3 = random_int(97, 122);  //小写字母
        }
        $chars = array_rand($chars, $num);
        if (!is_array($chars)) {
            $chars = [$chars];
        }
        foreach ($chars as $c) {
            if ($c > 9) {
                $result .= chr($c);
                continue;
            }
            $result .= $c;
        }
        return $result;
    }

    /**
     * @param $serverId
     * @param $channelId
     * @param $index
     * @return int
     */
    public static function uniqueRoleId($serverId, $channelId, $index): int
    {
        //  server 1023  channel 1023   index 131071
        $ret = (($serverId & 0x000003FF) << 27) | (($channelId & 0x000003FF) << 17) | ($index & 0x0003FFFF);
        $ret = dechex($ret);
        $a = gmp_init($ret, 16);
        $a = gmp_strval($a);
        return $a;
    }

    /**
     * 生成好友搜索表的分数值
     * @param int $gender 0女 1男
     * @param int $level 1-127
     * @param int $city
     * @return string 女-段位 1-9 | 男-段位 17-25
     */
    public static function makeFriendsScore(int $gender, int $level, int $city = 0)
    {
        // level 63 gender 3
        $ret = (($gender & 0x00000003) << 6) | ($level & 0x0000003f);
        $ret = dechex($ret);
        $a = gmp_init($ret, 16);
        $a = gmp_strval($a);
        return $a;
    }

    /**
     * 以描述生成不可变的时间戳
     * @param string $timeDescribe 参考strtotime函数
     * @param string $timeZone
     * @return int
     */
    public static function makeImmutableTime(string $timeDescribe, string $timeZone = "Asia/ShangHai")
    {
        if ($timeDescribe == "") {
            return self::getServerTimestamp();
        }
        try {
            return (new DateTimeImmutable($timeDescribe, new DateTimeZone($timeZone)))->getTimestamp();
        } catch (\Exception $e) {
            return 0;
        }
    }

    //获取周x英文名 $week 1-7
    public static function getWeekEn(int $week): string
    {
        $weekDay = array(
            1 => "Monday",
            2 => "Tuesday",
            3 => "Wednesday",
            4 => "Thursday",
            5 => "Friday",
            6 => "Saturday",
            7 => "Sunday",
        );
        return $weekDay[$week];
    }

    //检查是否跨天 true跨天
    public static function checkIsCrossDay(int $time): bool
    {
        if ($time == 0) {
            return false;
        }
        return date("Y-m-d", $time) !== date("Y-m-d");
    }

    //检查是否跨周 true跨周
    public static function checkIsCrossWeek(int $time): bool
    {
        if ($time == 0) {
            return false;
        }
        return date("nW", $time) !== date("nW");
    }

    //检查是否跨月 true跨月
    public static function checkIsCrossMonth(int $time): bool
    {
        if ($time == 0) {
            return false;
        }
        return date("n", $time) !== date("n");
    }

    public static function getServerTimestamp(): int
    {
        return time();
    }

    public static function getServerDate()
    {
        return date("Y-m-d h:i:s", self::getServerTimestamp());
    }

    //按对应时区格式化时间
    public static function getDateByTimeZone(int $zone)
    {
        return date("Y-m-d H:i:s", strtotime(gmdate("Y-m-d H:i:s"). " +{$zone} Hours"));
    }

    //获取天数间隔,targetTime>nowTime 间隔天数为正,
    public static function getGapDay(int $targetTime, int $nowTime = 0): int
    {
        if ($nowTime == 0) {
            $nowTime = self::getServerTimestamp();
        }
        $origin = new DateTimeImmutable(date("Y-m-d", $nowTime));
        $target = new DateTimeImmutable(date("Y-m-d", $targetTime));
        return $origin->diff($target)->format('%R%a');
    }

    public static function getClientIp()
    {
        if (getenv('HTTP_CLIENT_IP')) {
            $ip = getenv('HTTP_CLIENT_IP');
        }
        if (getenv('HTTP_X_REAL_IP')) {
            $ip = getenv('HTTP_X_REAL_IP');
        } elseif (getenv('HTTP_X_FORWARDED_FOR')) {
            $ip = getenv('HTTP_X_FORWARDED_FOR');
            $ips = explode(',', $ip);
            $ip = $ips[0];
        } elseif (getenv('REMOTE_ADDR')) {
            $ip = getenv('REMOTE_ADDR');
        } else {
            $ip = '0.0.0.0';
        }

        return $ip;
    }

    /**
     * 多个二维数组合并 键值都不变 覆盖相同key
     * @param array ...$arrays  [key=>value,..]
     * @return array
     */
    public static function arrMerge(array ...$arrays): array {
        $res = [];
        foreach ($arrays as $array) {
            foreach ($array as $key => $value) {
                $res[$key] = $value;
            }
        }
        return $res;
    }
}
