<?php
require_once dirname(dirname(__FILE__)).'/util/HttpClient.php';
require_once dirname(dirname(__FILE__)).'/util/ConfigHelper.php';
require_once dirname(dirname(__FILE__)).'/util/LoggerHelper.php';

require_once dirname(dirname(__FILE__)).'/model/DomainInfo.php';
require_once dirname(dirname(__FILE__)).'/constant/StateCode.php';
require_once dirname(dirname(__FILE__)).'/constant/ServiceName.php';
require_once dirname(dirname(__FILE__)).'/constant/SDKVersion.php';
require_once dirname(dirname(__FILE__)).'/service/DomainServerService.php';

/**
 *  请求服务端数据组装类
 */
class BaseSDKService{

    protected static function getSDKServerResponse (array $params, $prefix, $serviceName){
        return BaseSDKService::getServerResponse(ConfigHelper::getStrVal("sdkserver.baseUrl"),
            ConfigHelper::getStrValWithDefault("sdkserver.baseUrl.port", ServiceName::$SDK_SERVER_PORT),
            self::assemblyHyParam(ServiceName::$DOMAINSERVER_REQ_DN,ServiceName::$ACCOUNT_ID),
            $params, $prefix, $serviceName, ServiceName::$DOMAINSERVER_REQ_DN);
    }

    protected static function getSDKGameDataResponse (array $params, $prefix, $serviceName){
        return BaseSDKService::getServerResponse(ConfigHelper::getStrVal("sdkgamedata.baseUrl"),
            ConfigHelper::getStrValWithDefault("sdkgamedata.baseUrl.port", ServiceName::$SDK_GAMEDATA_PORT),
            self::assemblyHyParam(ServiceName::$DOMAINSERVER_GAMEDATA_DN,ServiceName::$ACCOUNT_ID),
            $params, $prefix, $serviceName, ServiceName::$DOMAINSERVER_GAMEDATA_DN);
    }

    protected static function getSDKRealNameResponse(array $params, $prefix, $serviceName){
        return BaseSDKService::getServerResponse(ConfigHelper::getStrVal("sdkrealname.baseUrl"),
            ConfigHelper::getStrValWithDefault("sdkrealname.baseUrl.port", ServiceName::$SDK_SERVER_PORT),
            self::assemblyHyParam(ServiceName::$DOMAINSERVER_REALNAME_DN,ServiceName::$ACCOUNT_ID),
            $params, $prefix, $serviceName, ServiceName::$DOMAINSERVER_REALNAME_DN);
    }


    /**
     * 获取SDKServer服务端的响应
     * @param array $params
     * @param $prefix
     * @param $serviceName
     * @return StdClass
     * @throws SDKException
     */
    private static function getServerResponse($serverBaseUrl, $serverPort, $domainServerReqBody, array $params, $prefix, $serviceName, $dn){
        try {
            $domainService = new DomainServerService();
            $requestParam = BaseSDKService::assemblyParameters($serviceName,$params);
            $requestBody = json_encode($requestParam);//把参数序列化成一个json字符串
            $responseBody = false;
            $domainCache = array();
            $domainSwith = ConfigHelper::getStrVal("sdkserver.domain.swith");
            if ($domainSwith == 'on' && $domainService->cacheOpen()) {
                $domainCache = $domainService->getDomainByCache($dn);
                $domainService->getHyInfoByCache(ServiceName::$ACCOUNT_ID);
            }

            $refCache = false;
            try {
                if(!empty($domainCache) && isset($domainCache['ipAddress']) && !empty($domainCache['ipAddress'])){
                    LoggerHelper::info("智能域名解析系统缓存存在，直接使用智能域名解析系统返回的IP".json_encode($domainCache['ipAddress'])."访问服务端.");
                    $responseBody = self::loopPost($domainCache['ipAddress'], $serverPort, $prefix , $serviceName, $requestBody);
                    $refCache = true;
                }else{
                    $responseBody = HttpClient::quickPost(BaseSDKService::assemblyUrl($serverBaseUrl, $serverPort, $prefix , $serviceName), $requestBody);
                }
            }catch (Exception $e) {
                $responseBody = false;
            }

            $responseObj = json_decode($responseBody, true);
            if ($domainSwith == 'on' && !is_array($responseObj)) {

                $domainInfo = $domainService->getDomainByServer(ServiceName::$ACCOUNT_ID, $dn, $domainServerReqBody, $refCache);
                if($domainInfo == null){
                    LoggerHelper::info("请求常规接口".BaseSDKService::assemblyUrl($serverBaseUrl, $serverPort, $prefix , $serviceName)."失败，请求智能域名解析也失败.");
                    throw new SDKException("请求接口失败");
                }
                $responseBody = self::loopPost($domainInfo['ipAddress'], $serverPort, $prefix , $serviceName, $requestBody);
                $responseObj = json_decode($responseBody, true);
            }
            if (isset($responseObj["state"]["code"]) && $responseObj["state"]["code"] != StateCode::$SUCCESS) {
                //接口返回失败，以异常的形式抛出
                LoggerHelper::info("请求接口状态码不为成功.".$responseBody);
                throw new SDKException($responseObj["state"]["msg"], $responseObj["state"]["code"]);
            }
            if (isset($responseObj["data"])) {
                return $responseObj["data"];
            } else {
                LoggerHelper::info("接口数据返回异常！");
                throw new SDKException("接口数据返回异常！", 99);
            }
        } catch (Exception $e) {
            if($e instanceof SDKException){
                throw $e;
            }
            LoggerHelper::info("请求接口调用出错.".$e->getMessage());
            throw new SDKException($e->getMessage(), -1);
        }
    }

    /**
     * 获取毫秒级的时间参数
     *
     */
     private static  function getMillisecond() {
        $time = explode ( " ", microtime () );
        $time = $time [1] . ($time [0] * 1000);
        $time2 = explode ( ".", $time );
        $time = $time2 [0];
        return $time;
    }

    /**
     * 按字母排序$params数组d的键,返回键.值的加密内容
     * @param $data 业务数据
     */
     public static function getSignData(array $params){
        ksort($params);
        $enData = '';
        foreach( $params as $key=>$val ){
            $enData = $enData.$key.'='.$val;
        }
        return $enData;
    }

    /**
     * 按字母排序$params数组d的键,返回键.值的加密内容
     * @param $data 业务数据
     */
     public static function getSignDataWithoutNotInKey(array $params, array $notInKey){
        ksort($params);
        $enData = '';
        foreach( $params as $key=>$val ){
            if(in_array($key, $notInKey)){
                continue;
            }
            $enData = $enData.$key.'='.$val;
        }
        return $enData;
    }

    /**
     * 封装访问入参数据
     * @param $serviceName
     * @param array $params
     * @return array
     */
     private static function assemblyParameters($serviceName,array $params){
        /////////////////组装游戏参数-开始/////////////////////
        $gameParam = array();
        //gameid是在游戏接入时由UC平台分配
        $gameParam["gameId"] = ConfigHelper::getIntVal("sdkserver.game.gameId");
        //////////////////组装游戏参数-结束////////////////////
        /*
        签名规则=签名内容.apiKey
        假定apiKey=202cb962234w4ers2aaa,sid=abcdefg123456
        那么签名原文sid=abcdefg123456202cb962234w4ers2aaa
        签名结果6e9c3c1e7d99293dfc0c81442f9a9984
        */
        $signSource = BaseSDKService::getSignData($params).ConfigHelper::getStrVal("sdkserver.game.apikey");//组装签名原文
        $sign = md5($signSource);//MD5加密签名

        $ex = array();
        $ex["ex"] = "language:".SDKVersion::$LANGUAGE ."|version:".SDKVersion::$VERSION;
        ///////////////////组装请求参数-开始////////////////////
        $requestParam = array();
        $requestParam["id"] = BaseSDKService::getMillisecond();//当前系统时间（毫秒）
        $requestParam["service"] = $serviceName ;//"account.verifySession";
        $requestParam["game"] = $gameParam;
        $requestParam["client"] = $ex;
        if(count($params) == 0){
            $requestParam["data"] = new stdClass();
        }else{
            $requestParam["data"] = $params;
        }
        $requestParam["encrypt"] = "md5";
        $requestParam["sign"] = $sign;
        ///////////////////组装请求参数-结束/////////////////////

        return $requestParam;

    }

    private static function assemblyUrl($sdkServerBaseUrl, $serverPort, $prefix, $serviceName){
        return $sdkServerBaseUrl.":".$serverPort."/".$prefix.$serviceName;
    }

    private static function assemblyHyParam($dn, $accountId) {
        $valid = strtolower(md5($dn."aligame"));
        return "dn=".$dn."&accountId=".$accountId."&valid=".$valid;
    }
    private static function loopPost($ips, $serverPort, $prefix , $serviceName, $data) {
        if (empty($ips) || !is_array($ips)) {
            return false;
        }
        $i = 1;
        $begin = time();
        $content = array();
        foreach ($ips as $ip) {
            $url = self::assemblyUrl("http://".$ip, $serverPort, $prefix , $serviceName);
            $content = json_decode(self::curlPost($url, $data, 3),true);
            if (!empty($content) && is_array($content)) {
                LoggerHelper::info("请求成功，地址：".$url."。data：".$data."。return:".json_encode($content));
                break;
            } else {
                LoggerHelper::info("请求失败，地址：".$url."。data：".$data."。return:".json_encode($content));
            }
            if ($i >= 3 || time() - $begin >= 5) {
                break;
            }
            $i++;
        }
        return json_encode($content);

    }

    private static function curlPost($url, $data, $timeout) {
        $curl = curl_init();
        // 设置你需要抓取的URL
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $timeout);//设置超时时间
        curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);//设置超时时间
        curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');

        if (preg_match("/^https:\/\//", $url)) {
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);   //不对认证证书来源的检查
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, true);    //从证书中检查SSL加密算法是否存在
        }
        //设置header
        curl_setopt($curl, CURLOPT_HEADER, 0);
        //设置cURL 参数，要求结果保存到字符串中还是输出到屏幕上。
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        // 运行cURL，请求网页
        $document  = curl_exec($curl);
        if (curl_errno($curl)) {
            return false;
        }
        // 关闭URL请求
        curl_close($curl);
        return $document;
    }

}