<?php
/**
 * CodeIgniter library
 *
 * @category CodeIgniter
 * @license /_licenses/untermStrich/agb_normal.pdf
 * @author Christian Koller
 * @copyright 2009 untermStrich
 * @package us_libraries
 * @version $Id$
*/

if (!defined('BASEPATH')) { exit('No direct script access allowed'); }

require_once APPPATH.'us_libraries/vendor/autoload.php';
require_once APPPATH.'us_libraries/Value_object.php';

/**
 * Used to save a personal appointment
 *
 * @property    My_Controller           $CI                     The main CI object
 */
abstract class Personalapp_values extends Value_object
{

    protected $add_or_id;
        /**
     * @var $acknowledge DateTimeInterface
     */
    protected $acknowledge;
    protected $date;
    protected $from_yn;
    protected $from;
    protected $to;
    protected $notify_yn;
    protected $notify_date;
    protected $notify_time;
    protected $status;
    protected $state_color_id;
    protected $private;
    protected $project;
    protected $staff;
    protected $location;
    protected $url;
    protected $participants;
    protected $summary;
    protected $comment;
    protected $multistaff;
    protected $app_type;
    protected $several_days_day_yn;
    protected $several_days_day;
    protected $several_days_last_date;
    protected $periodical_yn;
    protected $periodical__number;
    protected $periodical__unit;
    protected $periodical_last_date;
    protected $editconfirm_dialog_sel_user;
    protected $editconfirm_dialog_all_users;
    protected $editconfirm_dialog_from_today;
    protected $mobile_dav_mode;
    protected $allow_all_datetime_collision;
    protected $allow_datetime_collision;    
    
    /**
     * @var bool allow_change_changed Allow to change already changed values
     */
    protected $allow_change_changed;

    protected $cache_appointments_to_save = null;

    /**
     * Extend values which were initialized from POST values
     * 
     * @param   Personalapp_values    $obj    The Personalapp_values object to extend
     * @return  void
     */
    abstract public function init_from_post_extend(Personalapp_values $obj): void;

    /**
     * Extend values which were initialized from values
     * 
     * @param   Personalapp_values      $obj        The Personalapp_values object to extend
     * @param   stdClass                $values     The values to get extendet information
     * @return  void
     */    
    abstract public function init_from_values_extend(Personalapp_values $obj, stdClass $values): void;  
    
    /**
     * Retrieves the number of multi-days for the personal appointment.
     * 
     * @return integer The number of multi-days for the personal appointment.
     */
    abstract public function get_multidays() : int;

    /**
     * Retrieves the attendees for the Office365 event.
     *
     * @return array The attendees for the Office365 event.
     */
    abstract public function get_o365_attendees(): array;

    /**
     * Get the begin date for each period of a new periodical appointment
     *
     * @return  array   Array of periodical dates for a new appointment
     */
    abstract public function get_periodical_dates(): array;

    /**
     * Increments the date and date_to, notify_date, and periodical_last_date by one day.
     *
     * @return void
     */
    abstract public function next_day(): void;

    /**
     * Get personal appointments to save, (handling multiuser, multiday and periodical appointments
     *
     * @return  array
     */
    public abstract function get_appointments_to_save();

    /**
     * Build values object
     * 
     * @return  Personalapp_values or Personalapp_values_o365
     */    
    final public static function factory_build()
    {
        $CI = get_instance();
        $CI->load->model('Personalappointments_model', 'personalapp');

        if($CI->personalapp instanceof Personalappointments_o365_model) {
            require_once APPPATH.'us_libraries/value_objects/personalapp_values_o365.php';
            return new Personalapp_values_o365();
        }
        require_once APPPATH.'us_libraries/value_objects/personalapp_values_local.php';
        return new Personalapp_values_local();
    }

    /**
     * Init the personal appointment values from $_POST
     *
     * @param   boolean     $mobile_dav_mode     Optional Mobile version
     * @return  object  An Personalapp_values object with all values out of the post array
     */
    final public static function init_from_post($mobile_dav_mode=false): Personalapp_values
    {
        $obj = self::factory_build();
        $obj->cache_appointments_to_save = null;
        $obj->CI = CI_Controller::get_instance();
        $obj->add_or_id = $obj->CI->input->post('personalapp_edit');
        $obj->date = new UsDate($obj->CI->input->post('personalapp_date__from'), null, true);
        $obj->from_yn = $obj->CI->input->post('personalapp_time_yn') ? true : false;
        if ($obj->from_yn) {
            $obj->from = new UsTime($obj->CI->input->post('personalapp_time__from'));
            $obj->to = new UsTime($obj->CI->input->post('personalapp_time__to'));
        }
        $obj->notify_yn = $obj->CI->input->post('personalapp_notify_yn') ? true : false;
        if ($obj->notify_yn) {
            $obj->notify_date = new UsDate($obj->CI->input->post('personalapp_notify__date'), null, true);
            $obj->notify_time = new UsTime($obj->CI->input->post('personalapp_notify__time'));
            if (!$obj->notify_date->is_null() && $obj->notify_date->format('Y')<1977) {
                $obj->notify_date = clone $obj->date;
            }
        }
        $obj->acknowledge = null;
        $obj->status = intval($obj->CI->input->post('personalapp_status'));
        $obj->state_color_id = intval($obj->CI->input->post('personalapp_state_color'));
        $obj->private = $obj->CI->input->post('personalapp_private') ? true : false;
        $obj->project = $obj->CI->input->post('personalapp_project');
        $obj->staff = $obj->CI->input->post('personalapp_staff');
        $obj->location = $obj->CI->input->post('personalapp_location');
        $obj->url = trim(base64_decode($obj->CI->input->post('personalapp_url')));
        $obj->participants = $obj->CI->input->post('personalapp_participants');
        $obj->summary = $obj->CI->input->post('personalapp_summary');
        $obj->comment = $obj->CI->input->post('personalapp_comment');
        $obj->multistaff = $obj->CI->input->post('personalapp_multistaff');
        if (!is_array($obj->multistaff)) {
            $obj->multistaff = array();
        }
        if (count($obj->multistaff)<1) {
            $obj->multistaff = array($obj->staff);
        }
        
        $obj->several_days_day_yn = $obj->CI->input->post('personalapp_several_days_day_yn') ? true : false;
        if ($obj->several_days_day_yn) {
            $obj->several_days_last_date = new UsDate($obj->CI->input->post('personalapp_several_days_last_date'), null, true);
            $obj->several_days_day = datetime_diff_days($obj->date, $obj->several_days_last_date)+1;
        } else {
            $obj->several_days_last_date = $obj->date;
            $obj->several_days_day = 0;
        }
        $obj->periodical_yn = $obj->CI->input->post('personalapp_periodical_yn') ? true : false;
        if ($obj->periodical_yn) {
            $obj->periodical__number = $obj->CI->input->post('personalapp_periodical__number');
            $obj->periodical__unit = $obj->CI->input->post('personalapp_periodical__unit');
            $obj->periodical_last_date = new UsDate($obj->CI->input->post('personalapp_periodical_last_date'), null, true);
        } else {
            $obj->periodical__unit = 0;
            $obj->periodical_last_date = $obj->date;
        }
        $obj->app_type = $obj->get_app_type();

        $editconfirm_dialog_sel_user_yn = $obj->CI->input->post('editconfirm_dialog_sel_user_yn') ? true : false;
        if ($editconfirm_dialog_sel_user_yn) {
            $obj->editconfirm_dialog_sel_user = $obj->CI->input->post('editconfirm_dialog_sel_user');
        } else {
            $obj->editconfirm_dialog_sel_user = null;
        }
        $editconfirm_dialog_all_users_yn = $obj->CI->input->post('editconfirm_dialog_all_users_yn') ? true : false;
        if ($editconfirm_dialog_all_users_yn) {
            $obj->editconfirm_dialog_all_users = $obj->CI->input->post('editconfirm_dialog_all_users');
        } else {
            $obj->editconfirm_dialog_all_users = null;
        }
        $obj->editconfirm_dialog_from_today = $obj->CI->input->post('editconfirm_dialog_from_today') ? true : false;

        $obj->allow_all_datetime_collision = $obj->CI->input->post('allow_all_datetime_collision') ? (boolean)$obj->CI->input->post('allow_all_datetime_collision') : false;
        $obj->allow_datetime_collision = $obj->CI->input->post('allow_datetime_collision') ? $obj->CI->input->post('allow_datetime_collision') : false;
        $obj->allow_change_changed = $obj->CI->input->post('allow_change_changed') ? true : false;
        
        $obj->mobile_dav_mode = $mobile_dav_mode;

        $obj->init_from_post_extend($obj);

        return $obj;
    }

    /**
     * Init the personal appointment values from a given stdClass object
     *
     * @param   object  $values stdClass object with appointment values to initialize a valid Personalapp_values object out of it
     *
     * @return  object  An Personalapp_values object with all values out of the given stdClass object
     */
    final public static function init_from_values(stdClass $values): Personalapp_values
    {
        $obj = self::factory_build();
        $obj->cache_appointments_to_save = null;
        $obj->CI = CI_Controller::get_instance();
        $obj->add_or_id = $values->id;
        $obj->date = ($values->date instanceof UsDate) ? $values->date : new UsDate($values->date, null);

        $time_from = ($values->time_from instanceof UsTime) ? $values->time_from : new UsTime($values->time_from);
        $time_to = ($values->time_to instanceof UsTime) ? $values->time_to : new UsTime($values->time_to);
        $obj->from_yn = (!$time_from->is_null() && !$time_to->is_null()) ? true : false;
        $obj->from = $time_from;
        $obj->to = $time_to;

        $obj->notify_yn = (isset($values->is_notify) && $values->is_notify) ? true : false;
        if (!isset($values->notify_date)) $values->notify_date = new UsDateTime();
        $obj->notify_date = ($values->notify_date instanceof UsBasicDateTime) ? $values->notify_date : new UsDate($values->notify_date, null);
        $obj->notify_time = ($values->notify_date instanceof UsBasicDateTime) ? $values->notify_date : new UsTime($values->notify_date);
        if (!$obj->notify_date->is_null() && $obj->notify_date->format('Y')<1977) {
            $obj->notify_date = clone $obj->date;
        }
        $obj->acknowledge = (isset($values->acknowledge)) ? new UsDateTime($values->acknowledge, null) : null;
        $obj->status = (isset($values->status)) ? intval($values->status) : Personalappointments_model::STATUS_CONFIRMED;
        $obj->state_color_id = (isset($values->state_color_id)) ? intval($values->state_color_id) : null;
        $obj->private = isset($values->is_private) ? boolval($values->is_private) : null;
        $obj->project = $values->project_id;
        $obj->staff = $values->staff_to_id;
        $obj->location = (isset($values->location)) ? $values->location : '';
        $obj->url = (isset($values->url)) ? $values->url : '';
        $obj->summary = (isset($values->summary)) ? $values->summary : '';
        $obj->comment = (isset($values->comment)) ? $values->comment : '';
        
        
        $obj->multistaff = (isset($values->multistaff) && is_array($values->multistaff)) ? $values->multistaff : array();
        if (count($obj->multistaff)<1) {
            $obj->multistaff = array($obj->staff);
        }

        if (property_exists($values, 'participants')) {
            $obj->participants = is_array($values->participants) ? $values->participants : array();
        } else {
            $obj->participants = array();
        }
        
        $obj->several_days_day_yn = false;
        $obj->several_days_last_date = $obj->date;
        $obj->several_days_day = 0;        
        if(isset($values->several_days_last_date)) {
            $several_days_last_date = ($values->several_days_last_date instanceof UsDate) ? $values->several_days_last_date : new UsDate($values->several_days_last_date, null);
            $days = datetime_diff_days($obj->date, $several_days_last_date);
            if ($days > 0) {
                $obj->several_days_day_yn = true;
                $obj->several_days_last_date = $several_days_last_date;
                $obj->several_days_day = $days + 1;
            }
        }

        $obj->periodical__number = 0;
        $obj->periodical__unit = 0;
        $obj->periodical_last_date = $obj->date;
        $obj->periodical_yn = (isset($values->periodical) && $values->periodical) ? true : false;
        if ($obj->periodical_yn) {
            $obj->periodical__number = $values->periodical;
            $obj->periodical__unit = (isset($values->periodical__unit)) ? $values->periodical__unit : $obj->periodical__unit;
            $obj->periodical_last_date = ($values->periodical_last_date instanceof UsDate) ? $values->periodical_last_date : new UsDate($values->periodical_last_date, null);
        }
        $obj->app_type = $values->app_type ?? $obj->get_app_type();

        $obj->editconfirm_dialog_sel_user = property_exists($values, 'editconfirm_dialog_sel_user') ? $values->editconfirm_dialog_sel_user : null;
        $obj->editconfirm_dialog_all_users = property_exists($values, 'editconfirm_dialog_all_users') ? $values->editconfirm_dialog_all_users : null;
        $obj->editconfirm_dialog_from_today = boolval($values->editconfirm_dialog_from_today ?? false);
        $obj->allow_change_changed = boolval($values->allow_change_changed ?? false);
        
        $obj->allow_datetime_collision = (isset($values->allow_datetime_collision) && is_array($values->allow_datetime_collision)) ? $values->allow_datetime_collision : false;
        $obj->allow_all_datetime_collision = property_exists($values, 'allow_all_datetime_collision') ? $values->allow_all_datetime_collision : false;
        
        $obj->mobile_dav_mode = false;

        $obj->init_from_values_extend($obj, $values);
        
        return $obj;
    }

    /**
     * Validate and ensure all values are appropriate
     *
     * @return  mixed   false on success, else the error message
     *
     * @todo    implement!!
     */
    public function validate()
    {
        $this->CI->lang->load('personalapp', $this->CI->web_settings->getLanguage());

        if ($this->add_or_id!=='add' && !is_numeric($this->add_or_id)) {
            return $this->CI->lang->line('personalapp_wrong_id');
        }

        if ($this->from_yn && ($this->from->is_null() || $this->to->is_null())) {
            return $this->CI->lang->line('personalapp_no_fromto');
        }

        if ($this->notify_yn &&($this->notify_date->is_null() || $this->notify_time->is_null())) {
            return $this->CI->lang->line('personalapp_no_notifydatetime');
        }

        return false;
    }

    /**
     * Get personal appointment values to save
     *
     * @return  array
     */
    public function get_appointment_to_save()
    {
        $data = array(
            'date'                          =>  $this->date->mysql(),#m
            'staff_to_id'                   =>  $this->staff,#m
            'location'                      =>  $this->location,#m
            'summary'                       =>  $this->rem_newlines($this->summary ??''),#m
            'comment'                       =>  $this->comment,#m
        );

        if (isset($this->url)) {
            $data['url'] = $this->rem_newlines($this->url ??'');
        }
        if (isset($this->project)) {
            $data['project_id'] = $this->project;
        }
        if (isset($this->private)) {
            $data['is_private'] = $this->private;
        }
        if (isset($this->status)) {
            $data['status'] = $this->status;
        }
        if (isset($this->state_color_id)) {
            $data['state_color_id'] = $this->state_color_id;
        }
        
        $data['is_notify'] = $this->notify_yn;
        if ($this->notify_yn) {
            $data['notify_date'] = $this->notify_date->mysqlday().' '.$this->notify_time->us_ltime();
        }
        $data['app_type'] = $this->get_app_type();
        $data['periodical'] = $this->periodical__unit ?: 0;
        
        if (!$this->mobile_dav_mode || $this->add_or_id === 'add') {
            $data['several_days_last_date'] = $this->several_days_last_date->mysql();
            $data['periodical_last_date'] = $this->periodical_last_date->mysql();
            if (!$this->notify_yn) {
                $data['notify_date'] = null;
            }
        }

        if ($this->from_yn) {
            $data['time_from'] = $this->from->mysql();
            $data['time_to'] = $this->to->mysql();
        } else {
            $time = new UsTime('now');
            $time->setTime(0, 0, 0);
            $data['time_from'] = $time->mysql();
            $time->setTime(0, 0, 0);
            $data['time_to'] = $time->mysql();
        }

        return $data;
    }
    
    /**
     * Get import view vars
     *
     * @return  array
     */
    public function get_import_view_vars()
    {
        return array(
            'type'              =>      'personalapp',
            'from_yn'           =>      $this->from_yn,
            'date_obj'          =>      $this->date,
            'time_from_obj'     =>      $this->from,
            'time_to_obj'       =>      $this->to,
            'several_days_day_yn'           =>      $this->several_days_day_yn,
            'several_days_last_date_obj'    =>      $this->several_days_last_date,
        );
    }

    /**
     * Get editconfirm values if an appointment is updated
     *
     * @return  array
     */
    public function get_editconfirm_values()
    {

        $data = array(
            'editconfirm_dialog_sel_user'       =>  $this->editconfirm_dialog_sel_user,
            'editconfirm_dialog_all_users'      =>  $this->editconfirm_dialog_all_users,
            'editconfirm_dialog_from_today'      =>  $this->editconfirm_dialog_from_today,
        );
        return $data;
    }

    /**
     * Get participants to save
     *
     * @return  array
     */
    public function get_participants_to_save()
    {
        return (is_array($this->participants)) ?$this->participants: array();
    }

    /**
     * Get the App Type of the appointment
     *
     * @return  integer   App Type
     */
    public function get_app_type()
    {
        if ($this->add_or_id === 'add') {
            $multiuser = (is_array($this->multistaff) && count($this->multistaff) > 1) ? true : false;
            $multday = ($this->several_days_day_yn === true) ? true : false;
            $periodical = ($this->periodical_yn === true) ? true : false;

            $app_type = $this->CI->personalapp->get_app_type($multiuser, $multday, $periodical);
        } elseif (empty($this->app_type)) {
            $item = $this->CI->personalapp->get_item($this->add_or_id);
            if (empty($item)) {
                basic_error('Personalappointment with ID:'.$this->add_or_id.' not found!', null, __CLASS__.'::'.__FUNCTION__);
            }
            $app_type = $item->app_type;
        } elseif (!empty($this->app_type)) {
            $app_type = $this->app_type;
        } else {
            basic_error('Wrong appointment values!', null, __CLASS__.'::'.__FUNCTION__);
        }
        return $app_type;
    }

    /**
     * Get all staff for an appointment
     *
     * @return  array     Multistaff Array
     */
    public function getMultistaff()
    {
        return $this->multistaff;
    }

    /**
     * Get all or a spcific users confirmation value(s)
     *
     * @param   string      $key    Optional a key of users confirmation value
     *
     * @return  mixed       Either an array of all users confirmation values or a specific confirmation value (can be from all datatypes)
     */
    public function get_users_confirmation_values($key=false)
    {
        if($key){
            return $this->$key;
        } else {
            $users_confirmation_values = array();
            $users_confirmation_values['allow_datetime_collision'] = $this->allow_datetime_collision ;
            $users_confirmation_values['allow_all_datetime_collision'] = $this->allow_all_datetime_collision ;
            $users_confirmation_values['allow_change_changed'] = $this->allow_change_changed ;

            return $users_confirmation_values;
        }
    }
    
    /**
     * Is for mobile version?
     * 
     * @return  boolean
     */
    function get_mobile_dav_mode()
    {
        return $this->mobile_dav_mode;
    }
    
    /**
     * Set project ID
     * 
     * @param   int     $id     New ID
     */
    function setProject_id(int $id): void
    {
        $this->project = $id;
    }

    /**
     * Get the acknowledge date and time, if any
     * 
     * @return UsDateTime|null
     */
    public function getAcknowledge(): ?UsDateTime
    {
        return $this->acknowledge;
    }    
    
    /**
     * Returns the names of all variables of this object that should be serialized
     *
     * @return  array
     */
    public function __sleep()
    {
        return array(
            "add_or_id",
            "date",
            "from_yn",
            "from",
            "to",
            "notify_yn",
            "notify_date",
            "notify_time",
            "status",
            "state_color_id",
            "private",
            "project",
            "staff",
            "location",
            "url",
            "participants",
            "summary",
            "comment",
            "multistaff",
            "app_type",
            "several_days_day_yn",
            "several_days_day",
            "several_days_last_date",
            "periodical_yn",
            "periodical__number",
            "periodical__unit",
            "periodical_last_date",
            "editconfirm_dialog_sel_user",
            "editconfirm_dialog_all_users",
            "editconfirm_dialog_from_today",
            "mobile_dav_mode",
            "allow_datetime_collision",
            "allow_all_datetime_collision",
            "allow_change_changed"
        );
    }
    
    /**
     * Reconstruct any resources
     *
     * @return  void
     */
    public function __wakeup()
    {
        $this->CI = CI_Controller::get_instance();
    }
    
}
