<?php

namespace OT;
class DataDictionary{

    public function __construct($table){
        $this->headers = $table['header'];
        $this->rows = $table['rows'];
        $this->crossingChar = '+';
        $this->horizontalBorderChar = '-';
        $this->verticalBorderChar = '|';
        $this->borderFormat = '%s';
        $this->cellHeaderFormat = '%s';
        $this->cellRowFormat = '%s';
        $this->paddingChar = ' ';
        $this->padType = STR_PAD_RIGHT;
    }
    /**
     * Renders table to output.
     *
     * Example:
     * +---------------+-----------------------+------------------+
     * | ISBN          | Title                 | Author           |
     * +---------------+-----------------------+------------------+
     * | 99921-58-10-7 | Divine Comedy         | Dante Alighieri  |
     * | 9971-5-0210-0 | A Tale of Two Cities  | Charles Dickens  |
     * | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
     * +---------------+-----------------------+------------------+
     *
     */
    public function render($out = true){
        if(!$this->rows)
            exit('invalid table content');
        //获得表头行首 +---------------+-----------------------+------------------+
        $output = $this->renderRowSeparator();
        //获取头部输出| ISBN          | Title                 | Author           |
        $output .= $this->renderRow($this->headers, $this->cellHeaderFormat);
        //header存在的话再输出行分割符
        if ($this->headers) {
            $output .= $this->renderRowSeparator();
        }
        //渲染每一行
        foreach ($this->rows as $row) {
            $output .= $this->renderRow($row, $this->cellRowFormat);
        }
        if ($this->rows) {
            $output .= $this->renderRowSeparator();
        }
        if($out){
            exit($output);
        }else{
            $this->cleanup();
            return $output;
        }
    }

    public function renderWitoutHeadTail($out = true){
        if(!$this->rows)
            exit('invalid table content');
        //获取头部输出| ISBN          | Title                 | Author           |
        $output .= $this->renderRow($this->headers, $this->cellHeaderFormat);
        //header存在的话再输出行分割符
        if ($this->headers) {
            $output .= $this->renderRowSeparator();
        }
        //渲染每一行
        foreach ($this->rows as $row) {
            $output .= $this->renderRow($row, $this->cellRowFormat);
        }
        if($out){
            print($output);
        }else{
            $this->cleanup();
            return $output;
        }
    }

    //渲染表格行起始分割行
    private function renderRowSeparator(){
        if (0 === $count = $this->getNumberOfColumns()) {
            return;
        }

        $markup = $this->crossingChar;
        for ($column = 0; $column < $count; $column++) {
            $markup .= str_repeat($this->horizontalBorderChar, $this->getColumnWidth($column))
                    .$this->crossingChar
            ;
        }

        return sprintf($this->borderFormat, $markup).PHP_EOL;
    }

    /**
     * 渲染表格行.
     *
     * Example: | 9971-5-0210-0 | A Tale of Two Cities  | Charles Dickens  |
     *
     * @param array  $row
     * @param string $cellFormat
     */
    private function renderRow(array $row, $cellFormat){
        if (empty($row)) {
            return;
        }

        $output = $this->renderColumnSeparator();
        for ($column = 0, $count = $this->getNumberOfColumns(); $column < $count; $column++) {
            $output .= $this->renderCell($row, $column, $cellFormat);
            $output .= $this->renderColumnSeparator();
        }
        $output .= $this->writeln('');
        return $output;
    }

     /**
     * 带边距的渲染单元格.
     *
     * @param array   $row
     * @param integer $column
     * @param string  $cellFormat
     */
    private function renderCell(array $row, $column, $cellFormat){
        $cell = isset($row[$column]) ? $row[$column] : '';
        return sprintf(
            $cellFormat,
            $this->str_pad(
                $this->paddingChar.$cell.$this->paddingChar,
                $this->getColumnWidth($column),
                $this->paddingChar,
                $this->padType
            )
        );
    }

    /**
     * 渲染水平列分隔符.
     */
    private function renderColumnSeparator(){
        return(sprintf($this->borderFormat, $this->verticalBorderChar));
    }

     /**
     * 获取表格的列数.
     *
     * @return int
     */
    private function getNumberOfColumns() {
        if (null !== $this->numberOfColumns) {
            return $this->numberOfColumns;
        }

        $columns = array(0);
        $columns[] = count($this->headers);
        foreach ($this->rows as $row) {
            $columns[] = count($row);
        }

        return $this->numberOfColumns = max($columns);
    }

     /**
     * 获取列宽.
     *
     * @param integer $column
     *
     * @return int
     */
    private function getColumnWidth($column) {
        if (isset($this->columnWidths[$column])) {
            return $this->columnWidths[$column];
        }

        $lengths = array(0);
        $lengths[] = $this->getCellWidth($this->headers, $column);
        foreach ($this->rows as $row) {
            $lengths[] = $this->getCellWidth($row, $column);
        }

        return $this->columnWidths[$column] = max($lengths) + 2;
    }

    /**
     * 获取单元格宽度.
     *
     * @param array   $row
     * @param integer $column
     *
     * @return int
     */
    private function getCellWidth(array $row, $column) {
        if ($column < 0) {
            return 0;
        }

        if (isset($row[$column])) {
            return $this->strlen($row[$column]);
        }

        return $this->getCellWidth($row, $column - 1);
    }

    /**
     * Returns the length of a string, using mb_strlen if it is available.
     *
     * @param string $string The string to check its length
     *
     * @return integer The length of the string
     */
    protected function strlen($string) {
        // if (!function_exists('mb_strlen')) {
            return (strlen($string) + mb_strlen($string,'UTF8')) / 2;
        // }

        // if (false === $encoding = mb_detect_encoding($string)) {
        //     return strlen($string);
        // }

        // return mb_strlen($string, $encoding);
    }

     /**
     * Called after rendering to cleanup cache data.
     */
    private function cleanup(){
        $this->columnWidths = array();
        $this->numberOfColumns = null;
    }

    public function writeln($line=''){
        return $line.PHP_EOL;
    }

    public function str_pad($input , $pad_length ,$pad_string , $pad_type){
        $strlen = $this->strlen($input);
        if($strlen < $pad_length){
            $difference = $pad_length - $strlen;
            switch ($pad_type) {
                case STR_PAD_RIGHT:
                    return $input . str_repeat($pad_string, $difference);
                    break;
                case STR_PAD_LEFT:
                    return str_repeat($pad_string, $difference) . $input;
                    break;
                default:
                    $left = $difference / 2;
                    $right = $difference - $left;
                    return str_repeat($pad_string, $left) . $input . str_repeat($pad_string, $right);
                    break;
            }
        }else{
            return $input;
        }
    }

    //生成当前数据库指定表的数据字典(字符串)
    public function generate($tableName=''){
    	$this->crossingChar = '|';
    	$out_array = array();
    	$output = '';
    	if($tableName){
    		echo substr($tableName, strlen(C('DB_PREFIX'))).PHP_EOL;
    		$rows = array();
    		$array = M()->query('SHOW FULL COLUMNS FROM '.$tableName);
    		foreach ($array as $key => $value) {
    			$rows[] = array($value['Field'], $value['Type'], $value['Comment']);
    		}

			$this->headers = array('字段','类型','注释');
    		$this->rows = $rows;
    		$this->renderWitoutHeadTail();
    	}
    	echo PHP_EOL;
    }

    public function generateAll(){
    	$tables = M()->query('SHOW TABLE STATUS;');
		$tables = array_column($tables,'Name');
		foreach ($tables as $value) {
			$this->generate($value);
			$this->cleanup();
		}
    }
}