<?php
/**
 * SQLiteSchema class
 * Implements the ISQLSchema interface
 * @author Jerôme Brilland
 * @version 2.0
 * @copyright (c) 2019-2021, Jérôme Brilland
 * @license http://www.gnu.org/licenses/gpl.txt GNU General Public License
 */
class SQLiteSchema implements ISQLSchema {

    protected $database;

    protected $dataTypes = [
        'integer' => 'INTEGER',
        'smallint' => 'INTEGER',
        'bigint' => 'INTEGER',
        'decimal' => 'REAL',
        'float' => 'REAL',
        'double' => 'REAL',
        'char' => 'TEXT',
        'varchar' => 'TEXT',
        'text' => 'TEXT',
        'clob' => 'BLOB',
        'boolean' => 'INTEGER(1)',
        'date' => 'TEXT',
        'time' => 'TEXT',
        'datetime' => 'TEXT',
        'timestamp' => 'INTEGER',
        'varbinary' => 'BLOB'
    ];

    /**
     * Class constructor
     * @param Medoo database
     * @access public
     */
    public function __construct( $database ) {
        $this->database = $database;
    }

    /**
     * @return array
     * @access public
     */
    public function getTables() {
        return $this->database->query( 'SELECT name FROM sqlite_master WHERE type=\'table\' ORDER BY name;' )->fetchAll();
    }

    /**
     * @param string $tableName
     * @return array
     * @access public
     */
    public function getColumns( $tableName ) {
        return $this->database->query( 'PRAGMA table_info(' . $tableName . ');' )->fetchAll();
    }

    /**
     * @param string $tableName
     * @return array
     * @access public
     */
    public function getColumnNames( $tableName ) {
        return array_column( $this->getColumns( $tableName ), 'name' );
    }

    /**
     * @param string $tableName
     * @param array $columns
     * @param mixed $parameters
     * @access public
     */
    public function createTable( $tableName, $columns, $parameters = null ) {
        $query = 'CREATE TABLE ' . $tableName . ' (';
        $query .= join( ', ', array_map( [$this, 'getColumnSQL'], array_keys( $columns ), $columns ) );
        $query .= ')';
        $this->database->exec( $query );
        $logger = IoC::resolve( 'logger' );
        $error = $this->database->error();
        if ( $error[1] ) {
            $logger->write( sprintf( '[%s] [%s] [%s] %s',  date( 'Y-m-d H:i:s' ), __FILE__, 'error', $error[2] ) );
        }
        if ( DEVELOPMENT_ENVIRONMENT ) {  
            $logger->write( sprintf( '[%s] [%s] [%s] %s', date( 'Y-m-d H:i:s' ), __FILE__, 'debug', $this->database->last() ) );
        }
    }

    /**
     * @param string $tableName
     * @access public
     */
    public function dropTable( $tableName ) {
        $query = 'DROP TABLE ' . $tableName;
        $this->database->exec( $query );
        $logger = IoC::resolve( 'logger' );
        $error = $this->database->error();
        if ( $error[1] ) {
            $logger->write( sprintf( '[%s] [%s] [%s] %s',  date( 'Y-m-d H:i:s' ), __FILE__, 'error', $error[2] ) );
        }
    }

    /**
     * @param string $tableName
     * @access public
     */
    public function dropTableIfexists( $tableName ) {
        $query = 'DROP TABLE IF EXISTS ' . $tableName;
        $this->database->exec( $query );
        $logger = IoC::resolve( 'logger' );
        $error = $this->database->error();
        if ( $error[1] ) {
            $logger->write( sprintf( '[%s] [%s] [%s] %s',  date( 'Y-m-d H:i:s' ), __FILE__, 'error', $error[2] ) );
        }
    }

    /**
     * @param string $tableName
     * @param string $columnName
     * @param mixed $options
     * @access public
     */
    public function addColumn( $tableName, $columnName, $datatype, $options = [] ) {
        $query = 'ALTER TABLE ' . $tableName . ' ADD ' . $this->getColumnSQL( $columnName, [$datatype, $options] );
        $this->database->exec( $query );
        $logger = IoC::resolve( 'logger' );
        $error = $this->database->error();
        if ( $error[1] ) {
            $logger->write( sprintf( '[%s] [%s] [%s] %s',  date( 'Y-m-d H:i:s' ), __FILE__, 'error', $error[2] ) );
        }
        if ( DEVELOPMENT_ENVIRONMENT ) {  
            $logger->write( sprintf( '[%s] [%s] [%s] %s', date( 'Y-m-d H:i:s' ), __FILE__, 'debug', $this->database->last() ) );
        }
    }

    /**
     * @param string $tableName
     * @param string $columnName
     * @access public
     */
    public function dropColumn( $tableName, $columnName ) {
    }
    
    /**
     * @param string $identifier
     * @param string $tableName
     * @param mixed $columnNames
     * @access public
     */
    public function addPrimaryKey( $identifier, $tableName, $columnNames ) {
    }

    /**
     * @param string $identifier
     * @param string $tableName
     * @access public
     */
    function dropPrimaryKey( $identifier, $tableName ) {
    }

    /**
     * @param string $identifier
     * @param string $tableName
     * @param string $columnName
     * @param string $referenceTableName
     * @param string $referenceColumnName
     * @param string $onDelete
     * @param string $onUpdate
     * @access public
     */
    public function addForeignKey( $identifier, $tableName, $columnName, $referenceTableName, $referenceColumnName, $onDelete = null, $onUpdate = null ) {
    }

    /**
     * @param string $identifier
     * @param string $tableName
     * @access public
     */
    public function dropForeignKey( $identifier, $tableName ) {
 
    }

    /**
     * @param string $identifier
     * @param string $tableName
     * @param mixed $columnNames
     * @param boolean $unique
     */
    public function addIndex( $identifier, $tableName, $columnNames, $unique = false ) {
        $query = 'CREATE ';
        if ( $unique ) {
            $query .= 'UNIQUE ';
        }
        $query .= 'INDEX ' . $identifier . ' ON ' . $tableName . ' (';
        if ( is_string( $columnNames ) ) {
            $query .=  $columnNames;
        } else if ( is_array( $columnNames ) ) {
            $query .= join( ',', $columnNames );
        }
        $query .= ')';
        $this->database->exec( $query );
        $logger = IoC::resolve( 'logger' );
        $error = $this->database->error();
        if ( $error[1] ) {
            $logger->write( sprintf( '[%s] [%s] [%s] %s',  date( 'Y-m-d H:i:s' ), __FILE__, 'error', $error[2] ) );
        }
    }

    /**
     * @param string $identifier
     * @param string $tableName
     */
    public function dropIndex( $identifier, $tableName ) {
        $query = ' DROP INDEX ' . $identifier;
        $this->database->exec( $query );
    }

    /**
     * @param string $dataType
     * @return string The actual data type
     * @access public
     */
    public function getDataType( $dataType ) {
        if ( array_key_exists( $dataType, $this->dataTypes ) ) {
            return $this->dataTypes[$dataType];
        } else {
            return $dataType;
        }
    }

    /**
     * @param string $columnName
     * @param mixed $columnDefinition
     * @return string
     * @access protected
     */
    protected function getColumnSQL( $columnName, $columnDefinition ) {
        if ( is_string( $columnDefinition ) ) {
            return $columnName . ' ' . $columnDefinition;
        } else if ( is_array( $columnDefinition ) ){
            $dataType = $this->getDataType( $columnDefinition[0] );
            $options = '';
            if ( !empty( $columnDefinition[1] ) && is_array( $columnDefinition[1] ) ) {
                if ( !empty( $columnDefinition[1]['length'] ) ) {
                    $options .= '(' . $columnDefinition[1]['length'] . ')';
                }
                if ( !empty( $columnDefinition[1]['not_null'] ) ) {
                    $options .=' NOT NULL';
                } else {
                    $options .= ' NULL';
                }
                if ( !empty( $columnDefinition[1]['default'] ) ) {
                    $options .= ' DEFAULT ' . $columnDefinition[1]['default'];
                }
                if ( !empty( $columnDefinition[1]['primary_key'] ) ) {
                    $options .= ' PRIMARY KEY';
                }
            }
            return $columnName . ' ' . $dataType . $options;
        }
    }

}
