<?php
/**
 * @package     VikBooking
 * @subpackage  com_vikbooking
 * @author      Alessio Gaggii - e4j - Extensionsforjoomla.com
 * @copyright   Copyright (C) 2020 e4j - Extensionsforjoomla.com. All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 * @link        https://vikwp.com
 */

defined('ABSPATH') or die('No script kiddies please!');

/**
 * Critical Dates handler class for Vik Booking.
 *
 * @since 	1.13.5 (J) - 1.3.5 (WP)
 */
class VikBookingCriticalDates
{
	/**
	 * The singleton instance of the class.
	 *
	 * @var VikBookingCriticalDates
	 */
	protected static $instance = null;

	/**
	 * Class constructor is protected.
	 *
	 * @see 	getInstance()
	 */
	protected function __construct()
	{}

	/**
	 * Returns the global Critical Dates object, either
	 * a new instance or the existing instance
	 * if the class was already instantiated.
	 *
	 * @return 	self 	a new instance of the class.
	 */
	public static function getInstance()
	{
		if (is_null(static::$instance)) {
			static::$instance = new static();
		}

		return static::$instance;
	}

	/**
	 * Checks whether a precise note exists on the given date.
	 * 
	 * @param 	string 		$ymd 		the date string in Y-m-d format.
	 * @param 	int 		$idroom 	the ID of the room involved.
	 * @param 	int 		$subunit 	the index of the sub-unit (0 by default).
	 * @param 	string 		$key 		the key name of the note.
	 * @param 	bool 		$get 		whether to get the found record.
	 *
	 * @return 	mixed 		True/array if festivity exists, false otherwise.
	 */
	public function dayNoteExists($ymd, $idroom, $subunit = 0, $key = '', $get = false)
	{
		// vars validation
		$idroom  = intval($idroom);
		$subunit = intval($subunit);

		$dbo = JFactory::getDbo();

		$q = "SELECT * FROM `#__vikbooking_critical_dates` WHERE `dt`=" . $dbo->q($ymd) . " AND `idroom`={$idroom} AND `subunit`={$subunit};";
		$dbo->setQuery($q);
		$note = $dbo->loadAssoc();
		if (!$note) {
			return false;
		}

		$notes_info = json_decode($note['info']);
		if (!$notes_info) {
			return false;
		}

		// update this information in the array in case it needs to be returned
		$note['info'] = $notes_info;

		// seek for the requested note
		foreach ($notes_info as $n) {
			if (empty($key) || (!empty($n->type) && $n->type == $key)) {
				return $get ? $note : true;
			}
		}

		return false;
	}

	/**
	 * Stores/updates a note for the given date, idroom and subunit. If a record for the given data exists, then the
	 * note is added to or updated in the current record. Otherwise a new record is created.
	 * 
	 * @param 	array 		$note 		the array note to store.
	 * @param 	string 		$ymd 		the date string in Y-m-d format.
	 * @param 	int 		$idroom 	the ID of the room involved.
	 * @param 	int 		$subunit 	the index of the sub-unit (0 by default).
	 *
	 * @return 	bool 		True if the new record is stored or updated, false otherwise.
	 */
	public function storeDayNote($note, $ymd, $idroom = 0, $subunit = 0)
	{
		// vars validation
		$idroom  = intval($idroom);
		$subunit = intval($subunit);
		if (!is_array($note) || (empty($note['name']) && empty($note['descr']))) {
			return false;
		}

		if (!isset($note['type'])) {
			$note['type'] = 'custom';
		}

		if (!isset($note['ts'])) {
			$note['ts'] = time();
		}

		if (!empty($note['name'])) {
			// make sure the "name" does not contain commas, which may be used by JS as separator in the readable notes data attribute
			$note['name'] = trim(str_replace(',', '', $note['name']));
		}

		$dbo = JFactory::getDbo();

		$q = "SELECT * FROM `#__vikbooking_critical_dates` WHERE `dt`=" .$dbo->q($ymd) . " AND `idroom`={$idroom} AND `subunit`={$subunit}";
		$dbo->setQuery($q);
		$prevNote = $dbo->loadAssoc();
		if (!$prevNote) {
			// create a new record
			$q = "INSERT INTO `#__vikbooking_critical_dates` (`dt`, `idroom`, `subunit`, `info`) VALUES (".$dbo->q($ymd).", ".$dbo->q($idroom).", ".$dbo->q($subunit).", ".$dbo->q(json_encode(array($note))).");";
			$dbo->setQuery($q);
			$dbo->execute();

			return (bool) ((int) $dbo->insertid());
		}
		
		// update current record by pushing the note into the existing info array of objects
		$notes_arr = json_decode($prevNote['info']);
		array_push($notes_arr, (object) $prevNote);
		$q = "UPDATE `#__vikbooking_critical_dates` SET `info`=".$dbo->q(json_encode($notes_arr))." WHERE `id`={$prevNote['id']};";
		$dbo->setQuery($q);
		$dbo->execute();

		return (bool) ((int) $dbo->getAffectedRows());
	}

	/**
	 * Deletes a specific note for the given date, room ID and subunit.
	 * If the day will not contain anymore notes, then
	 * the whole record will be deleted.
	 * 
	 * @param 	int 		$index 		the array index of the note.
	 * @param 	string 		$ymd 		the date string in Y-m-d format.
	 * @param 	int 		$idroom 	the room ID involved.
	 * @param 	int 		$subunit 	the subunit of the room ID.
	 * @param 	string 		$type 		the key name of the note ('custom' for manual entries).
	 *
	 * @return 	bool 		True if the fest is removed, false otherwise.
	 */
	public function deleteDayNote($index, $ymd, $idroom = 0, $subunit = 0, $type = 'custom')
	{
		$record = $this->dayNoteExists($ymd, $idroom, $subunit, $type, true);
		if (!$record) {
			return false;
		}

		$drop_record = false;
		if (isset($record['info'][$index])) {
			// note was found by array-index
			array_splice($record['info'], $index, 1);
			if (!count($record['info'])) {
				// this day has got no more notes
				$drop_record = true;
			}
		} else {
			// seek for the requested note by type
			foreach ($record['info'] as $k => $note) {
				if (empty($type) || (!empty($note->type) && $note->type == $type)) {
					// note found
					array_splice($record['info'], $k, 1);
					if (!count($record['info'])) {
						// this day has got no more notes
						$drop_record = true;
					}
					break;
				}
			}
		}
		
		$dbo = JFactory::getDbo();
		if ($drop_record) {
			$q = "DELETE FROM `#__vikbooking_critical_dates` WHERE `dt`=" . $dbo->q($ymd) . " AND `idroom`={$idroom} AND `subunit`={$subunit}";
		} else {
			$q = "UPDATE `#__vikbooking_critical_dates` SET `info`=".$dbo->q(json_encode($record['info']))." WHERE `dt`=" . $dbo->q($ymd) . " AND `idroom`={$idroom} AND `subunit`={$subunit}";
		}
		$dbo->setQuery($q);
		$dbo->execute();

		return true;
	}

	/**
	 * Loads the room day notes of any type and returns and associative
	 * array indexed by date with decoded notes information.
	 * 
	 * @param 	string 		$from_ymd 	Optional minimum date in Y-m-d (defaults to today).
	 * @param 	string 		$to_ymd 	Optional maximum date in Y-m-d (defaults to none).
	 * @param 	int 		$idroom 	Optional room id filter.
	 * @param 	int 		$subunit 	Optional room index filter.
	 *
	 * @return 	array 		the list of festivities found in VikBooking.
	 */
	public function loadRoomDayNotes($from_ymd = null, $to_ymd = null, $idroom = null, $subunit = null)
	{
		$dbo = JFactory::getDbo();

		$rdnotes = [];

		if (empty($from_ymd)) {
			$from_ymd = date('Y-m-d');
		}

		$clauses = [];
		array_push($clauses, "`dt` >= " . $dbo->q($from_ymd));

		if (!empty($to_ymd)) {
			array_push($clauses, "`dt` <= " . $dbo->q($to_ymd));
		}
		if ($idroom !== null && is_int($idroom)) {
			array_push($clauses, "`idroom`=" . (int) $idroom);
		}
		if ($subunit !== null && is_int($subunit)) {
			array_push($clauses, "`subunit`=" . (int) $subunit);
		}
		
		$q = "SELECT * FROM `#__vikbooking_critical_dates` WHERE " . implode(' AND ', $clauses) . " ORDER BY `dt` ASC;";
		$dbo->setQuery($q);
		$all_notes = $dbo->loadAssocList();

		// make sure to decode all notes infos
		foreach ($all_notes as $k => $v) {
			$v['info'] = json_decode($v['info']);
			$keyid = $v['dt'] . '_' . ((int) $v['idroom']) . '_' . ((int) $v['subunit']);
			// set room-day note
			$rdnotes[$keyid] = $v;
		}

		return $rdnotes;
	}
}
