<?php
/**
* Since 2013 Ovidiu Cimpean.
*
* Ovidiu Cimpean - Newsletter Pro © All rights reserved.
*
* DISCLAIMER
*
* Do not edit, modify or copy this file.
* If you wish to customize it, contact us at addons4prestashop@gmail.com.
*
* @author    Ovidiu Cimpean <addons4prestashop@gmail.com>
* @copyright Since 2013 Ovidiu Cimpean
* @license   Do not edit, modify or copy this file
*
* @version   Release: 4
*/

class NewsletterProMySQLDumpXml
{
    public $tables;

    public $hex_value;

    public $insert_ignore;

    public $headers;

    public $xml;

    public $filename;

    public $prepend_string;

    public function __construct($hex_value = false, $insert_ignore = true)
    {
        $this->headers = [];
        $this->tables = [];
        $this->prepend_string = [];
        $this->hex_value = $hex_value;
        $this->insert_ignore = $insert_ignore;
        $this->xml = new NewsletterProCreateXML('database');

        $this->xml->attribute($this->xml->root, 'name', _DB_NAME_);
    }

    /**
     * Add header.
     *
     * @param string $name  Header name
     * @param string $value Header value
     */
    public function addHeader($name, $value)
    {
        $this->headers[$name] = $value;
    }

    /**
     * Get header by name.
     *
     * @param string $name Header name
     *
     * @return string/false Header value
     */
    public function getHeaderByName($name)
    {
        if (isset($this->headers[$name])) {
            return $this->headers[$name];
        }

        return false;
    }

    /**
     * Add tables.
     *
     * @param array/string $table_name Table name
     */
    public function addTables($table_name)
    {
        if (is_array($table_name)) {
            foreach ($table_name as $table_n) {
                $this->tables[] = _DB_PREFIX_.$table_n;
            }
        } else {
            $this->tables[] = _DB_PREFIX_.$table_name;
        }
    }

    /**
     * Get all tables.
     *
     * @return array
     */
    public function getTables()
    {
        return $this->tables;
    }

    public function setHeader(array $headers)
    {
        $this->headers = $headers;
    }

    public function prependString($string)
    {
        $this->prepend_string[] = $string;
    }

    public function save($filename)
    {
        $success = [];

        $this->filename = $filename;

        $this->xml->header = $this->xml->create($this->xml->root, 'header');
        foreach ($this->headers as $name => $value) {
            $this->xml->append($this->xml->header, $name, $value);
        }

        $this->xml->tables = $this->xml->create($this->xml->root, 'tables');

        if ($this->file = fopen($filename, 'w')) {
            $success[] = $this->saveToFile(implode('', $this->prepend_string));

            foreach ($this->tables as $table_name) {
                if ($this->tableExists($table_name)) {
                    $success[] = $this->createTable($table_name);
                }
            }

            $success[] = $this->saveToFile($this->xml->getContent($this->xml->root));
            @fclose($this->file);
        } else {
            throw new Exception($this->module->l('Cannot create the backup. Please check the CHMOD permissions.'));
        }

        return !in_array(false, $success);
    }

    public function tableExists($table_name)
    {
        return count(Db::getInstance()->executeS('SHOW TABLES LIKE "'.pSQL($table_name).'"'));
    }

    public function saveToFile($data)
    {
        return @fwrite($this->file, $data);
    }

    public function createTable($table_name)
    {
        $success = [];

        $records = Db::getInstance()->executeS('SHOW FIELDS FROM `'.pSQL($table_name).'`');

        if (!$records) {
            return false;
        }

        $table = $this->xml->create($this->xml->tables, 'table', 'name', pSQL($table_name));

        $fields = $this->xml->append($table, 'fields');

        $select_statement = 'SELECT ';
        $hex_field = [];
        $i = 0;
        foreach ($records as $record) {
            $is_hex = ($this->hex_value && $this->isTextValue($record['Type']));

            if ($is_hex) {
                $select_statement .= 'HEX(`'.$record['Field'].'`) AS '.$record['Field'].'';
                $hex_field[$i] = true;
            } else {
                $select_statement .= '`'.$record['Field'].'`';
            }

            $field = $this->xml->append($fields, 'field');
            $this->xml->attribute($field, 'name', $record['Field']);

            $type = $this->getFieldType($record['Type']);

            $this->xml->attribute($field, 'datatype', $type['type']);
            $this->xml->attribute($field, 'length_set', $type['size']);
            $this->xml->attribute($field, 'unsigned', $type['unsigned']);
            $this->xml->attribute($field, 'allow_null', $this->getFieldNull($record['Null']));
            $this->xml->attribute($field, 'key', $this->getFieldKey($record['Key']));
            $this->xml->attribute($field, 'default', $this->getFieldDefault($record['Default']));
            $this->xml->attribute($field, 'default_is_null', $this->getFieldIsDefault($record['Default']));
            $this->xml->attribute($field, 'extra', $this->getFieldExtra($record['Extra']));
            $this->xml->attribute($field, 'is_hex', (int) $is_hex);

            $select_statement .= ', ';

            ++$i;
        }

        $select_statement = Tools::substr($select_statement, 0, -2).' FROM `'.pSQL($table_name).'`';
        $records = Db::getInstance()->executeS($select_statement);

        $num_rows = count($records);

        if ($num_rows > 0) {
            $records_xml = $this->xml->append($table, 'records');
            foreach ($records as $record) {
                $record_xml = $this->xml->append($records_xml, 'record');

                $j = 0;
                foreach ($record as $field_name => $value) {
                    if (isset($hex_field[$j]) && $hex_field[$j] && (Tools::strlen($value) > 0)) {
                        $field_value = '0x'.$value;
                    } elseif (is_null($value)) {
                        $field_value = 'NULL';
                    } else {
                        $field_value = $value;
                    }

                    $this->xml->append($record_xml, $field_name, $field_value);

                    ++$j;
                }
            }
        }

        return !in_array(false, $success);
    }

    public function isTextValue($field_type)
    {
        switch ($field_type) {
            case 'tinytext':
            case 'text':
            case 'mediumtext':
            case 'longtext':
            case 'binary':
            case 'varbinary':
            case 'tinyblob':
            case 'blob':
            case 'mediumblob':
            case 'longblob':
                return true;
            default:
                return false;
        }
    }

    public function getFieldType($value)
    {
        preg_match('/(?P<type>\w+)(\((?P<size>.*)\))?\s?(?P<unsigned>\w+)?/i', $value, $match);

        return [
            'type' => isset($match['type']) ? $match['type'] : false,
            'size' => isset($match['size']) ? $match['size'] : 0,
            'unsigned' => isset($match['unsigned']) ? $match['unsigned'] : 0,
        ];
    }

    public function getFieldNull($value)
    {
        switch (Tools::strtolower($value)) {
            case 'yes':
                return 1;

            case 'no':
            default:
                return 0;
        }

        return 0;
    }

    public function getFieldKey($value)
    {
        switch (Tools::strtolower($value)) {
            case 'pri':
                return 'PRI';

            case 'uni':
                return 'UNI';

            case 'mul':
                return 'MUL';

            default:
                return '';
        }

        return '';
    }

    public function getFieldExtra($value)
    {
        switch (Tools::strtolower($value)) {
            case 'auto_increment':
                return 'auto_increment';

            default:
                return '';
        }

        return '';
    }

    public function getFieldDefault($value)
    {
        if (is_null($value)) {
            return null;
        } else {
            return $value;
        }
    }

    public function getFieldIsDefault($value)
    {
        return (int) is_null($value);
    }
}
