<?php
/**
 * Class DatabaseTableBuilder
 * Generates the database tables used in module builder modules.
 *
 * @author Rihards Silins <rihards.silins@mtcmedia.co.uk>
 *
 * @version 2 14/07/2016
 *
 * @copyright MTC media Ltd. 2015
 */
namespace Mtc\Modules\ModuleBuilder\Classes\Builders;

use Illuminate\Database\Capsule\Manager as Capsule;

class DatabaseTableBuilder
{
    /**
     * Database table name.
     *
     * @var string
     */
    private $table_name;

    /**
     * Iniitalized eloquent class of the model
     *
     * @var Object
     */
    private $model;

    /**
     * Special column names that shouldn't be dropped in a table in any case.
     *
     * @var string[]
     */
    public $special_columns = array(
        'id',
        'active',
        'order',
        'published',
        'timestamp',
        'created',
        'updated',
        'created_at',
        'updated_at'
    );

    /**
     * DatabaseTableBuilder::setTableName()
     * Set table name of table to be built.
     *
     * @param string table_name
     *
     * @return bool success
     */
    public function setTableName($table_name)
    {
        // validate
        if (preg_match("/^[a-z_A-Z\-]+$/", $table_name)) {
            $this->table_name = $table_name;

            return true;
        }

        return false;
    }

    /**
     * DatabaseTableBuilder::setModel()
     * Set iniitalized eloquent class of the model.
     * @param string model
     */
    public function setModel($model)
    {
        // validate
        $this->model = $model;
        $this->table_name = $this->model->getTable();
    }

    /**
     * Generates the database table for model relative.
     * @param mixed[][] $relative_setup
     * @return string[] $result
     */
    public function buildRelationTable($relative_setup)
    {
        $result = array();

        if (empty($this->table_name)) {
            return $result;
        }

        if (Capsule::schema()->hasTable($this->table_name)) {
            $parent_key = $relative_setup['parent_key'];
            if (!Capsule::schema()->hasColumn($this->table_name, $parent_key)) {
                Capsule::schema()->table($this->table_name, function ($table) use ($parent_key) {
                    $table->integer($parent_key)->index($parent_key);
                });
                $result[] = 'Adding "'.$parent_key.'" (int) column to table "'.$this->table_name.'"!';
            }
            return $result;
        }

        Capsule::schema()->create($this->table_name, function ($table) use ($relative_setup) {
            $table->increments('id');
            $table->integer($relative_setup['parent_key'])
                  ->index($relative_setup['parent_key']);
            $table->tinyInteger('active')
                  ->default(1)
                  ->index('active');
        });

        $result[] = "Built relation table '".$this->table_name."'.";
        return $result;
    }

    /**
     * DatabaseTableBuilder::build()
     * Generates the database tables used in module builder modules.
     *
     * @return bool success
     */
    public function build()
    {
        if (empty($this->table_name)) {
            return false;
        }

        if (Capsule::schema()->hasTable($this->table_name)) {
            return false;
        }

        Capsule::schema()->create($this->table_name, function ($table) {
            $table->increments('id');
            $table->string('name')
                  ->index('name');
            $table->tinyInteger('active')
                  ->default(1)
                  ->index('active');
            $table->integer('order')
                  ->default(0)
                  ->index('order');
        });

        return true;
    }

    /**
     * DatabaseTableBuilder::change()
     * Changes table according to form requirements.
     *
     * @param string[][] requested_columns
     *
     * @return string[] result - empty means no changes
     */
    public function change($requested_columns)
    {
        if (empty($this->table_name) || empty($this->model)) {
            return false;
        }

        $schema = Capsule::schema()->getColumnListing($this->table_name);

        $result = array();

        // Go through $requested_columns and add in any missing table columns
        foreach ($requested_columns as $row_name => $row) {
            if($row['type'] == 'fieldset'){
                continue;
            }
            if (method_exists($this->model, $row_name) && get_class($this->model->{$row_name}()) == "Illuminate\Database\Eloquent\Relations\BelongsToMany") {
                $link_table_name = $this->model->{$row_name}()->getTable();
                if (!Capsule::schema()->hasTable($link_table_name)) {
                    $link_table_foreign_key = $this->model->{$row_name}()->getForeignKey();
                    $link_table_foreign_key = explode(".", $link_table_foreign_key)[1]; // remove the prepended table name
                    $link_table_other_key = $this->model->{$row_name}()->getOtherKey();
                    $link_table_other_key = explode(".", $link_table_other_key)[1]; // remove the prepended table name
                    Capsule::schema()->create($link_table_name, function ($table) use ($link_table_foreign_key, $link_table_other_key) {
                        $table->increments('id');
                        $table->integer($link_table_foreign_key)->index($link_table_foreign_key);
                        $table->integer($link_table_other_key)->index($link_table_other_key);
                    });
                    $result[] = 'Adding "'.$link_table_name.'" link table!';
                }
            } elseif (method_exists($this->model, $row_name) && get_class($this->model->{$row_name}()) == "Illuminate\Database\Eloquent\Relations\HasMany") {
                $foreign_key = $this->model->{$row_name}()->getPlainForeignKey();
                $related_model = $this->model->{$row_name}()->getRelated();
                $related_model_table = $related_model->getTable();
                if (!Capsule::schema()->hasColumn($related_model_table, $foreign_key)) {
                    Capsule::schema()->table($related_model_table, function ($table) use ($foreign_key) {
                        $table->integer($foreign_key)->index($foreign_key);
                    });
                    $result[] = 'Adding "'.$foreign_key.'" (int) column to table "'.$related_model_table.'"!';
                }
                if ($row['type'] == 'images') {
                    if (!Capsule::schema()->hasColumn($related_model_table, "path")) {
                        Capsule::schema()->table($related_model_table, function ($table) {
                            $table->string("path");
                        });
                        $result[] = 'Adding "path" (string) column to table "'.$related_model_table.'"!';
                    }
                }
            } elseif (!method_exists($this->model, $row_name)) {
                $exists_in_table = false;
                for ($i = 0; $i < count($schema); ++$i) {
                    if ($schema[$i] == $row_name) {
                        $exists_in_table = true;
                    }
                }

                if (!$exists_in_table) {
                    if ($row['type'] == 'number' ||  $row['type'] == 'select') {
                        Capsule::schema()->table($this->table_name, function ($table) use ($row_name) {
                            $table->integer($row_name);
                        });
                        $result[] = 'Adding '.$row_name." (int) column! Don't forget to optimize this!";
                    } elseif ($row['type'] == 'checkbox') {
                        Capsule::schema()->table($this->table_name, function ($table) use ($row_name) {
                            $table->tinyInteger($row_name);
                        });
                        $result[] = 'Adding '.$row_name." (tinyint) column! Don't forget to optimize this!";
                    } elseif ($row['type'] == 'datetime') {
                        Capsule::schema()->table($this->table_name, function ($table) use ($row_name) {
                            $table->dateTime($row_name);
                        });
                        $result[] = 'Adding '.$row_name." (datetime) column! Don't forget to optimize this!";
                    } elseif ($row['type'] == 'coordinates') {
                        Capsule::schema()->table($this->table_name, function ($table) use ($row_name) {
                            $table->string($row_name, 64);
                        });
                        $result[] = 'Adding '.$row_name." (tinyint) column! Don't forget to optimize this!";
                    } elseif ($row['type'] == 'textarea') {
                        Capsule::schema()->table($this->table_name, function ($table) use ($row_name) {
                            $table->text($row_name);
                        });
                        $result[] = 'Adding '.$row_name." (text) column! Don't forget to optimize this!";
                    } elseif ($row['type'] == 'image' || $row['type'] == 'file') {
                        Capsule::schema()->table($this->table_name, function ($table) use ($row_name) {
                            $table->string($row_name, 64);
                        });
                        $result[] = 'Adding '.$row_name." (varchar 64) column! Don't forget to optimize this!";
                    } elseif ($row['type'] === 'images' && !method_exists($this->model, $row_name)) {
                        $message = 'To add an \'images\' multi field make sure to make a ' . MODEL_CLASS_NAME . 'Images ';
                        if (MODEL_CLASS_NAME . 'Images' != MODEL_CLASS_NAME . ucfirst($row_name)) {
                            $message .= 'or ' . MODEL_CLASS_NAME . ucfirst($row_name) . ' ';
                        }
                        $message .= 'or similar eloquent model and add the following to the ' . MODEL_CLASS_NAME . ' class file.<br/><br/>
                        public function ' . $row_name . '() {<br/>
                        &nbsp;&nbsp;&nbsp;&nbsp;return $this->hasMany("Mtc\Modules\\' . MODEL_CLASS_NAME . 'Image\Classes\\' . MODEL_CLASS_NAME . 'Image");<br/>
                        }<br/>';
                        if (MODEL_CLASS_NAME . 'Images' != MODEL_CLASS_NAME . ucfirst($row_name)) {
                            $message .= 'or<br/>
                            public function ' . $row_name . '() {<br/>
                            &nbsp;&nbsp;&nbsp;&nbsp;return $this->hasMany("Mtc\Modules\\' . MODEL_CLASS_NAME . ucfirst($row_name) . '\Classes\\' . MODEL_CLASS_NAME . ucfirst($row_name) . '");<br/>
                            }';
                        }
                        $result[] = $message;
                    } else {
                        Capsule::schema()->table($this->table_name, function ($table) use ($row_name) {
                            $table->string($row_name, 1024);
                        });
                        $result[] = 'Adding '.$row_name." (varchar 1024) column! Don't forget to optimize this!";
                    }
                }
            }
        }

        // Go through current $schema and remove any table columns not mentioned in $requested_columns
        for ($i = 0; $i < count($schema); ++$i) {
            if (in_array($schema[$i], $this->special_columns)) {
                continue;
            }
            $exists_in_requested_columns = false;
            foreach ($requested_columns as $row_name => $row) {
                if ($schema[$i] == $row_name) {
                    $exists_in_requested_columns = true;
                }
            }

            if (!$exists_in_requested_columns) {
                $row_name = $schema[$i];
                Capsule::schema()->table($this->table_name, function ($table) use ($row_name) {
                    $table->dropColumn($row_name);
                });
                $result[] = 'Dropping '.$row_name.' column!';
            }
        }

        return $result;
    }
}