<?php

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

namespace Tool\LoadTemplate;

use Framework\Define\ErrorDefine;
use Framework\Log\LogMark;
use Game\Config\GameConfig;
use Game\Constant\TemplateDefine;
use Game\Constant\TemplateDefineBase;
use Throwable;
use Tool\LoadTemplate\Logic\UidIndexLogic;

class Load
{
    use Log;

    private array $fileList;

    private LoadSingle $single;

    private Logic $logic;

    private Save $saveDB;

    private Tidy $tidy;

    public function __construct()
    {
        $this->registerHandler();
        LogMark::getInstance()->init(GameConfig::getInstance()->LOG_PATH());
        $this->fileList = array();
        $this->single = new LoadSingle();
        $this->logic = new Logic();
        $this->saveDB = new Save();
        $this->tidy = new Tidy($this->saveDB);
    }

    public function deal(): bool
    {
        if ($this->getAllFile() == false) {
            return false;
        }
        $index = 1;
        foreach ($this->fileList as $type => $filePath) {
            $tt1 = time();
            if ($this->dealSingle($type, $filePath) == false) {
                $log = sprintf(
                    "index[%d] table[%s] update Fail" . PHP_EOL,
                    $type,
                    $filePath
                );
                $this->addError($log);
                return false;
            }
            $tt2 = time();
            $log = sprintf(
                PHP_EOL . "index[%d] success update table[%s] use time[%d]" . PHP_EOL,
                $type,
                $filePath,
                $tt2 - $tt1
            );
            $this->addInfo($log);
            ++$index;
        }
        //初始uid index
        $this->dealUidIndex();
        return true;
    }

    private function dealSingle($type, $filePath): bool
    {
        $tplConstPath = Config::getTemplateName($type);
        if ($tplConstPath == "") {
            $this->addError("Server not use template:" . $filePath);
            return true;
        }
        //删除共享内存旧内容
        if (false == $this->saveDB->delCache($type)) {
            $this->addError("Delete memory cache error template type:" . $type);
            return false;
        }
        if ($this->saveDB->initCache($type) == false) {
            $this->addError("Create memory cache error template type:" . $type);
            return false;
        }
        $this->logic->init($type);
        /**
         * @var TemplateDefineBase $obj
         */
        $obj = new $tplConstPath();
        $tplDefine = $obj->getTermList();
        $this->single->cleanUp();
        $this->single->load($filePath, $this->logic, $tplDefine);
        if (count($this->single->getContext()) == 0) {
            $this->addInfo("file " . $filePath . " have no data");
            return true;
        }
        if (false == $this->saveDB->updateTable($this->single->getContext())) {
            $this->addError("save template to memory cache error  type:" . $type);
            return false;
        }
        $tidyList = $this->logic->getTidyList();
        if (count($tidyList) == 0) {
            return true;
        }
        if (false == $this->tidy($type, $tidyList, $this->single->getContext())) {
            $this->addError("tidy error  type:" . $type);
            return false;
        }
        return true;
    }

    //处理生成Uid通用的Index
    private function dealUidIndex(): bool
    {
        $type = TemplateDefine::TYPE_UID_INDEX;
        $tplConstPath = Config::getTemplateName($type);
        //删除共享内存旧内容
        if (false == $this->saveDB->delCache($type)) {
            $this->addError("Delete memory cache error template type:" . $type);
            return false;
        }
        if ($this->saveDB->initCache($type) == false) {
            $this->addError("Create memory cache error template type:" . $type);
            return false;
        }
        $this->logic->init($type);
        $this->single->cleanUp();
        $logic = new UidIndexLogic();
        $data = $logic->initIndex();
        $this->saveDB->updateTable($data);
        return true;
    }

    private function tidy($type, $tidyList, $context): bool
    {
        foreach ($tidyList as $tidy) {
            if (!$this->tidy->ResetLogic($tidy)) {
                $log = sprintf("update Table[%s] Fail Because tidy Reset Logic[%s] Fail!! ", $type, $tidy);
                $this->addError($log);
                return false;
            }
            if (!$this->tidy->DealLogic($context)) {
                $log = sprintf("update Table[%s] Fail Because tidy deal Logic Fail!! ", $type);
                $this->addError($log);
                return false;
            }
            if (!$this->tidy->Save($this->saveDB)) {
                $log = sprintf("update Table[%s] Fail Because tidy save db Fail!! ", $type);
                $this->addError($log);
                return false;
            }
        }
        return true;
    }

    private function getAllFile(): bool
    {
        $fileList = Config::FILE_LIST;
        foreach ($fileList as $type => $fileName) {
            $file_path = Config::DATA_PATH . $fileName . ".txt";
            if (file_exists($file_path) == false) {
                $log = sprintf("tpl[%s] not exist", $file_path);
                $this->addError($log);
                return false;
            }
            $this->fileList[$type] = $file_path;
        }
        return true;
    }

    private function registerHandler()
    {
        set_exception_handler(function (Throwable $exception) {
            $this->catchException($exception);
            echo "Uncaught exception: " , $exception->getMessage(), "\n";
        });

        set_error_handler(function (int $number, string $message, string $error_file, int $error_line) {
            $log = "catch error";
            LogMark::getInstance()->markCritical(
                ErrorDefine::EXCEPTION_ERROR,
                $log,
                array("errorInfo" => $message, "code" => $number, "file" => $error_file, "line" => $error_line)
            );
            echo "Handler captured error $number: '$message' file:'$error_file' row:'$error_line'" . PHP_EOL  ;
        });

        register_shutdown_function(function () {
            // todo 程序结束后，检查是否有报错情况
        });
    }

    private function catchException(Throwable $exception)
    {
        $log = "catch exception";
        LogMark::getInstance()->markCritical(
            ErrorDefine::EXCEPTION_ERROR,
            $log,
            array("exception" => $exception->getMessage(), "code" => $exception->getCode(),
                "file" => $exception->getFile(), "line" => $exception->getLine(),
                "trace" => $exception->getTraceAsString())
        );
    }
}
