<?php
require_once("StoredFile.php");
require_once("BasicStor.php");

class ScheduleGroup {

  private $groupId;

  public function __construct($p_groupId = null) {
    $this->groupId = $p_groupId;
  }

  /**
   * Convert a date to an ID by stripping out all characters
   * and padding with zeros.
   *
   * @param string $p_dateStr
   */
  public static function dateToId($p_dateStr) {
    $p_dateStr = str_replace(":", "", $p_dateStr);
    $p_dateStr = str_replace(" ", "", $p_dateStr);
    $p_dateStr = str_replace(".", "", $p_dateStr);
    $p_dateStr = str_replace("-", "", $p_dateStr);
    $p_dateStr = substr($p_dateStr, 0, 17);
    $p_dateStr = str_pad($p_dateStr, 17, "0");
    return $p_dateStr;
  }

  /**
   * Add the two times together, return the result.
   *
   * @param string $p_baseTime
   *  Specified as YYYY-MM-DD HH:MM:SS
   *
   * @param string $p_addTime
   *  Specified as HH:MM:SS.nnnnnn
   *
   * @return string
   *    The end time, to the nearest second.
   */
//  protected function calculateEndTime($p_startTime, $p_trackTime) {
//    $p_trackTime = substr($p_startTime, 0, );
//    $start = new DateTime();
//    $interval = new DateInterval()
//
//  }

  /**
   * Add a music clip or playlist to the schedule.
   *
   * @param $p_datetime
   *    In the format YYYY-MM-DD HH:MM:SS.mmmmmm
   * @param $p_audioFileId
   *    (optional, either this or $p_playlistId must be set) DB ID of the audio file
   * @param $p_playlistId
   *    (optional, either this of $p_audioFileId must be set) DB ID of the playlist
   * @param $p_options
   *    Does nothing at the moment.
   *
   * @return int|PEAR_Error
   *    Return PEAR_Error if the item could not be added.
   *    Error code 555 is a scheduling conflict.
   */
  public function add($p_datetime, $p_audioFileId = null, $p_playlistId = null, $p_options = null) {
    global $CC_CONFIG, $CC_DBC;
    if (!is_null($p_audioFileId)) {
      // Schedule a single audio track

      // Load existing track
      $track = StoredFile::Recall($p_audioFileId);
      if (is_null($track)) {
        return new PEAR_Error("Could not find audio track.");
      }

      // Check if there are any conflicts with existing entries
      $metadata = $track->getMetadata();
      $length = trim($metadata["length"]);
      if (empty($length)) {
        return new PEAR_Error("Length is empty.");
      }
      if (!Schedule::isScheduleEmptyInRange($p_datetime, $length)) {
        return new PEAR_Error("Schedule conflict.", 555);
      }

      // Insert into the table
      $this->groupId = $CC_DBC->GetOne("SELECT nextval('schedule_group_id_seq')");
      $id = $this->dateToId($p_datetime);
      $sql = "INSERT INTO ".$CC_CONFIG["scheduleTable"]
        ." (id, playlist_id, starts, ends, clip_length, group_id, file_id)"
        ." VALUES ($id, 0, TIMESTAMP '$p_datetime', "
        ." (TIMESTAMP '$p_datetime' + INTERVAL '$length'),"
        ." '$length',"
        ." {$this->groupId}, $p_audioFileId)";
      $result = $CC_DBC->query($sql);
      if (PEAR::isError($result)) {
        var_dump($sql);
        return $result;
      }
      return $this->groupId;

    } elseif (!is_null($p_playlistId)){
      // Schedule a whole playlist

      // Load existing playlist
      $playlist = Playlist::Recall($p_playlistId);
      if (is_null($playlist)) {
        return new PEAR_Error("Could not find playlist.");
      }

      // Check if there are any conflicts with existing entries
      $length = trim($playlist->getLength());
      if (empty($length)) {
        return new PEAR_Error("Length is empty.");
      }
      if (!Schedule::isScheduleEmptyInRange($p_datetime, $length)) {
        return new PEAR_Error("Schedule conflict.", 555);
      }

      // Insert all items into the schedule
      $this->groupId = $CC_DBC->GetOne("SELECT nextval('schedule_group_id_seq')");
      $id = $this->dateToId($p_datetime);
      $itemStartTime = $p_datetime;

      $plItems = $playlist->getContents();
      foreach ($plItems as $row) {
        $trackLength = $row["cliplength"];
        $sql = "INSERT INTO ".$CC_CONFIG["scheduleTable"]
          ." (id, playlist_id, starts, ends, group_id, file_id,"
          ." clip_length, cue_in, cue_out, fade_in, fade_out)"
          ." VALUES ($id, $p_playlistId, TIMESTAMP '$itemStartTime', "
          ." (TIMESTAMP '$itemStartTime' + INTERVAL '$trackLength'),"
          ." '{$this->groupId}', '{$row['file_id']}', '$trackLength', '{$row['cuein']}',"
          ." '{$row['cueout']}', '{$row['fadein']}','{$row['fadeout']}')";
        $result = $CC_DBC->query($sql);
        if (PEAR::isError($result)) {
          var_dump($sql);
          return $result;
        }
        $itemStartTime = $CC_DBC->getOne("SELECT TIMESTAMP '$itemStartTime' + INTERVAL '$trackLength'");
        $id = $this->dateToId($itemStartTime);
      }
      return $this->groupId;
    }
  }

  public function addAfter($p_groupId, $p_audioFileId) {
    global $CC_CONFIG, $CC_DBC;
    // Get the end time for the given entry
    $sql = "SELECT ends FROM ".$CC_CONFIG["scheduleTable"]
          ." WHERE group_id=$p_groupId";
    $startTime = $CC_DBC->GetOne($sql);
    return $this->add($startTime, $p_audioFileId);
  }

  public function update() {

  }

  /**
   * Remove the group from the schedule.
   * Note: does not check if it is in the past, you can remove anything.
   *
   * @return boolean
   *    TRUE on success, false if there is no group ID defined.
   */
  public function remove() {
    global $CC_CONFIG, $CC_DBC;
    if (is_null($this->groupId) || !is_numeric($this->groupId)) {
      return false;
    }
    $sql = "DELETE FROM ".$CC_CONFIG["scheduleTable"]
         ." WHERE group_id = ".$this->groupId;
         
    return $CC_DBC->query($sql);
  }

  /**
   * Return the number of items in this group.
   * @return string
   */
  public function count() {
    global $CC_CONFIG, $CC_DBC;
    $sql = "SELECT COUNT(*) FROM {$CC_CONFIG['scheduleTable']}"
          ." WHERE group_id={$this->groupId}";
    return $CC_DBC->GetOne($sql);
  }

  /*
   * Return the list of items in this group as a 2D array.
   * @return array
   */
  public function getItems() {
    global $CC_CONFIG, $CC_DBC;
    $sql = "SELECT * FROM {$CC_CONFIG['scheduleTable']}"
          ." WHERE group_id={$this->groupId}";
    return $CC_DBC->GetAll($sql);
  }

  public function reschedule($toDateTime) {
    global $CC_CONFIG, $CC_DBC;
//    $sql = "UPDATE ".$CC_CONFIG["scheduleTable"]. " SET id=, starts=,ends="
  }

}

class Schedule {

  function __construct() {

  }

  /**
   * Return true if there is nothing in the schedule for the given times.
   *
   * @param string $p_datetime
   * @param string $p_length
   *
   * @return boolean|PEAR_Error
   */
  public static function isScheduleEmptyInRange($p_datetime, $p_length) {
    global $CC_CONFIG, $CC_DBC;
    if (empty($p_length)) {
      return new PEAR_Error("Schedule::isSchedulerEmptyInRange: param p_length is empty.");
    }
    $sql = "SELECT COUNT(*) FROM ".$CC_CONFIG["scheduleTable"]
      ." WHERE (starts >= '$p_datetime') "
      ." AND (ends <= (TIMESTAMP '$p_datetime' + INTERVAL '$p_length'))";
    //$_SESSION["debug"] = $sql;
    $count = $CC_DBC->GetOne($sql);
    return ($count == '0');
  }

  public function onAddTrackToPlaylist($playlistId, $audioTrackId) {

  }

  public function onRemoveTrackFromPlaylist($playlistId, $audioTrackId) {

  }

  /**
   * Returns array indexed numberically of:
   *    "playlistId"/"playlist_id" (aliases to the same thing)
   *    "start"/"starts" (aliases to the same thing) as YYYY-MM-DD HH:MM:SS.nnnnnn
   *    "end"/"ends" (aliases to the same thing) as YYYY-MM-DD HH:MM:SS.nnnnnn
   *    "group_id"/"id" (aliases to the same thing)
   *    "clip_length" (for playlists only, this is the length of the entire playlist)
   *    "name" (playlist only)
   *    "creator" (playlist only)
   *    "file_id" (audioclip only)
   *    "count" (number of items in the playlist, always 1 for audioclips.
   *      Note that playlists with one item will also have count = 1.
   *
   * @param string $p_fromDateTime
   *    In the format YYYY-MM-DD HH:MM:SS.nnnnnn
   * @param string $p_toDateTime
   *    In the format YYYY-MM-DD HH:MM:SS.nnnnnn
   * @param boolean $p_playlistsOnly
   *    Retreive playlists as a single item.
   * @return array
   * 		Returns empty array if nothing found
   */
  public static function GetItems($p_fromDateTime, $p_toDateTime, $p_playlistsOnly = true) {
    global $CC_CONFIG, $CC_DBC;
    $rows = array();
    if (!$p_playlistsOnly) {
      $sql = "SELECT * FROM ".$CC_CONFIG["scheduleTable"]
            ." WHERE (starts >= TIMESTAMP '$p_fromDateTime') "
            ." AND (ends <= TIMESTAMP '$p_toDateTime')";
      $rows = $CC_DBC->GetAll($sql);
      foreach ($rows as &$row) {
        $row["count"] = "1";
        $row["playlistId"] = $row["playlist_id"];
        $row["start"] = $row["starts"];
        $row["end"] = $row["ends"];
        $row["id"] = $row["group_id"];
      }
    } else {
      $sql = "SELECT MIN(name) AS name, MIN(creator) AS creator, group_id, "
           ." SUM(clip_length) AS clip_length,"
           ." MIN(file_id) AS file_id, COUNT(*) as count,"
           ." MIN(playlist_id) AS playlist_id, MIN(starts) AS starts, MAX(ends) AS ends"
           ." FROM ".$CC_CONFIG["scheduleTable"]
           ." LEFT JOIN ".$CC_CONFIG["playListTable"]." ON playlist_id = ".$CC_CONFIG["playListTable"].".id"
           ." WHERE (starts >= TIMESTAMP '$p_fromDateTime') AND (ends <= TIMESTAMP '$p_toDateTime')"
           ." GROUP BY group_id"
           ." ORDER BY starts";
      //var_dump($sql);
      $rows = $CC_DBC->GetAll($sql);
      if (!PEAR::isError($rows)) {
        foreach ($rows as &$row) {
          $row["playlistId"] = $row["playlist_id"];
          $row["start"] = $row["starts"];
          $row["end"] = $row["ends"];
          $row["id"] = $row["group_id"];
        }
      }
    }
    return $rows;
  }

  public function getSchedulerTime() {

  }

  public function getCurrentlyPlaying() {

  }

  public function getNextItem($nextCount = 1) {

  }

  public function getStatus() {

  }

  private static function CcTimeToPypoTime($p_time) {
    $p_time = substr($p_time, 0, 19);
    $p_time = str_replace(" ", "-", $p_time);
    $p_time = str_replace(":", "-", $p_time);
    return $p_time;
  }

  private static function PypoTimeToCcTime($p_time) {
  	$t = explode("-", $p_time);
  	return $t[0]."-".$t[1]."-".$t[2]." ".$t[3].":".$t[4].":00";
  }
  
  /**
   * Export the schedule in json formatted for pypo (the liquidsoap scheduler)
   *
   * @param string $range
   * 		In the format "YYYY-MM-DD HH:mm:ss"
   * @param string $source
   * 		In the format "YYYY-MM-DD HH:mm:ss"
   */
	public static function ExportRangeAsJson($p_fromDateTime, $p_toDateTime)
	{
	    global $CC_CONFIG, $CC_DBC;
	    $range_start = Schedule::PypoTimeToCcTime($p_fromDateTime);
	    $range_end = Schedule::PypoTimeToCcTime($p_toDateTime);
	    $range_dt = array('start' => $range_start, 'end' => $range_end);
		//var_dump($range_dt);
		
		// Scheduler wants everything in a playlist
		$data = Schedule::GetItems($range_start, $range_end, true);
		//echo "<pre>";var_dump($data);
	  	$playlists = array();

		if (is_array($data) && count($data) > 0)
		{
			foreach ($data as $dx)
			{
			    // Is this the first item in the playlist?
				$start = $dx['start'];
				// chop off subseconds
		        $start = substr($start, 0, 19);
		
		        // Start time is the array key, needs to be in the format "YYYY-MM-DD-HH-mm-ss"
		        $pkey = Schedule::CcTimeToPypoTime($start);
		        $timestamp =  strtotime($start);
				$playlists[$pkey]['source'] = "PLAYLIST";
				$playlists[$pkey]['x_ident'] = $dx["playlist_id"];
				$playlists[$pkey]['subtype'] = '1'; // Just needs to be between 1 and 4 inclusive
				$playlists[$pkey]['timestamp'] = $timestamp;
				$playlists[$pkey]['duration'] = $dx['clip_length'];
				$playlists[$pkey]['played'] = '0';
				$playlists[$pkey]['schedule_id'] = $dx['group_id'];
			}
		}

		foreach ($playlists as &$playlist)
		{
		    $scheduleGroup = new ScheduleGroup($playlist["schedule_id"]);
      		$items = $scheduleGroup->getItems();
			$medias = array();
			$playlist['subtype'] = '1';
			foreach ($items as $item)
			{
			  	$storedFile = StoredFile::Recall($item["file_id"]);
  				$uri = $storedFile->getFileUrl();
				$medias[] = array(
					'id' => $item["file_id"],
					'uri' => $uri,
					'fade_in' => $item["fade_in"],
					'fade_out' => $item["fade_out"],
					'fade_cross' => 0,
					'cue_in' => $item["cue_in"],
					'cue_out' => $item["cue_out"],
					);
			}
			$playlist['medias'] = $medias;
		}

		$result = array();
		$result['status'] = array('range' => $range_dt, 'version' => 0.2);
		$result['playlists'] = $playlists;
		$result['check'] = 1;

		print json_encode($result);
	}

}

?>