<?php
/**
 * Database migrations
 *
 * 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 upgrade;

use conf\conf;
use db\query;
use template\result;

/**
 * Database migrations
 *
 * @package Zoph
 * @author Jeroen Roos
 */
abstract class migration {

    /** @var array steps for upgrade */
    protected $stepsUp = array();
    /** @var array description for upgrade steps */
    protected $descUp = array();
    /** @var array steps for downgrade */
    protected $stepsDown = array();
    /** @var array description for dwongrade steps */
    protected $descDown = array();

    /** @var int migration::DOWN - indicates 'downgrade' step */
    protected const DOWN=1;
    /** @var int migration::UP - indicates 'upgrade' step */
    protected const UP=2;

    /** @var string description for this migration */
    protected const DESC="";
    /** @var string version this migration upgrades from */
    protected const FROM="";
    /** @var string version this migration upgrades to */
    protected const TO="";

    /**
     * Get description
     * @return string description
     */
    final public function getDesc() {
        return static::DESC;
    }

    /**
     * Get version this migration upgrades from
     * @return string version
     */
    final public function getFrom() {
        return static::FROM;
    }

    /**
     * Get version this migration upgrades to
     * @return string version
     */
    final public function getTo() {
        return static::TO;
    }

    /**
     * Get descriptions of all upgrade steps
     * @return array string descriptions
     */
    final public function show() {
        return $this->descUp;
    }

    /**
     * Execute upgrade
     * @return return array template\result
     */
    final public function up() {
        $results = array();
        foreach ($this->stepsUp as $step) {
            $desc=array_shift($this->descUp);
            $results[] =$this->doStep($step, $desc);
        }
        return $results;
    }

    /**
     * Execute downgrade
     * @return return array template\result
     */
    final public function down() {
        $results = array();
        foreach ($this->stepsDown as $step) {
            $desc=array_shift($this->descDown);
            $results[] =$this->doStep($step, $desc);
        }
        return $results;
    }

    /**
     * Add a step
     * @param int       migration::UP|migration::DOWN - indicate whether this is an upgrade or downgrade step
     * @param db\query  database query to execute for this step
     * @param string    description of the step
     */
    final protected function addStep(int $dir, query $qry, string $desc) {
        switch ($dir) {
            case static::UP:
                $this->stepsUp[] = $qry;
                $this->descUp[] = $desc;
            break;
            case static::DOWN:
                $this->stepsDown[] = $qry;
                $this->descDown[] = $desc;
            break;
            default:
                throw new exception("Error");
            break;
        }

    }

    /**
     * Execute a step
     * @return template\result
     */
    private function doStep(query $step, string $desc) {
        try {
            $step->execute();
            $result=result::SUCCESS;
            $error=null;
        } catch (\Exception $e) {
            $result=result::ERROR;
            $error=$e->getMessage();
        }
        return new result($result, $desc, $error);
    }

    /**
     * Required migration
     * Are we at an older version than the one this migration brings us to?
     * if so, it is required to run.
     * @param string current version
     * @return bool migration required
     */
    final public function isRequired(string $current) {
        return (static::TO > $current);
    }

    /**
     * Is this migration the one that brought us to the current state?
     * if so, it is eligable to run as rollback
     * @param string current version
     * @return bool migration is current
     */
    final public function isCurrent(string $current) {
        return (static::TO == $current);
    }
}
