<?php
include "vendor/autoload.php";

// 调试输出
function vd($msg)
{
    print_r($msg);
    echo PHP_EOL;
}

function vde($msg)
{
    vd($msg);
    exit;
}

// 解析参数
function explainArgv($argv)
{
    $args = [];
    foreach ($argv as $value) {
        list($k, $v) = explode('=', $value);
        $k        = ltrim(ltrim(trim($k), '--'), '-');
        $v        = trim($v);
        $args[$k] = $v;
    }
    return $args;
}

function usage()
{
    echo '使用示例:' . PHP_EOL;
    echo 'php ./dump.php -app=test' . PHP_EOL;
//    echo 'php ./dump.php -app=test -split=true' . PHP_EOL;
}

class Dump
{
    // 文件夹路径符
    private static $DS = '/';

    /**
     * @var string
     */
    private $appName;

    /**
     * 是否需要分子目录
     *
     * @var boolean
     */
    private $split = false;

    /**
     * 源文件目录
     */
    private $sourceDir;

    /**
     * 脚本目录
     */
    private $scriptDir;

    /**
     * 导出文件目录
     */
    private $outputDir;

    /**
     * 生成json目录
     */
    private $jsonDir;

    /**
     * 生成model目录
     */
    private $modelDir;

    /**
     * 默认配置地址
     */
    private $defaultConfigFile;

    /**
     * Dump constructor.
     *
     * @param array $args
     */
    public function __construct($args)
    {
        if (!isset($args['app'])) {
            vd('参数错误');
            usage();
            exit;
        }
        // 初始化导出参数
        $this->appName = $args['app'];
        if (isset($args['split'])) {
            $this->split = $args['split'];
        }
        $this->sourceDir = __DIR__ . self::$DS . 'projects' . self::$DS . $this->appName . self::$DS . 'source';
        $this->scriptDir = __DIR__ . self::$DS . 'projects' . self::$DS . $this->appName . self::$DS . 'script';
        $this->outputDir = __DIR__ . self::$DS . 'projects' . self::$DS . $this->appName . self::$DS . 'output';
        $this->jsonDir   = __DIR__ . self::$DS . 'projects' . self::$DS . $this->appName . self::$DS . 'json';
        $this->modelDir  = __DIR__ . self::$DS . 'projects' . self::$DS . $this->appName . self::$DS . 'model';

        // 检查目录文件是否缺失
        if (!is_dir($this->sourceDir)) {
            vde('源文件目录缺失:' . $this->sourceDir);
        }
        if (!is_dir($this->scriptDir)) {
            vde('脚本目录缺失:' . $this->scriptDir);
        }
        if (!is_dir($this->outputDir)) {
            vde('输出配置目录缺失:' . $this->outputDir);
        }
        if (!is_dir($this->jsonDir)) {
            vde('输出json目录缺失:' . $this->jsonDir);
        }
        if (!is_dir($this->modelDir)) {
            vde('输出model目录缺失:' . $this->modelDir);
        }

        // 初始化默认配置文件
        $this->defaultConfigFile = __DIR__ . self::$DS . 'default-script.php';
    }

    /**
     * 执行主函数
     */
    public function run()
    {
        // 遍历源目录, 找出所有可执行的文件
        $files     = [];
        $sourceDir = dir($this->sourceDir);
        while ($file = $sourceDir->read()) {
            if ($file == '.' || $file == '..') {
                continue;
            }
            $pathInfo = pathinfo($file);
            // 现在仅支持xlsx文件
            if ($pathInfo['extension'] != 'xlsx') {
                continue;
            }
            $files[] = $pathInfo;
        }
        $sourceDir->close();
        if (empty($files)) {
            vde('源目录为空, 未找到.xlsx文件, 源目录:' . $this->sourceDir);
        }

        // 执行导出
        $success = 0;
        foreach ($files as $fileInfo) {
            if ($this->dump($fileInfo)) {
                ++$success;
            }
        }

        vd(sprintf("导出完成,共[%d]个文件, 成功[%d]个文件.", count($files), $success));
    }


    /**
     * 导出表
     *
     * @param array $fileInfo
     * @return bool
     */
    private function dump($fileInfo)
    {
        // 源文件
        $file = $this->sourceDir . self::$DS . $fileInfo['basename'];

        $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
        $reader->setReadDataOnly(true);
        $spreadsheet = $reader->load($file);
        // 读取所有sheet
        $sheetList = $spreadsheet->getSheetNames();
        foreach ($sheetList as $index => $sheetName) {
            $sourceData = $spreadsheet->getSheet($index)->toArray();
            $this->dumpSheet($fileInfo['basename'], $sheetName, $sourceData);
        }
    }

    /**
     * 导出sheet中的数据
     * @param string $fileName  文件名
     * @param string $sheetName sheet名
     * @param array $sourceData
     * @return boolean
     */
    private function dumpSheet($fileName, $sheetName, $sourceData)
    {

        // 找到文件对应的执行脚本
        // 如果找不到, 则使用默认的配置
        $script = $this->scriptDir . self::$DS . $sheetName . '.php';
        if (!file_exists($script)) {
            vd(sprintf('[%s][%s]对应的脚本文件不存在, 脚本文件:%s', $fileName, $sheetName, $script));
            //            return false;

            $script = $this->defaultConfigFile;
            vd(sprintf('[%s][%s]使用默认配置, 脚本文件:%s', $fileName, $sheetName, $script));
        }

        // 引入脚本
        include $script;

        // 处理excel文件内容
        if (count($sourceData) < $dataLine) {
            vd(sprintf('[%s][%s]文件没有数据', $fileName, $sheetName));
            return false;
        }

        // 如果未设置字段名列, 那么自动生成, 到处所有列
        if (empty($columns)) {
            foreach ($sourceData[$columnLine - 1] as $key => $columnName) {
                $columnName = trim($columnName);
                if (strlen($columnName) == 0) {
                    continue;
                }
                $columnType           = trim($sourceData[$typeLine - 1][$key]);
                $columns[$columnName] = $columnType;
            }
        }

        // 处理导出列对应的索引
        $indexes = [];
//        vd($sourceData[$columnLine - 1]);
        foreach ($sourceData[$columnLine - 1] as $index => $columnName) {
            $columnName = trim($columnName);
            if (isset($columns[$columnName])) {
                $indexes[$columnName] = $index;
            }
        }

        // 主键列
        if (!$keyColumn) {
            $keyColumn = current($sourceData[$columnLine - 1]);
        }

        // 普通处理逻辑
        $data = [];
        foreach ($sourceData as $k => $v) {
            // 非数据列不处理
            if ($k < $dataLine - 1) {
                continue;
            }

            $row = [];
            foreach ($columns as $columnName => $columnType) {
                $columnValue = trim($v[$indexes[$columnName]]);
                switch ($columnType) {
                    case 'float':
                        $columnValue = floatval($columnValue);
                        break;
                    case 'number':
                        $columnValue = intval($columnValue);
                        break;
                    case 'json':
                        $columnValue = json_decode($columnValue, true);
                        break;
                    default:
                        $columnValue = strval($columnValue);
                        break;
                }
                $row[$columnName] = $columnValue;
            }
            // 跳过空行
            if (!$row[$keyColumn]) {
                continue;
            }

            $data[$row[$keyColumn]] = $row;
        }

        // 额外处理逻辑
        $handle = $sheetName . '_handle';
        if (function_exists($handle)) {
            $data = $handle($data);
        }

        // 写入PHP文件
        $this->writePHP($sheetName, $data);

        // 生成json文件
        $this->writeJSON($sheetName, $data);

        // 生成model文件
        $this->writeModel($sheetName, $clientColumns);
    }

    /**
     * @param string $file 源文件地址
     * @return array
     */
    private function getDataFromSource($file)
    {
        $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();
//        $reader->setReadDataOnly(['Sheet 1']);
        $reader->setReadDataOnly(true);
        $spreadsheet = $reader->load($file);
//         $data        = $spreadsheet->getActiveSheet()->toArray();
        $data = $spreadsheet->getSheet(5)->toArray();

        vde($data);
        return $data;
    }

    /**
     * 写入php文件
     *
     * @param string $filename 写入文件路径
     * @param array $data      写入文件内容
     * @return void
     */
    private function writePHP($filename, $data)
    {
        $file = $this->outputDir . self::$DS . $filename . '.config.php';
        // 写入配置文件
        $configs = var_export($data, true);
        // 整理输出格式
        $config = <<<TXT
<?php
return {$configs};
TXT;

        // 写入导出文件
        file_put_contents($file, $config);
    }

    /**
     * 生成json文件
     *
     * @param string $filename 写入文件名称
     * @param array $data      待写入的文件内容
     */
    private function writeJSON($filename, $data)
    {
        $file = $this->jsonDir . self::$DS . $filename . '.json';
        // 写入导出文件
        file_put_contents($file, json_encode($data, JSON_UNESCAPED_UNICODE));
    }

    /**
     * 写入php model 文件
     *
     * @param string $filename     写入文件路径
     * @param array $clientColumns 输出给客户端的字段列表
     * @return void
     */
    private function writeModel($filename, $clientColumns)
    {
        $columnString = "[]";
        if (count($clientColumns) > 0) {
            $columnString = '["' . join('", "', $clientColumns) . '"]';
        }
        $className = ucwords($filename);
        $file      = $this->modelDir . self::$DS . $className . '.php';
        // 整理输出格式
        $config = <<<TXT
<?php

class {$className}
{
    /**
     * Returns table name mapped in the model.
     *
     * @return string
     */
    public static function getSource()
    {
        return '{$filename}';
    }

    /**
     * 获取导出给客户端所需要的字段
     *
     * @return array
     */
    public static function getToClientColumns()
    {
         return {$columnString};
    }

    /**
     * 生成客户端需要的字段, 生成列表
     *
     * @return array
     */
    public static function getListForClient()
    {
        \$data = [];
        if (empty(self::getToClientColumns())) {
            \$data = array_values(self::getList());
        } else {
            foreach (self::getList() as \$key => \$value) {
                foreach (\$value as \$k => \$v) {
                    if (!in_array(\$k, self::getToClientColumns())) {
                        unset(\$value[\$k]);
                    }
                }
                \$data[] = \$value;
            }
        }
        return \$data;
    }

    /**
     * 获取单条配置内容
     *
     * @return array
     */
    public static function get(\$configId)
    {
        return IConfig::get()->loadAppConfig(self::getSource(), \$configId);
    }

    /**
     * 获取所有配置
     *
     * @return array
     */
    public static function getList()
    {
        return IConfig::get()->loadAppConfig(self::getSource());
    }
}

TXT;

        // 写入导出文件
        file_put_contents($file, $config);
    }
}


/*
 * 使用说明
 * php ./dump.php -app=shot -split=false
 */
// 读取 & 解析命令行参数
array_shift($argv);
$args = explainArgv($argv);
// 执行导出
$dump = new Dump($args);
$dump->run();
