<?php

namespace loyalsoft;

include_once __DIR__ . '/../main.php';                                          # 导入主要工具库

/**
 *  API入口
 * @author gwang (mail@wanggangzero.cn)
 */
class AppServer {

    /**
     * The Main Entrance of App
     * @return Resp
     */
    public function api() {
        $uniq = uniqid();                                                       # 自己生成一个唯一串，作为此次处理过程的标记。
        $GLOBALS['deal-uid'] = $uniq;                                           # 写入日志的时候带上，方便辨认同一个处理过程.
        $GLOBALS['zoneid'] = req()->zoneid;                                     # 暂时未想到更好的方案 
        $err = self::BeforeProc();                                              # 预处理
        my_Assert(ErrCode::ok == $err, $err);                                   # 如果tk校验未通过
        $resp = self::Route();                                                  # 分发逻辑
        self::AfterProc($resp);                                                 # 执行后处理逻辑 
        return $resp;
    }

    /**
     * 预处理逻辑
     */
    private static function BeforeProc() {
        if (!GAME_ONLINE) {
            return ErrCode::ok;                                                 # 直接返回 0：成功
        }
        my_Assert(!config::Inst()->isBaned(req()->uid), ErrCode::err_server_updating); # 检查封号 
        if (self::isUpdating()                                                  # 检查是否处于更新阶段，暂停对玩家请求的响应。
                && !config::Inst()->isTester(req()->uid)) {                     # 排除测试号
            return ErrCode::err_server_updating;
        }
        $ssd = GameConfig::service_schedule_getItem(1);                         # 服务计划(固定只有1条)
        if (now() > $ssd->startts && now() < $ssd->endts) {                     # 在维护期间 
            Err(ErrCode::err_server_maintaining, $ssd->reason);
        }
        // todo: 检查clientVersion 判断是否应该强制升级  
        if (!Index::$isDebugging && now() - req()->ts > OFFSET_MSGTIME) {       # 如果服务端客户端时间戳超过规定误差，则消息非法
            return ErrCode::err_outtime;
        }
        return ErrCode::ok;                                                     # 所有检查通过
    }

    /**
     * 后处理逻辑
     * @param Resp $resp
     */
    private static function AfterProc(&$resp) {
        if (req()->userInfoChanged) {                                           # 回写玩家数据
            if (!UserProc::setUserInfo(req()->userInfo)) {                      # 失败, 改写返回值
                Err(ErrCode::err_innerfault, "回写玩家数据");
            }
        }
        $resp->AfterProc();
        GAME_ONLINE and self::LogCmd($resp);                                    # 记录操作日志
        CLog::flush();                                                          # flush日志
    }

    /**
     * 新版: 使用pdo_mysql+dao版本 
     * @param type $resp
     */
    private static function LogCmd($resp) {
        $tablename = 'tab_op_log' . date('Ymd');                                # 今天的表名
        $old_tablename = 'tab_op_log' . date('Ymd', now(-86400 * 21));          # 待删除的表名 日志保留21天 
        $sql = sprintf('create table if not exists %s like `tpl_op_log_tab`;', $tablename); # 创建今天的表
        $sql .= sprintf('drop table if exists %s;', $old_tablename);            # 循环删除以前的表
        $sql .= sprintf("insert into %s (`uid`,`zoneid`,`cmd`,`days`,`param`,`ret`) values ('%s', %d, %d, %d, '%s', '%s');", #
                $tablename, req()->uid, req()->zoneid, req()->cmd, #            # Uid, zoneid, cmd
                (isset(req()->userInfo) ? (tsDay() - tsDay(req()->userInfo->game->baseInfo->firstLogin)) : 0), #  # ps.留存天数
                JsonUtil::encode(req()->paras), #                               # req->paras
                JsonUtil::encode($resp->result)); #                             # resp->result
        daoInst()->exec($sql);                                                  # 执行sql
    }

    /**
     * 路由方法 
     */
    private static function Route() {
        $proc = OpeCode::getProc(req()->ope);                                   # 映射处理模块.
        my_Assert($proc != "err", ErrCode::ope_err);                            # 未能找到对应的处理模块
        try {
            $resp = call_user_func(array($proc, 'procMain'), req());            # 调用对应的处理逻辑
            if (!($resp instanceof Resp)) {                                     # 异常返回值
                Err(ErrCode::err_innerfault, JsonUtil::encode($resp));
            }
            return $resp;                                                       # 返回值
        } catch (\Exception $ex) {                                              # 异常信息写入日志
            $msg = CommUtil::str2UTF8($ex->getMessage());                       # 异常信息转下码 
            Err(ErrCode::err_unknownn, "call_user_func got Exception: $msg");   # 返回给客户端
        }
    }

    /**
     *  返回是否处于更新中
     * @return boolean
     */
    private static function isUpdating() {
        $ts = now();
        if ($ts > glc()->updatingBeginTs && $ts < glc()->updatingEndTs) {
            return true;
        }
        return false;
    }

}
