<?php
/**
 * Sets are a set of photos
 *
 * 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
 *
 * @author Jeroen Roos
 * @package Zoph
 */

namespace set;

use db\select;
use db\param;
use db\insert;
use db\update;
use db\clause;
use db\delete;
use photo;
use photo\collection;
use template\block;
use template\template;
use user;
use PDO;
use ZophTable;

/**
 * The set class groups a set of photos in a certain order
 * @author Jeroen Roos
 * @package Zoph
 */
class model extends zophTable {
    /** @var string The name of the database table */
    protected static $tableName="sets";
    /** @var array List of primary keys */
    protected static $primaryKeys=array("set_id");
    /** @var array Fields that may not be empty */
    protected static $notNull=array("name");
    /** @var bool keep keys with insert. In most cases the keys are set
     *  by the db with auto_increment */
    protected static $keepKeys = false;
    /** @var string URL for this class */
    protected static $url="set.php?set_id=";

    /**
     * Create set
     * @param int If existing set is to be pulled from the db, the id has to be given
     * @return set
     */
    public function __construct($id = 0) {
        parent::__construct($id);
    }

    /**
     * Update existing set in db
     */
    public function update() {
        $this->set("timestamp", "now()");
        parent::update();
        $this->lookup();
    }

    /**
     * Delete set from db
     * Also delete photo-set relations
     */
    public function delete() {
        if (!$this->get("set_id")) {
            return;
        }
        parent::delete(array("photo_sets"));
    }

    public function isWritableBy(user $user) {
        return $user->isAdmin();
    }

    /**
     * Get an array of information to be displayed for this set
     */
    public function getDisplayArray() {
        return array(
            translate("name") => $this->get("name"),
            translate("created by", false) => $this->getUser()->getLink(),
        );
    }

    /**
     * Get the photos in this set
     */
    public function getPhotos() {
        $qry=new select(array("ps" => "photo_sets"));
        $qry->addFields(array("photo_id"));
        $qry->where(new clause("set_id=:setid"));
        $qry->addParam(new param(":setid", $this->getId(), PDO::PARAM_INT));
        $qry->addOrder("photo_order");
        return collection::createFromArray(photo::getRecordsFromQuery($qry));
    }

    /**
     * Get the number of photos in this set
     */
    public function getPhotoCount() {
        $qry=new select(array("ps" => "photo_sets"));
        $qry->addFunction(array("count" => "COUNT(photo_id)"));
        $qry->where(new clause("set_id=:setid"));
        $qry->addParam(new param(":setid", $this->getId(), PDO::PARAM_INT));
        return $qry->getCount();
    }

    public function getName() {
        return $this->get("name");
    }

    /**
     * Add a photo to this set
     * @param photo photo to add
     */
    public function addPhoto(photo $photo) {
        if (!$this->getOrder($photo)) {
            $qry=new insert(array("photo_sets"));
            $qry->addParam(new param(":set_id", $this->getId(), PDO::PARAM_INT));
            $qry->addParam(new param(":photo_id", $photo->getId(), PDO::PARAM_INT));
            $qry->addParam(new param(":photo_order", $this->getMaxOrder() + 1, PDO::PARAM_INT));
            $qry->execute();
        }
    }

    /**
     * Remove a photo from this set
     * @param photo Page to remove
     */
    public function removePhoto(photo $photo) {
        $qry=new delete(array("photo_sets"));
        $where=new clause("set_id=:setid");
        $where->addAnd(new clause("photo_id=:photoid"));
        $qry->addParam(new param(":setid", $this->getId(), PDO::PARAM_INT));
        $qry->addParam(new param(":photoid", $photo->getId(), PDO::PARAM_INT));
        $qry->where($where);

        $qry->execute();

        $this->renumber();
    }

    /**
     * Move a photo after another photo
     * @param photo Photo to move
     * @param photo Photo at target position
     */
    public function movePhoto(photo $photo, photo $target) {
        if ($target->getId() == 0) {
            // move to end
            $targetOrder = $this->getMaxOrder() + 1;
        } else {
            $targetOrder=$this->getOrder($target);
        }
        $oldOrder=$this->getOrder($photo);

        if ($targetOrder <= $oldOrder) {
            $oldOrder++;
        }

        $paramTargetOrder=new param(":targetorder", $targetOrder, PDO::PARAM_INT);
        $paramOldOrder=new param(":oldorder", $oldOrder, PDO::PARAM_INT);
        $paramSetId=new param(":setid", $this->getId(), PDO::PARAM_INT);

        // [ step 1 ] Make place
        $qry=new update(array("photo_sets"));
        $qry->addSetFunction("photo_order = photo_order + 1");
        $where=new clause("photo_order>=:targetorder");
        $where->addAnd(new clause("set_id=:setid"));
        $qry->where($where);
        $qry->addParams(array($paramTargetOrder, $paramSetId));

        $qry->execute();

        // [ step 2 ] move
        $qry=new update(array("photo_sets"));
        $qry->addSet("photo_order", "targetorder");
        $where=new clause("photo_order=:oldorder");
        $where->addAnd(new clause("set_id=:setid"));
        $qry->where($where);
        $qry->addParams(array($paramTargetOrder, $paramOldOrder, $paramSetId));

        $qry->execute();

        // [ step 3 ] renumber
        $qry=new update(array("photo_sets"));
        $qry->addSetFunction("photo_order = photo_order - 1");
        $where=new clause("photo_order>=:oldorder");
        $where->addAnd(new clause("set_id=:setid"));
        $qry->where($where);
        $qry->addParams(array($paramOldOrder, $paramSetId));

        $qry->execute();
    }

    public function getAutoCover() {
        return new photo($this->get("coverphoto"));
    }

    /**
     * Get the highest used order value for this set
     * @return int maximum order
     */
    private function getMaxOrder() {
        $qry=new select(array("ps" => "photo_sets"));
        $qry->addFunction(array("max_order" => "MAX(photo_order)"));
        $qry->where(new clause("set_id=:setid"));
        $qry->addParam(new param(":setid", $this->getId(), PDO::PARAM_INT));

        $stmt=$qry->execute();
        return intval($stmt->fetchColumn());
    }

    /**
     * Get Order
     */
    private function getOrder(photo $photo) {
        $qry=new select(array("ps" => "photo_sets"));
        $qry->addFields(array("photo_order"));
        $where=new clause("set_id=:setid");
        $where->addAnd(new clause("photo_id=:photoid"));
        $qry->where($where);
        $qry->addParam(new param(":setid", $this->getId(), PDO::PARAM_INT));
        $qry->addParam(new param(":photoid", $photo->getId(), PDO::PARAM_INT));

        $stmt=$qry->execute();
        return intval($stmt->fetchColumn());
    }

    /**
     * Get the user who created this set
     * @return user the user
     */
    public function getUser() {
        $user = new user($this->get("createdby"));
        $user->lookup();
        return $user;
    }

    private static function getSets(array $sets=null) {
        if (!$sets) {
           $sets=static::getAll();
        }
        $lsets=array();
        foreach ($sets as $set) {
            $set->lookup();
            $lsets[]=$set;
        }
        return $lsets;
    }

    /**
     * Get table of sets
     * @param array sets to put in the table (default: all)
     * @return block template block with all sets in table
     */
    public static function getTable(array $sets=null) {
        return new block("sets", array(
            "sets" => static::getSets($sets)
        ));
    }

    /**
     * Get dropdown of sets
     * @param array sets to put in the table (default: all)
     * @return block template block with all sets in dropdown
     */
    public static function getDropdown(array $sets=null) {
        $ddsets = array();
        foreach (static::getSets($sets) as $set) {
            $ddsets[$set->getId()] = $set->getName();
        }
        return template::createDropdown("_set_id", null, $ddsets);
    }

    public static function renumberAll() {

        foreach (self::getSets() as $set) {
            if ($set->getPhotoCount() < $set->getMaxOrder()) {
                $set->renumber();
            }
        }
    }

    private function renumber() {
        $i = 0;
        foreach ($this->getPhotos() as $photo) {
            $i++;
            if ($this->getOrder($photo) != $i) {
                $this->setOrder($photo, $i);
            }
        }

    }

    private function setOrder(photo $photo, int $order) {
        $paramOrder=new param(":order", $order, PDO::PARAM_INT);
        $paramPhotoId=new param(":photoid", $photo->getId(), PDO::PARAM_INT);
        $paramSetId=new param(":setid", $this->getId(), PDO::PARAM_INT);

        $qry=new update(array("photo_sets"));
        $qry->addSet("photo_order",  "order");
        $where=new clause("photo_id=:photoid");
        $where->addAnd(new clause("set_id=:setid"));
        $qry->where($where);
        $qry->addParams(array($paramOrder, $paramSetId, $paramPhotoId));

        $qry->execute();
    }

    /**
     * Lookup set by name
     * @param string name
     */
    public static function getByName($name) {
        if (empty($name)) {
            return false;
        }
        $qry=new select(array("s" => "sets"));
        $qry->addFields(array("set_id"));
        $qry->where(new clause("lower(name)=:name"));
        $qry->addParam(new param(":name", strtolower($name), PDO::PARAM_STR));
        return static::getRecordsFromQuery($qry);
    }

    /**
     * Get Top N albums
     */
}
