<?php
/**
 * A class for people on a photo
 *
 * 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 photo;

use photo;
use user;
use person;

use PDO;

use db\db;
use db\clause;
use db\param;
use db\select;
use db\update;

/**
 * People on a photo
 * @author Jeroen Roos
 * @package Zoph
 */
class people  {
    /**
     * Create new photo/people object
     * @param photo photo that the people are on
     */
    public function __construct(photo $photo) {
        $this->photo = $photo;
    }

    /**
     * Get an array of people on the photo in question
     * @return array array of people - to be transformed to JSON
     */
    public function getData() {
        $people = array();

        foreach ($this->getInRows() as $row) {
            $people[] = array_map(array("self", "getPersonData"), $row);
        }

        return array(
            "photoId"       => $this->photo->getId(),
            "people"        => $people
        );
    }
    /**
     * Get a list of people on this photo
     * @return array of people
     */
    public function getAll() {
        $qry=new select(array("p" => "people"));
        $qry->join(array("pp" => "photo_people"), "pp.person_id = p.person_id");
        $distinct=true;
        $qry->addFields(array("person_id"), $distinct);
        $qry->addFields(array("last_name", "first_name", "called", "pp.position", "pp.row"));

        $where=new clause("pp.photo_id=:photoid");

        $qry->addParam(new param(":photoid", (int) $this->photo->getId(), PDO::PARAM_INT));
        $qry->addOrder("pp.row");
        $qry->addOrder("pp.position");
        $qry->where($where);

        return person::getRecordsFromQuery($qry);
    }

    /**
     * Get a list of people on this photo, arranged in rows
     * @return array of people
     */
    public function getInRows() {
        $rows=array();
        $people=$this->getAll();
        foreach ($people as $person) {
            $prow = (int) $person->get("row");
            if (!isset($rows[$prow])) {
                $rows[$prow] = array();
            }
            $rows[$prow][] = $person;
        }
        return $rows;
    }

    /**
     * Gets last used position for people on a photo
     * @param int row to find the last position in
     * @return int position
     */
    public function getLastPos($row = 0) {
        $qry=new select(array("pp" => "photo_people"));
        $qry->addFunction(array("pos" => "max(position)"));
        $where=new clause("photo_id=:photoid");
        $where->addAnd(new clause("row=:row"));
        $qry->where($where);
        $qry->addParam(new param(":photoid", (int) $this->photo->getId(), PDO::PARAM_INT));
        $qry->addParam(new param(":row", (int) $row, PDO::PARAM_INT));
        $result=db::query($qry)->fetch(PDO::FETCH_ASSOC);
        return (int) $result["pos"];
    }

    /**
     * Set position of a person
     * the person currently on that position will be moved to the original position of $person
     * if there is noone on the new position, this function will bail out as it will
     * be an erroneous call.
     * @param person person
     * @param int position to move to
     */
    public function setPosition(person $person, $pos) {
        list($personRow, $personPos) = $this->getPosition($person);

        $second=$this->getForPosition($personRow, $pos);

        if (!$second) {
            // no person on this position, not doing it
            return;
        }

        $qry = new update(array("photo_people"));
        $where=new clause("photo_id=:photoid");
        $where->addAnd(new clause("person_id=:personid"));
        $qry->where($where);
        $qry->addSet("row", "row");
        $qry->addSet("position", "pos");

        $params = array(
            new param(":photoid", (int) $this->photo->getId(), PDO::PARAM_INT),
            new param(":personid", (int) $person->getId(), PDO::PARAM_INT),
            new param(":row", (int) $personRow, PDO::PARAM_INT),
            new param(":pos", (int) $pos, PDO::PARAM_INT)
        );
        $qry->addParams($params);
        $qry->execute();

        $params = array(
            new param(":photoid", (int) $this->photo->getId(), PDO::PARAM_INT),
            new param(":personid", (int) $second->getId(), PDO::PARAM_INT),
            new param(":row", (int) $personRow, PDO::PARAM_INT),
            new param(":pos", (int) $personPos, PDO::PARAM_INT)
        );
        $qry->addParams($params);
        $qry->execute();
    }

    /**
     * Set row for a person
     * @param person person
     * @param int row to move to
     */
    public function setRow(person $person, $row) {
        // The person is already on row 0, we will have to move everyone up
        if ($row == -1) {
            $qry = new update(array("photo_people"));
            $where=new clause("photo_id=:photoid");
            $qry->addSetFunction("row=row+1");
            $qry->addParam(new param(":photoid", (int) $this->photo->getId(), PDO::PARAM_INT));
            $qry->where($where);
            $qry->execute();
            $row = 0;
        }

        $person->removePhoto($this->photo);
        $person->addPhoto($this->photo, $row);

        // We could have ended up with empty rows in the process
        // this will get rid of them:
        $rows = $this->getInRows();
        $newrow = 0;
        foreach ($rows as $rowid => $row) {
            $qry = new update(array("photo_people"));
            $where=new clause("photo_id=:photoid");
            $where->addAnd(new clause("row=:row"));
            $qry->addSet("row", "newrow");
            $qry->addParam(new param(":photoid", (int) $this->photo->getId(), PDO::PARAM_INT));
            $qry->addParam(new param(":newrow", (int) $newrow, PDO::PARAM_INT));
            $qry->addParam(new param(":row", (int) $rowid, PDO::PARAM_INT));
            $qry->where($where);
            $qry->execute();
            $newrow++;
        }
    }

    /**
     * Get the position for a person on the photo
     * @param person person to find on this photo
     * @return array return array of (row, position)
     */
    public function getPosition(person $person) {
        $qry=new select(array("pp" => "photo_people"));
        $qry->addFields(array("row", "position"));
        $where=new clause("photo_id=:photoid");
        $where->addAnd(new clause("person_id=:personid"));
        $qry->where($where);
        $qry->addParam(new param(":photoid", (int) $this->photo->getId(), PDO::PARAM_INT));
        $qry->addParam(new param(":personid", (int) $person->getId(), PDO::PARAM_INT));
        $result=db::query($qry)->fetch(PDO::FETCH_ASSOC);
        return array(
            (int) $result["row"],
            (int) $result["position"]
        );
    }

    /**
     * Get the person on this photo, given the position
     * @param int row
     * @param int position
     * @return person
     */
    public function getForPosition($row, $pos) {
        $qry=new select(array("pp" => "photo_people"));
        $qry->addFields(array("person_id"));
        $where=new clause("photo_id=:photoid");
        $where->addAnd(new clause("row=:row"));
        $where->addAnd(new clause("position=:pos"));
        $qry->where($where);
        $qry->addParam(new param(":photoid", (int) $this->photo->getId(), PDO::PARAM_INT));
        $qry->addParam(new param(":row", (int) $row, PDO::PARAM_INT));
        $qry->addParam(new param(":pos", (int) $pos, PDO::PARAM_INT));
        $result=db::query($qry)->fetch(PDO::FETCH_ASSOC);
        if ($result) {
            return new person($result["person_id"]);
        }
    }

    /**
     * Get array of data about a person
     * This method is called by getData()
     * and relies on a person object that is generated by
     * self::getInRows, which adds row/pos data to the object
     * @param person person to get data on
     * @return array data array
     */
    private static function getPersonData(person $person) {
        $row = (int) $person->get("row");
        $pos = (int) $person->get("position");
        return array(
            "id"    => $person->getId(),
            "name"  => $person->getName(),
            "url"   => $person->getURL(),
            "row"   => $row,
            "position"   => $pos
        );
    }
}
?>
