<?php
/**
 * Database column class, to describe tables
 *
 * This file is part of Zoph.
 *
 * Zoph is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Zoph is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with Zoph; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * @package Zoph
 * @author Jeroen Roos
 */

namespace db;

/**
 * Database column class, to describe tables
 *
 * Used in CREATE TABLE queries, possibly later also in others (ALTER?)
 *
 * @package Zoph
 * @author Jeroen Roos
 */
class column {

    /** @var string name of the column */
    private $name;

    /** @var string type of the column */
    private $type;

    /** @var int size of the column */
    private $size;

    /** @var array possible values for ENUM type */
    private $values;

    /** @var int decimals in the column */
    private $decimals;

    /** @var bool column value may be NULL */
    private $notNull = false;

    /** @var bool column value autoincrements */
    private $autoIncrement = false;

    /** @var bool column is key */
    private $isKey = false;

    /** @var string if column is key, this stores the name of the key */
    private $keyName = "";

    /** @var in if column is key, this stores the length of the key (or 0 for whole field) */
    private $keyLength = 0;

    /** @var bool column is primary key */
    private $primaryKey = false;

    /** @var bool column is unsigned */
    private $unsigned = false;

    /** @var string on update */
    private $onUpdate = "";

    /** @var string default value */
    private $default = "";

    private static $noQuotes = array(
        "CURRENT_TIMESTAMP",
        "NULL"
    );

    /**
     * Create a new column
     * @param string name
     */
    public function __construct($name) {
        $this->name=$name;
    }

    /**
     * Set column to UNSIGNED
     */
    public function unsigned() {
        $this->unsigned=true;
        return $this;
    }

    /**
     * Set column to AUTO_INCREMENT
     */
    public function autoIncrement() {
        $this->autoIncrement=true;
        $this->setKey(); // Auto_increment fields must be key
        return $this;
    }

    /**
     * Set column to NOT NULL
     */
    public function notNull() {
        $this->notNull=true;
        return $this;
    }

    /**
     * Is column to NOT NULL?
     * @return bool is NOT NULL
     */
    public function isNotNull() {
        return($this->notNull);
    }

    /**
     * Set column as KEY
     */
    public function setKey(string $keyname = null, int $length = 0) {
        $this->isKey=true;
        if ($keyname) {
            $this->keyName=$keyname;
        } else {
            $this->keyName=$this->name;
        }
        if ($length > 0) {
            $this->keyLength=$length;
        }
        return $this;
    }

    /**
     * Set column as PRIMARY KEY
     */
    public function setPrimaryKey() {
        $this->primaryKey=true;
        return $this;
    }

    /**
     * Check if column is KEY
     * @return bool is KEY
     */
    public function isKey() {
        return !$this->isPrimaryKey() && $this->isKey;
    }

    /**
     * Get column name
     * @return string name
     */
    public function getName() {
        return $this->name;
    }

    /**
     * Get key name
     * @return string keyName
     */
    public function getKeyName() {
        if (!$this->isKey) {
            throw new keyException($this->name . " is not a key");
        }
        return $this->keyName;
    }

    /**
     * Get key length
     * @return int part of the field to use as key
     */
    public function getKeyLength() {
        if (!$this->isKey) {
            throw new keyException($this->name . " is not a key");
        }
        return $this->keyLength;
    }

    /**
     * Check if column is PRIMARY KEY
     * @return bool is PRIMARY KEY
     */
    public function isPrimaryKey() {
        return $this->primaryKey;
    }

    /**
     * Set column DEFAULT value
     */
    public function default(string $default) {
        $this->default = $this->addQuotes($default);
        return $this;
    }

    /**
     * Set column ON UPDATE value
     */
    public function onUpdate(string $onUpdate) {
        $this->onUpdate = $this->addQuotes($onUpdate);
        return $this;
    }

    /**
     * VARCHAR type
     * @param size int length of field
     */
    public function varchar(int $size) {
        $this->type=type::VARCHAR;
        $this->size=$size;
        return $this;
    }

    /**
     * CHAR type
     * @param size int length of field
     */
    public function char(int $size) {
        $this->type=type::CHAR;
        $this->size=$size;
        return $this;
    }

    /**
     * TINYINT type
     * note that specifying the 'length' is not supported
     * this only works with 'ZEROFILL', which is not supported
     * (and it's not really the length)
     */
    public function tinyint() {
        $this->type=type::TINYINT;
        return $this;
    }

    /**
     * SMALLINT type
     * note that specifying the 'length' is not supported
     * this only works with 'ZEROFILL', which is not supported
     * (and it's not really the length)
     */
    public function smallint() {
        $this->type=type::SMALLINT;
        return $this;
    }

    /**
     * INT type
     * note that specifying the 'length' is not supported
     * this only works with 'ZEROFILL', which is not supported
     * (and it's not really the length)
     */
    public function int() {
        $this->type=type::INT;
        return $this;
    }

    /**
     * FLOAT type
     * single precission float
     * @param int m total number of digits
     * @param int d number of digits after the comma
     * (and it's not really the length)
     */
    public function float(int $m, int $d) {
        $this->type=type::FLOAT;
        $this->size = $m;
        $this->decimals = $d;
        return $this;
    }

    /**
     * TEXT type
     * note that specifying the length is not supported
     */
    public function text() {
        $this->type=type::TEXT;
        return $this;
    }

    /**
     * BLOB type
     * note that specifying the length is not supported
     */
    public function blob() {
        $this->type=type::BLOB;
        return $this;
    }

    /**
     * DATE type
     */
    public function date() {
        $this->type=type::DATE;
        return $this;
    }

    /**
     * DATETIME type
     */
    public function datetime() {
        $this->type=type::DATETIME;
        return $this;
    }

    /**
     * TIMESTAMP type
     */
    public function timestamp() {
        $this->type=type::TIMESTAMP;
        return $this;
    }


    /**
     * ENUM type
     */
    public function enum(...$values) {
        $this->type=type::ENUM;
        $this->values=$values;
        return $this;
    }

    /**
     * Build the clause
     * @return string clause
     */
    public function __toString() {

        $sql = "`" . $this->name . "` " . strtoupper($this->type);

        switch ($this->type) {
            case type::TINYINT:
            case type::SMALLINT:
            case type::INT:
                $sql .= $this->unsigned ? " UNSIGNED" : "";
                $sql .= $this->autoIncrement ? " AUTO_INCREMENT" : "";
                break;
            case type::FLOAT:
                $sql .= "(" . $this->size . "," . $this->decimals . ")";
                $sql .= $this->unsigned ? " UNSIGNED" : "";
                $sql .= $this->autoIncrement ? " AUTO_INCREMENT" : "";
                break;
            case type::CHAR:
            case type::VARCHAR:
                $sql .= "(" . $this->size . ")";
                break;
            case type::TEXT:
            case type::BLOB:
            case type::DATE:
            case type::DATETIME:
            case type::TIMESTAMP:
                break;
            case type::ENUM:
                $sql .= "(\"" . implode("\", \"", $this->values) . "\")";

                break;
        }
        $sql .= $this->notNull ? " NOT NULL" : "";
        $sql .= $this->default ? " DEFAULT " . $this->default : "";
        $sql .= $this->onUpdate ? " ON UPDATE " . $this->onUpdate : "";
        return $sql;
    }

    /**
     * Add Quotes for ON UPDATE and DEFAULT
     *
     * This adds quotes to a string, except if they are in the 'noQuotes' array
     * this is used for SQL keywords as ON UPDATE or DEFAULT value
     * @param string to be quoted
     * @return string quoted string
     */
    private function addQuotes(string $string) {
        if (!in_array($string, static::$noQuotes)) {
            $string = "\"" . $string . "\"";
        }
        return $string;
    }
}

