<?php

namespace Acms\Services\Blog;

use SQL;
use DB;
use Symfony\Component\Yaml\Yaml;

class Export
{
    /**
     * @var array
     */
    protected $yaml;

    /**
     * @var array
     */
    protected $tables;

    /**
     * @var string
     */
    protected $prefix;

    public function __construct()
    {
        $this->yaml = array();
        $this->setTables(array(
            'category', 'column', 'config', 'comment',
            'dashboard', 'entry', 'field', 'form',
            'module', 'rule', 'tag', 'schedule', 'layout_grid',
        ));
        $dsn = dsn();
        $this->prefix = $dsn['prefix'];
    }

    /**
     * export blog data
     *
     * @param int $bid
     *
     * @return string
     */
    public function run($bid)
    {
        foreach ( $this->tables as $table ) {
            $SQL = SQL::newSelect($table);
            $SQL->addWhereOpr($table.'_blog_id', $bid);

            $method = 'fixQuery' . ucfirst($table);
            if ( is_callable(array($this, $method))) {
                $SQL = call_user_func_array(array($this, $method), array($SQL));
            }
            $records = DB::query($SQL->get(dsn()), 'all');

            $this->fix($records, $table);
            $this->setYaml($records, $table);
        }
        return $this->getYaml();
    }

    /**
     * set export tables
     *
     * @param array $tables
     *
     * @return void
     *
     * @throws \RuntimeException
     */
    public function setTables($tables=array())
    {
        if ( !is_array($tables) ) {
            throw new \RuntimeException('Not specified tables.');
        }
        $this->tables = $tables;
    }

    /**
     * get data as array
     *
     * @return array
     */
    public function getArray()
    {
        return $this->yaml;
    }

    /**
     * get data as yaml
     *
     * @return string
     */
    public function getYaml()
    {
        return Yaml::dump($this->yaml, 2, 4);
    }

    /**
     * set data as yaml
     *
     * @param array $records
     * @param string $table
     *
     * @return void
     */
    private function setYaml($records=array(), $table)
    {
        $this->yaml[$table] = $records;
    }

    /**
     * fix data
     *
     * @param &array $records
     * @param string $table
     *
     * @return void
     */
    private function fix(& $records, $table)
    {
        if ( $table === 'column' ) {
            $this->fixHalfSpace($records);
        }
        if ( $table === 'schedule' ) {
            $this->fixSchedule($records);
        }
        if ( $table === 'fulltext' ) {
            $this->fixFulltext($records);
        }
    }

    /**
     * escape single byte spaces of row's header
     *
     * @param $records
     *
     * @return void
     */
    private function fixHalfSpace(& $records)
    {
        $cnt = count($records);

        for ( $i=0; $i < $cnt; $i++ ) {
            if ( 'text' !== $records[$i]['column_type'] ) {
                continue;
            }
            $txt = $records[$i]['column_field_1'];
            /*
             * carrige returns \r and \r\n
             * Paragraph Separator (U+2028)
             * Line Separator (U+2029)
             * Next Line (NEL) (U+0085)
             */
            $records[$i]['column_field_1'] = preg_replace('/(\xe2\x80[\xa8-\xa9]|\xc2\x85|\r\n|\r)/', "\n", $txt);
        }
    }

    /**
     * ignore user fulltext
     *
     * @param $records
     */
    private function fixFulltext(& $records)
    {
        $cnt    = count($records);
        for ( $i=0; $i < $cnt; $i++ ) {
            if ( !empty($records[$i]['fulltext_uid']) ) {
                unset($records[$i]);
            }
            $txt = $records[$i]['fulltext_value'];
            $records[$i]['fulltext_value'] = preg_replace('/(\xe2\x80[\xa8-\xa9]|\xc2\x85|\r\n|\r)/', "\n", $txt);
        }
        $records = array_values($records);
    }

    /**
     * ignore schedule data without base set
     *
     * @param $records
     */
    private function fixSchedule(& $records)
    {
        $cnt    = count($records);

        for ( $i=0; $i < $cnt; $i++ ) {
            if ( 1
                and $records[$i]['schedule_year']   == 0000
                and $records[$i]['schedule_month']  == 00
            ) {
                // no touch
            } else {
                unset($records[$i]);
            }
        }
        $records = array_values($records);
    }

    /**
     * ユニットデータからゴミ箱のデータを除外
     *
     * @param $SQL
     * @return mixed
     */
    private function fixQueryColumn($SQL)
    {
        $columns = DB::query('SHOW COLUMNS FROM ' . $this->prefix . 'column', 'all');
        foreach ( $columns as $column ) {
            $SQL->addSelect($column['Field']);
        }
        $SQL->addLeftJoin('entry', 'column_entry_id', 'entry_id');
        $SQL->addWhereOpr('entry_status', 'trash', '<>');

        return $SQL;
    }

    /**
     * コメントデータからゴミ箱のデータを除外
     *
     * @param $SQL
     * @return mixed
     */
    private function fixQueryComment($SQL)
    {
        $columns = DB::query('SHOW COLUMNS FROM ' . $this->prefix . 'comment', 'all');
        foreach ( $columns as $column ) {
            $SQL->addSelect($column['Field']);
        }
        $SQL->addLeftJoin('entry', 'comment_entry_id', 'entry_id');
        $SQL->addWhereOpr('entry_status', 'trash', '<>');

        return $SQL;
    }

    /**
     * エントリーデータからゴミ箱のデータを除外
     *
     * @param $SQL
     * @return mixed
     */
    private function fixQueryEntry($SQL)
    {
        $SQL->addWhereOpr('entry_status', 'trash', '<>');

        return $SQL;
    }

    /**
     * フィールドデータからゴミ箱のデータを除外
     *
     * @param $SQL
     * @return mixed
     */
    private function fixQueryField($SQL)
    {
        $columns = DB::query('SHOW COLUMNS FROM ' . $this->prefix . 'field', 'all');
        foreach ( $columns as $column ) {
            $SQL->addSelect($column['Field']);
        }
        $SQL->addLeftJoin('entry', 'field_eid', 'entry_id');

        $SUB = SQL::newWhere();
        $SUB->addWhereOpr('entry_status', 'trash', '<>', 'OR');
        $SUB->addWhereOpr('field_eid', null, '=', 'OR');
        $SQL->addWhere($SUB);

        return $SQL;
    }

    /**
     * フルテキストデータからゴミ箱のデータを除外
     *
     * @param $SQL
     * @return mixed
     */
    private function fixQueryFulltext($SQL)
    {
        $columns = DB::query('SHOW COLUMNS FROM ' . $this->prefix . 'fulltext', 'all');
        foreach ( $columns as $column ) {
            $SQL->addSelect($column['Field']);
        }
        $SQL->addLeftJoin('entry', 'fulltext_eid', 'entry_id');
        $SQL->addWhereOpr('entry_status', 'trash', '<>');

        return $SQL;
    }
}
