<?php defined('BASEPATH') OR exit('No direct script access allowed');

/* ----------------------------------------------------------------------------
 * Easy!Appointments - Open Source Web Scheduler
 *
 * @package     EasyAppointments
 * @author      A.Tselegidis <alextselegidis@gmail.com>
 * @copyright   Copyright (c) 2013 - 2018, Alex Tselegidis
 * @license     http://opensource.org/licenses/GPL-3.0 - GPLv3
 * @link        http://easyappointments.org
 * @since       v1.0.0
 * ---------------------------------------------------------------------------- */

use \EA\Engine\Types\Text;
use \EA\Engine\Types\Email;
use \EA\Engine\Types\Url;
//use Twilio\Rest\Client;

/**
 * Backend API Controller
 *
 * Contains all the backend AJAX callbacks.
 *
 * @package Controllers
 */
class Backend_api extends CI_Controller {
    /**
     * @var array
     */
    protected $privileges;

    /**
     * Class Constructor
     */
    public function __construct()
    {
        parent::__construct();

        // All the methods in this class must be accessible through a POST request.
        if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST')
        {
            $this->security->csrf_show_error();
        }

        $this->load->library('session');
        $this->load->model('roles_model');

        if ($this->session->userdata('role_slug'))
        {
            $this->privileges = $this->roles_model->get_privileges($this->session->userdata('role_slug'));
        }

        // Set user's selected language.
        if ($this->session->userdata('language'))
        {
            $this->config->set_item('language', $this->session->userdata('language'));
            $this->lang->load('translations', $this->session->userdata('language'));
        }
        else
        {
            $this->lang->load('translations', $this->config->item('language')); // default
        }
    }

    protected function sendsms1($account,$auth,$from,$to,$body,$purl='') {

        try
        {
        $fromEncode = "From=" .  urlencode($from) . "&To=". urlencode($to) . "&Body=" . urlencode($body);

       // echo $fromEncode . "<br>";

      //  echo base64_encode("$account:$auth");

        $curl = curl_init();

        curl_setopt_array($curl, array(
            CURLOPT_URL => "https://api.twilio.com/2010-04-01/Accounts/$account/Messages.json",
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING => '',
            CURLOPT_MAXREDIRS => 10,
            CURLOPT_TIMEOUT => 0,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_CUSTOMREQUEST => 'POST',
            CURLOPT_POSTFIELDS => $fromEncode,
            CURLOPT_HTTPHEADER => array(
                "Authorization: Basic " . base64_encode("$account:$auth"),
                'Content-Type: application/x-www-form-urlencoded'
            ),
        ));


        $response = curl_exec($curl);


        $reponseJson =  json_encode($response);
        $tmp = json_decode($reponseJson, true);
        //print_r("<pre>" . $tmp . "</pre>");
        //return $tmp['sid'];
        }
        catch (Exception $exc)
        {
            throw new \RuntimeException('Email could not been sent. Mailer Error (Line ' . __LINE__ . '): '
                . $exc);
        }
    }

    public function ajax_get_waitlistid()
{
    try
        {
            $this->output->set_content_type('application/json');
            $this->load->model('waitlistlinkvar_model');
            
                        $record_id = $this->db->escape($_POST['record_id']);
      $where_clause = 'id'. ' = ' . $record_id ;

            $response['waitlisters'] = $this->waitlistlinkvar_model->get_batch($where_clause);


            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode($response));
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
         
}

    /**
     * Get Calendar Events
     *
     * This method will return all the calendar events within a specified period.
     */
    public function ajax_get_calendar_events()
    {
        try
        {
            $this->output->set_content_type('application/json');
            $this->load->model('appointments_model');
            $this->load->model('customers_model');
            $this->load->model('services_model');
            $this->load->model('providers_model');

            $startDate = $this->input->post('startDate') . ' 00:00:00';
            $endDate = $this->input->post('endDate') . ' 23:59:59';

            $response = [
                'appointments' => $this->appointments_model->get_batch([
                    'is_unavailable' => FALSE,
                    'start_datetime >=' => $startDate,
                    'end_datetime <=' => $endDate
                ]),
                'unavailabilities' => $this->appointments_model->get_batch([
                    'is_unavailable' => TRUE,
                    'start_datetime >=' => $startDate,
                    'end_datetime <=' => $endDate
                ])
            ];

            foreach ($response['appointments'] as &$appointment)
            {
                $appointment['provider'] = $this->providers_model->get_row($appointment['id_users_provider']);
                $appointment['service'] = $this->services_model->get_row($appointment['id_services']);
                $appointment['customer'] = $this->customers_model->get_row($appointment['id_users_customer']);
            }

            $userId = $this->session->userdata('user_id');
            $roleSlug = $this->session->userdata('role_slug');

            // If the current user is a provider he must only see his own appointments. 
            if ($roleSlug === DB_SLUG_PROVIDER)
            {
                foreach ($response['appointments'] as $index => $appointment)
                {
                    if ((int)$appointment['id_users_provider'] !== (int)$userId)
                    {
                        unset($response['appointments'][$index]);
                    }
                }

                foreach ($response['unavailabilities'] as $index => $unavailability)
                {
                    if ((int)$unavailability['id_users_provider'] !== (int)$userId)
                    {
                        unset($response['unavailabilities'][$index]);
                    }
                }
            }

            // If the current user is a secretary he must only see the appointments of his providers.
            if ($roleSlug === DB_SLUG_SECRETARY)
            {
                $this->load->model('secretaries_model');
                $providers = $this->secretaries_model->get_row($userId)['providers'];
                foreach ($response['appointments'] as $index => $appointment)
                {
                    if ( ! in_array((int)$appointment['id_users_provider'], $providers))
                    {
                        unset($response['appointments'][$index]);
                    }
                }

                foreach ($response['unavailabilities'] as $index => $unavailability)
                {
                    if ( ! in_array((int)$unavailability['id_users_provider'], $providers))
                    {
                        unset($response['unavailabilities'][$index]);
                    }
                }
            }

            $this->output->set_output(json_encode($response));
        }
        catch (Exception $exc)
        {
            $this->output->set_output(json_encode([
                'exceptions' => [exceptionToJavaScript($exc)]
            ]));
        }
    }

    /**
     * [AJAX] Get the registered appointments for the given date period and record.
     *
     * This method returns the database appointments and unavailable periods for the
     * user selected date period and record type (provider or service).
     *
     * Required POST Parameters:
     *
     * - int $_POST['record_id'] Selected record id.
     * - string $_POST['filter_type'] Could be either FILTER_TYPE_PROVIDER or FILTER_TYPE_SERVICE.
     * - string $_POST['start_date'] The user selected start date.
     * - string $_POST['end_date'] The user selected end date.
     */
    public function ajax_get_calendar_appointments()
    {
        try
        {
            if ($this->privileges[PRIV_APPOINTMENTS]['view'] == FALSE)
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            if ( ! $this->input->post('filter_type'))
            {
                $this->output
                    ->set_content_type('application/json')
                    ->set_output(json_encode(['appointments' => []]));
                return;
            }

            $this->load->model('appointments_model');
            $this->load->model('providers_model');
            $this->load->model('services_model');
            $this->load->model('customers_model');

            if ($this->input->post('filter_type') == FILTER_TYPE_PROVIDER)
            {
                $where_id = 'id_users_provider';
            }
            else
            {
                $where_id = 'id_services';
            }

            // Get appointments
            $record_id = $this->db->escape($_POST['record_id']);
            $start_date = $this->db->escape($_POST['start_date']);
            $end_date = $this->db->escape(date('Y-m-d', strtotime($_POST['end_date'] . ' +1 day')));

            $where_clause = $where_id . ' = ' . $record_id . '
                AND ((start_datetime > ' . $start_date . ' AND start_datetime < ' . $end_date . ') 
                or (end_datetime > ' . $start_date . ' AND end_datetime < ' . $end_date . ') 
                or (start_datetime <= ' . $start_date . ' AND end_datetime >= ' . $end_date . ')) 
                AND is_unavailable = 0
            ';

            $response['appointments'] = $this->appointments_model->get_batch($where_clause);

            foreach ($response['appointments'] as &$appointment)
            {
                $appointment['provider'] = $this->providers_model->get_row($appointment['id_users_provider']);
                $appointment['service'] = $this->services_model->get_row($appointment['id_services']);
                $appointment['customer'] = $this->customers_model->get_row($appointment['id_users_customer']);
            }

            // Get unavailable periods (only for provider).
            if ($this->input->post('filter_type') == FILTER_TYPE_PROVIDER)
            {
                $where_clause = $where_id . ' = ' . $record_id . '
                    AND ((start_datetime > ' . $start_date . ' AND start_datetime < ' . $end_date . ') 
                    or (end_datetime > ' . $start_date . ' AND end_datetime < ' . $end_date . ') 
                    or (start_datetime <= ' . $start_date . ' AND end_datetime >= ' . $end_date . ')) 
                    AND is_unavailable = 1
                ';

                $response['unavailables'] = $this->appointments_model->get_batch($where_clause);
            }

            if ($this->input->post('filter_type') == FILTER_TYPE_SERVICE)
            {
                $where_clause = $where_id . ' = ' . $record_id . '
                    AND ((start_datetime > ' . $start_date . ' AND start_datetime < ' . $end_date . ') 
                    or (end_datetime > ' . $start_date . ' AND end_datetime < ' . $end_date . ') 
                    or (start_datetime <= ' . $start_date . ' AND end_datetime >= ' . $end_date . ')) 
                    AND is_unavailable = 3
                ';

            }else if($this->input->post('filter_type') == FILTER_TYPE_PROVIDER){
                $provider = $this->providers_model->get_row($_POST['record_id']);
                $where_clause ='((start_datetime > ' . $start_date . ' AND start_datetime < ' . $end_date . ') 
                    or (end_datetime > ' . $start_date . ' AND end_datetime < ' . $end_date . ') 
                    or (start_datetime <= ' . $start_date . ' AND end_datetime >= ' . $end_date . ')) 
                    AND is_unavailable = 3';

                $Countofservices = count($provider['services']);
                if($Countofservices > 0 )
                    $where_clause .= " AND (";

                for($i=0; $i<$Countofservices; $i++) {
                    if(($i+1) == $Countofservices) {
                        $where_clause .= '(id_services' . ' = ' . $provider['services'][$i] .' ))';
                        }else{
                        $where_clause .= '(id_services' . ' = ' . $provider['services'][$i].') OR ';
                    }

                }
               // throw new Exception(var_dump($provider['services']));

                //throw new Exception($where_clause);

                }
            $response['maxapptperiods'] = $this->appointments_model->get_batch($where_clause);
            foreach ($response['maxapptperiods'] as &$maxappointment)
            {
                $maxappointment['service'] = $this->services_model->get_row($maxappointment['id_services']);
            }




            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode($response));
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }




/** 
 * 
 * 
 *API GET CALENDAR INFO
 * 
 * */

    public function ajax_get_api_calendar_appointments()
    {
        try
        {
            if ($this->privileges[PRIV_APPOINTMENTS]['view'] == FALSE)
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            $this->load->model('appointments_model');
            $this->load->model('providers_model');
            $this->load->model('services_model');
            $this->load->model('customers_model');


            // Get appointments
            $record_id = $this->db->escape($_POST['record_id']);

            $where_clause = 'id'. ' = ' . $record_id . '
                AND is_unavailable = 0
            ';

            $response['appointments'] = $this->appointments_model->get_batch($where_clause);

            foreach ($response['appointments'] as &$appointment)
            {
                $appointment['provider'] = $this->providers_model->get_row($appointment['id_users_provider']);
                $appointment['service'] = $this->services_model->get_row($appointment['id_services']);
                $appointment['customer'] = $this->customers_model->get_row($appointment['id_users_customer']);
            }


            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode($response));
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }


    /**
     * [AJAX] Validation to tell backend user warnings INCLUDING
     *
     * 1. Waitlist Doses (overbooking)
     * 2. Not following max/min age rules
     * 3. Booking during unavailable time
     * 4. Booking during Max appt period time
     *
     *

    public function ajax_backend_validation()
    {
        try {
            $warnings =[];
            $this->load->model('appointments_model');
            $this->load->model('services_model');

            if ($this->input->post('appointment_data')) {
                $appointment = json_decode($this->input->post('appointment_data'), TRUE);

                $REQUIRED_PRIV = (!isset($appointment['id']))
                    ? $this->privileges[PRIV_APPOINTMENTS]['add']
                    : $this->privileges[PRIV_APPOINTMENTS]['edit'];
                if ($REQUIRED_PRIV == FALSE) {
                    throw new Exception('You do not have the required privileges for this task.');
                }


                $service = $this->services_model->get_row($appointment['id_services']);

                if($service['waitlistonofffunction'] == 'ON'){
                    $appointmentdoses = $this->appointments_model->getdosesappointments($service);
                    $getunexpiredwaitlistpeople = $this->appointments_model->getunexpiredwaitlistpeople($service);
                    $getpeopleonwaitlistwithnostatus = $this->appointments_model->getpeopleonwaitlistwithnostatus($service);


                    $availabledoses = $service['waitlist_doses'] - $appointmentdoses - $getunexpiredwaitlistpeople - $getpeopleonwaitlistwithnostatus;


                    if($availabledoses > 0){

                        $this->services_model->_updatewaitlistoff($service);

                    }else{
                        $this->services_model->_updatewaitliston($service);
                        array_push($warnings, 100);


                    }
                }


                $service_duration = $this->services_model->get_value('duration', $appointment['id_services']);

                $exclude_appointments = (isset($appointment['id'])) ? [$appointment['id']] : [];

                $attendants_number = $this->services_model->get_value('attendants_number', $appointment['id_services']);

                if ($attendants_number > 1)
                {

                    if(isset($appointment['id'])){
                        $addappointmentid =$appointment['id'];
                    }else{
                        $addappointmentid=NULL;
                    }
                    // Exclude all the appointments that will are currently registered.
                    $exclude = $this->appointments_model->getbatchforappointment($appointment['id_services'],$appointment['start_datetime'],$addappointmentid);

                    if ( ! empty($exclude) && count($exclude) < $attendants_number)
                    {
                        foreach ($exclude as $entry)
                        {
                            $exclude_appointments[] = $entry['id'];
                        }
                    }
                }


                $available_periods = $this->_get_provider_available_time_periods(
                    $appointment['id_users_provider'], $appointment['id_services'],
                    date('Y-m-d', strtotime($appointment['start_datetime'])),
                    $exclude_appointments);

                $is_still_available = FALSE;

                foreach ($available_periods as $period)
                {
                    $appt_start = new DateTime($appointment['start_datetime']);
                    $appt_start = $appt_start->format('H:i');

                    $appt_end = new DateTime($appointment['start_datetime']);
                    $appt_end->add(new DateInterval('PT' . $service_duration . 'M'));
                    $appt_end = $appt_end->format('H:i');

                    $period_start = date('H:i', strtotime($period['start']));
                    $period_end = date('H:i', strtotime($period['end']));

                    if ($period_start <= $appt_start && $period_end >= $appt_end)
                    {
                        $is_still_available = TRUE;
                        break;
                    }
                }

                if($is_still_available == false){
                    array_push($warnings, 200);
                }

                if ($this->input->post('customer_data'))
                {
                    $customer = json_decode($this->input->post('customer_data'), TRUE);

                    $REQUIRED_PRIV = ( ! isset($customer['id']))
                        ? $this->privileges[PRIV_CUSTOMERS]['add']
                        : $this->privileges[PRIV_CUSTOMERS]['edit'];
                    if ($REQUIRED_PRIV == FALSE)
                    {
                        throw new Exception('You do not have the required privileges for this task.');
                    }

                    $dateOfBirth = $customer['birthdate'];
                    $today = date("Y-m-d");
                    $diff = date_diff(date_create($dateOfBirth), date_create($today));
                    $age = $diff->format('%y');
                    if ($age > $service['minimum'] && $age < $service['maximum']) {
                    }

                    }else {
                    array_push($warningsToUser, 300);


                }




            }

        } catch (Exception $exc) {
            $warnings[] = exceptionToJavaScript($exc);
        }
    }
*/

    /**
     * Remove the max appointment periods  from the available time periods of the selected date IF max appts has reached liit.
     *
     * @param array $periods Available time periods.
     * @param array $maxapptperiods Max appt periods of the current date.
     *
     * @return array Returns the available time periods without the unavailabilities.
     */
    public function remove_maxappts($periods, $maxapptperiods, $service_id)
    {
        foreach ($maxapptperiods as $maxapptp) {
            $maxappt_start = new DateTime($maxapptp['start_datetime']);
            $maxappt_end= new DateTime($maxapptp['end_datetime']);

            //throw new Exception(var_dump($maxappt_start));

            $maxappt_number = $maxapptp['max_appts'];

            $availableapptday = $this->appointments_model->get_appt_number_per_day([
                'id_services' => $service_id,
                'is_unavailable' => 0,
                'start_datetime <=' => $maxapptp['start_datetime'],
                'end_datetime >=' => $maxapptp['end_datetime'],

            ]);

            if ($availableapptday >= $maxappt_number) {
                foreach ($periods as &$period) {
                    $period_start =  $period['start'];
                    $period_end = $period['end'];

                    if ($maxappt_start <= $period_start && $maxappt_end >= $period_start && $maxappt_end <= $period_end) {
                        // left
                        $period['start'] = $maxappt_end;
                        continue;
                    }

                    if ($maxappt_start >= $period_start && $maxappt_start <= $period_end && $maxappt_end >= $period_start && $maxappt_end <= $period_end) {
                        // middle
                        $period['end'] = $maxappt_start;
                        $periods[] = [
                            'start' => $maxappt_end,
                            'end' => $period_end
                        ];
                        continue;
                    }

                    if ($maxappt_start >= $period_start && $maxappt_start <= $period_end && $maxappt_end >= $period_end) {
                        // right
                        $period['end'] = $maxappt_start;
                        continue;
                    }

                    if ($maxappt_start <= $period_start && $maxappt_end >= $period_end) {
                        // Unavaibility contains period
                        $period['start'] = $maxappt_end;
                        continue;
                    }
                }
            }
        }

        //throw new Exception(var_dump($periods));

        return $periods;
    }


    protected function _get_provider_available_time_periods(
        $provider_id,
        $service_id,
        $selected_date,
        $excluded_appointment_ids = []
    ) {
        $this->load->model('appointments_model');
        $this->load->model('providers_model');
        $this->load->model('services_model');

        // Get the service, provider's working plan and provider appointments.
        $working_plan = json_decode($this->providers_model->get_setting('working_plan', $provider_id), TRUE);

        $maxapptperiods = $this->appointments_model->get_batch([
            'is_unavailable' => 3,
            'id_services' => $service_id
        ]);

        $provider_appointments = $this->appointments_model->get_batch([
            'id_users_provider' => $provider_id,
        ]);

        // Sometimes it might be necessary to not take into account some appointment records in order to display what
        // the providers' available time periods would be without them.
        foreach ($excluded_appointment_ids as $excluded_appointment_id)
        {
            foreach ($provider_appointments as $index => $reserved)
            {
                if ($reserved['id'] == $excluded_appointment_id)
                {
                    unset($provider_appointments[$index]);
                }
            }
        }

        // Find the empty spaces on the plan. The first split between the plan is due to a break (if any). After that
        // every reserved appointment is considered to be a taken space in the plan.
        $selected_date_working_plan = $working_plan[strtolower(date('l', strtotime($selected_date)))];

        $periods = [];

        if (isset($selected_date_working_plan['breaks']))
        {
            $periods[] = [
                'start' => $selected_date_working_plan['start'],
                'end' => $selected_date_working_plan['end']
            ];

            $day_start = new DateTime($selected_date_working_plan['start']);
            $day_end = new DateTime($selected_date_working_plan['end']);

            // Split the working plan to available time periods that do not contain the breaks in them.
            foreach ($selected_date_working_plan['breaks'] as $index => $break)
            {
                $break_start = new DateTime($break['start']);
                $break_end = new DateTime($break['end']);

                if ($break_start < $day_start)
                {
                    $break_start = $day_start;
                }

                if ($break_end > $day_end)
                {
                    $break_end = $day_end;
                }

                if ($break_start >= $break_end)
                {
                    continue;
                }

                foreach ($periods as $key => $period)
                {
                    $period_start = new DateTime($period['start']);
                    $period_end = new DateTime($period['end']);

                    $remove_current_period = FALSE;


                    if ($break_start > $period_start && $break_start < $period_end && $break_end > $period_start)
                    {
                        $periods[] = [
                            'start' => $period_start->format('H:i'),
                            'end' => $break_start->format('H:i')
                        ];

                        $remove_current_period = TRUE;
                    }

                    if ($break_start < $period_end && $break_end > $period_start && $break_end < $period_end)
                    {
                        $periods[] = [
                            'start' => $break_end->format('H:i'),
                            'end' => $period_end->format('H:i')
                        ];

                        $remove_current_period = TRUE;
                    }

                    if ($break_start == $period_start && $break_end == $period_end)
                    {
                        $remove_current_period = TRUE;
                    }

                    if ($remove_current_period)
                    {
                        unset($periods[$key]);
                    }
                }
            }
        }


        $periods = $this->remove_maxappts($periods, $maxapptperiods, $service_id);

        // Break the empty periods with the reserved appointments.
        foreach ($provider_appointments as $provider_appointment)
        {
            foreach ($periods as $index => &$period)
            {
                $appointment_start = new DateTime($provider_appointment['start_datetime']);
                $appointment_end = new DateTime($provider_appointment['end_datetime']);
                $period_start = new DateTime($selected_date . ' ' . $period['start']);
                $period_end = new DateTime($selected_date . ' ' . $period['end']);

                if ($appointment_start <= $period_start && $appointment_end <= $period_end && $appointment_end <= $period_start)
                {
                    // The appointment does not belong in this time period, so we  will not change anything.
                }
                else
                {
                    if ($appointment_start <= $period_start && $appointment_end <= $period_end && $appointment_end >= $period_start)
                    {
                        // The appointment starts before the period and finishes somewhere inside. We will need to break
                        // this period and leave the available part.
                        $period['start'] = $appointment_end->format('H:i');
                    }
                    else
                    {
                        if ($appointment_start >= $period_start && $appointment_end < $period_end)
                        {
                            // The appointment is inside the time period, so we will split the period into two new
                            // others.
                            unset($periods[$index]);

                            $periods[] = [
                                'start' => $period_start->format('H:i'),
                                'end' => $appointment_start->format('H:i')
                            ];

                            $periods[] = [
                                'start' => $appointment_end->format('H:i'),
                                'end' => $period_end->format('H:i')
                            ];
                        }
                        else if ($appointment_start == $period_start && $appointment_end == $period_end)
                        {
                            unset($periods[$index]); // The whole period is blocked so remove it from the available periods array.
                        }
                        else
                        {
                            if ($appointment_start >= $period_start && $appointment_end >= $period_start && $appointment_start <= $period_end)
                            {
                                // The appointment starts in the period and finishes out of it. We will need to remove
                                // the time that is taken from the appointment.
                                $period['end'] = $appointment_start->format('H:i');
                            }
                            else
                            {
                                if ($appointment_start >= $period_start && $appointment_end >= $period_end && $appointment_start >= $period_end)
                                {
                                    // The appointment does not belong in the period so do not change anything.
                                }
                                else
                                {
                                    if ($appointment_start <= $period_start && $appointment_end >= $period_end && $appointment_start <= $period_end)
                                    {
                                        // The appointment is bigger than the period, so this period needs to be removed.
                                        unset($periods[$index]);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        return array_values($periods);
    }


        /**
     * [AJAX] Save appointment changes that are made from the backend calendar page.
     *
     * Required POST Parameters:
     *
     * - array $_POST['appointment_data'] (OPTIONAL) Array with the appointment data.
     * - array $_POST['customer_data'] (OPTIONAL) Array with the customer data.
     */
    public function ajax_save_appointment()
    {
        try
        {
            $this->load->model('appointments_model');
            $this->load->model('providers_model');
            $this->load->model('services_model');
            $this->load->model('customers_model');
            $this->load->model('settings_model');
                              $this->load->model('waitlistlinkvar_model');


            // :: SAVE CUSTOMER CHANGES TO DATABASE
            if ($this->input->post('customer_data'))
            {
                $customer = json_decode($this->input->post('customer_data'), TRUE);

                $REQUIRED_PRIV = ( ! isset($customer['id']))
                    ? $this->privileges[PRIV_CUSTOMERS]['add']
                    : $this->privileges[PRIV_CUSTOMERS]['edit'];
                if ($REQUIRED_PRIV == FALSE)
                {
                    throw new Exception('You do not have the required privileges for this task.');
                }

                $customer['id'] = $this->customers_model->add($customer);
            }

            // :: SAVE APPOINTMENT CHANGES TO DATABASE
            if ($this->input->post('appointment_data'))
            {
                $appointment = json_decode($this->input->post('appointment_data'), TRUE);

                $REQUIRED_PRIV = ( ! isset($appointment['id']))
                    ? $this->privileges[PRIV_APPOINTMENTS]['add']
                    : $this->privileges[PRIV_APPOINTMENTS]['edit'];
                if ($REQUIRED_PRIV == FALSE)
                {
                    throw new Exception('You do not have the required privileges for this task.');
                }

if( ! isset($appointment['waitlistid'])){
  
}else{

      $this->waitlistlinkvar_model->update($appointment['waitlistid']);
    
    unset($appointment['waitlistid']);
}
                $manage_mode = isset($appointment['id']);
                // If the appointment does not contain the customer record id, then it
                // means that is is going to be inserted. Get the customer's record id.
                if ( ! isset($appointment['id_users_customer']))
                {
                    $appointment['id_users_customer'] = $customer['id'];
                }

                $appointment['id'] = $this->appointments_model->add($appointment);
            }

            $appointment = $this->appointments_model->get_row($appointment['id']);
            $provider = $this->providers_model->get_row($appointment['id_users_provider']);
            $customer = $this->customers_model->get_row($appointment['id_users_customer']);
            $service = $this->services_model->get_row($appointment['id_services']);

if($service['waitlistonofffunction'] == 'ON'){
            $appointmentdoses = $this->appointments_model->getdosesappointments($service);
                        $getunexpiredwaitlistpeople = $this->appointments_model->getunexpiredwaitlistpeople($service);
                        $getpeopleonwaitlistwithnostatus = $this->appointments_model->getpeopleonwaitlistwithnostatus($service);

    
$availabledoses = $service['waitlist_doses'] - $appointmentdoses - $getunexpiredwaitlistpeople - $getpeopleonwaitlistwithnostatus; 

            
if($availabledoses > 0){

$this->services_model->_updatewaitlistoff($service); 

}else{
$this->services_model->_updatewaitliston($service); 


}
}
            $company_settings = [
                'company_name' => $this->settings_model->get_setting('company_name'),
                'company_link' => $this->settings_model->get_setting('company_link'),
                'company_email' => $this->settings_model->get_setting('company_email'),
                'date_format' => $this->settings_model->get_setting('date_format'),
                'time_format' => $this->settings_model->get_setting('time_format')
            ];

            // :: SYNC APPOINTMENT CHANGES WITH GOOGLE CALENDAR
            try
            {
                $google_sync = $this->providers_model->get_setting('google_sync',
                    $appointment['id_users_provider']);

                if ($google_sync == TRUE)
                {
                    $google_token = json_decode($this->providers_model->get_setting('google_token',
                        $appointment['id_users_provider']));

                    $this->load->library('Google_sync');
                    $this->google_sync->refresh_token($google_token->refresh_token);

                    if ($appointment['id_google_calendar'] == NULL)
                    {
                        $google_event = $this->google_sync->add_appointment($appointment, $provider,
                            $service, $customer, $company_settings);
                        $appointment['id_google_calendar'] = $google_event->id;
                        $this->appointments_model->add($appointment); // Store google calendar id.
                    }
                    else
                    {
                        $this->google_sync->update_appointment($appointment, $provider,
                            $service, $customer, $company_settings);
                    }
                }
            }
            catch (Exception $exc)
            {
                $warnings[] = exceptionToJavaScript($exc);
            }

            // :: SEND EMAIL NOTIFICATIONS TO PROVIDER AND CUSTOMER
            try
            {
                $this->config->load('email');
                $email = new \EA\Engine\Notifications\Email($this, $this->config->config);

                $send_provider = $this->providers_model
                    ->get_setting('notifications', $provider['id']);

                if ( ! $manage_mode)
                {
                    $customer_title = new Text($this->lang->line('appointment_booked'));
                    $customer_message = new Text($this->lang->line('thank_you_for_appointment'));
                    $provider_title = new Text($this->lang->line('appointment_added_to_your_plan'));
                    $provider_message = new Text($this->lang->line('appointment_link_description'));
                     $changes = ''; 

                }
                else
                {
                    
                    $customer_title = new Text($this->lang->line('appointment_changes_saved'));
                    $customer_message = new Text('');
                    $provider_title = new Text($this->lang->line('appointment_details_changed'));
                    $provider_message = new Text('');
                    $changes = 'Appointment Changes Saved'; 
                    

                }

                $customer_link = new Url(site_url('appointments/index/' . $appointment['hash']));
                $provider_link = new Url(site_url('backend/index/' . $appointment['hash']));

                $send_customer = $this->settings_model->get_setting('customer_notifications');

                $this->load->library('ics_file');
                $ics_stream = $this->ics_file->get_stream($appointment, $service, $provider, $customer);

                if ((bool)$send_customer === TRUE)
                {
                                                            if($appointment['dontsend'] !== 'DN'){


                    if (filter_var($customer['email'], FILTER_VALIDATE_EMAIL)){
                    $email->sendAppointmentDetails($appointment, $provider,
                        $service, $customer, $company_settings, $customer_title,
                        $customer_message, $customer_link, $customer['email'], new Email($customer['email']), new Text($ics_stream), Config::FRONTEND_COLOR);
                        
                                 $today = date("Y-m-d h:i:sa"); 
                 $addemaillog['date_sent'] = $today;
                 $addemaillog['pharmacy'] = Config::COMPANY_FIRST_NAME.' '.Config::COMPANY_LAST_NAME; 
                 $addemaillog['apptid'] = $appointment['id']; 
                 $addemaillog['reason'] = 'Customer Confirmation/Edit Emails-P'; 
                
       $this->appointments_model->sendemaillog($addemaillog); 
                }
                        if(!empty($customer['phone_number'])){
                        
                        if($this->lang->line('smsonoff') === 'ON'){             
                   
                   // require 'twilio-php-main/src/Twilio/autoload.php';

// Use the REST API Client to make requests to the Twilio REST API

// Your Account SID and Auth Token from twilio.com/console
$sid = Config::SID;
$token = Config::AUTH;
$millie1 = explode(" ",$appointment['start_datetime']);
$textmessage = $changes." "."Confirmation of appointment at"." ".$this->settings_model->get_setting('company_name')." "."for"." ".$millie1[0]." "."at"." ".date("g:i a", strtotime($appointment['start_datetime'])). " "."for"." ". $customer['first_name']." ".$customer['last_name'] ." ".$this->lang->line('smsconfirmmessage');
/*$client = new Client($sid, $token);

// Use the client to do fun stuff like send text messages!
$client->messages->create(
    // the number you'd like to send the message to
    $customer['phone_number'],
    [
        // A Twilio phone number you purchased at twilio.com/console
        'from' => Config::TWILNUM,
        // the body of the text message you'd like to send
        'body' => $textmessage

    ]
);*/
      $this->sendsms1($sid, $token, Config::TWILNUM, $customer['phone_number'], $textmessage);

                            $today = date("Y-m-d h:i:sa");

                 $addtextlog['date_sent'] = $today;
                 $addtextlog['pharmacy'] = Config::COMPANY_FIRST_NAME.' '.Config::COMPANY_LAST_NAME; 
                 $addtextlog['apptid'] = $appointment['id']; 
                 $addtextlog['reason'] = 'Customer Confirmation/Edit Text-P'; 
                 $addtextlog['lengthoftext'] = strlen($textmessage); 
$this->appointments_model->sendtextlog($addtextlog); 
}
}
                        
                }
            }

                if ($send_provider == TRUE)
                {
                    $pieces = explode(";", $provider['mobile_number']);
                    $email->sendAppointmentDetails($appointment, $provider,
                        $service, $customer, $company_settings, $provider_title,
                        $provider_message, $provider_link, $provider['mobile_number'], new Email($pieces[0]), new Text($ics_stream), Config::FRONTEND_COLOR);
                
                     $today = date("Y-m-d h:i:sa"); 
                 $addemaillog['date_sent'] = $today;
                 $addemaillog['pharmacy'] = Config::COMPANY_FIRST_NAME.' '.Config::COMPANY_LAST_NAME; 
                 $addemaillog['apptid'] = $appointment['id']; 
                 $addemaillog['reason'] = 'Provider Confirmation/Edit Emails-P'; 
                
       $this->appointments_model->sendemaillog($addemaillog);

                    
                }

            }
            catch (Exception $exc)
            {
                $warnings[] .= exceptionToJavaScript($exc);
            }



            if ( ! isset($warnings))
            {
                //$exceptions = 'test';
                $this->output
                    ->set_content_type('application/json')
                    ->set_output(json_encode(AJAX_SUCCESS));
                  // ->set_output(json_encode(['exceptions' => $exceptions]));

            }
            else
            {
                $this->output
                    ->set_content_type('application/json')

                    ->set_output(json_encode(['warnings' => $warnings]));
            }
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * [AJAX] Delete appointment from the database.
     *
     * This method deletes an existing appointment from the database. Once this action is finished it cannot be undone.
     * Notification emails are send to both provider and customer and the delete action is executed to the Google
     * Calendar account of the provider, if the "google_sync" setting is enabled.
     *
     * Required POST Parameters:
     *
     * - int $_POST['appointment_id'] The appointment id to be deleted.
     */
    public function ajax_delete_appointment()
    {
        try
        {
            if ($this->privileges[PRIV_APPOINTMENTS]['delete'] == FALSE)
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            if ( ! $this->input->post('appointment_id'))
            {
                throw new Exception('No appointment id provided.');
            }

            // :: STORE APPOINTMENT DATA FOR LATER USE IN THIS METHOD
            $this->load->model('appointments_model');
            $this->load->model('providers_model');
            $this->load->model('customers_model');
            $this->load->model('services_model');
            $this->load->model('settings_model');

            $appointment = $this->appointments_model->get_row($this->input->post('appointment_id'));
            $provider = $this->providers_model->get_row($appointment['id_users_provider']);
            $customer = $this->customers_model->get_row($appointment['id_users_customer']);
            $service = $this->services_model->get_row($appointment['id_services']);

            $company_settings = [
                'company_name' => $this->settings_model->get_setting('company_name'),
                'company_email' => $this->settings_model->get_setting('company_email'),
                'company_link' => $this->settings_model->get_setting('company_link'),
                'date_format' => $this->settings_model->get_setting('date_format'),
                'time_format' => $this->settings_model->get_setting('time_format')
            ];

            // :: DELETE APPOINTMENT RECORD FROM DATABASE
            $this->appointments_model->delete($this->input->post('appointment_id'));

         $today = date("Y-m-d h:i:sa"); 
$deleteapptslog['date_sent'] = $today; 
$deleteapptslog['pharmacy'] = Config::COMPANY_FIRST_NAME.' '.Config::COMPANY_LAST_NAME;
$deleteapptslog['apptid'] = $appointment['id']; 
$deleteapptslog['customer_id'] = $customer['id']; 
$deleteapptslog['customer_first_name'] = $customer['first_name']; 
$deleteapptslog['customer_last_name'] = $customer['last_name']; 
$deleteapptslog['email'] = $customer['email']; 
$deleteapptslog['phone'] = $customer['phone_number']; 
$deleteapptslog['email_sent'] = 'customer email'; 
$deleteapptslog['text_sent'] = 'customer text'; 
$deleteapptslog['start_datetime'] = $appointment['start_datetime']; 
$deleteapptslog['end_datetime'] = $appointment['end_datetime']; 
$deleteapptslog['provider_id'] = $appointment['id_users_provider']; 
$deleteapptslog['service_id'] = $appointment['id_services']; 
$deleteapptslog['hash'] = $appointment['hash']; 
$deleteapptslog['reason'] = $this->input->post('cancel_reason'); 
$deleteapptslog['book_datetime'] = $appointment['book_datetime']; 
$deleteapptslog['deletedby'] = 'PHARMACY'; 
$deleteapptslog['inputted_firstname'] = 'NA'; 
$deleteapptslog['inputted_lastname'] = 'NA'; 

                 $this->appointments_model->deletedapptslog($deleteapptslog); 
            // :: SYNC DELETE WITH GOOGLE CALENDAR
            if ($appointment['id_google_calendar'] != NULL)
            {
                try
                {
                    $google_sync = $this->providers_model->get_setting('google_sync', $provider['id']);

                    if ($google_sync == TRUE)
                    {
                        $google_token = json_decode($this->providers_model
                            ->get_setting('google_token', $provider['id']));
                        $this->load->library('Google_sync');
                        $this->google_sync->refresh_token($google_token->refresh_token);
                        $this->google_sync->delete_appointment($provider, $appointment['id_google_calendar']);
                    }
                }
                catch (Exception $exc)
                {
                    $warnings[] = exceptionToJavaScript($exc);
                }
            }

            // :: SEND NOTIFICATION EMAILS TO PROVIDER AND CUSTOMER
            try
            {
                $this->config->load('email');
                $email = new \EA\Engine\Notifications\Email($this, $this->config->config);

                $send_provider = $this->providers_model
                    ->get_setting('notifications', $provider['id']);

                if ((bool)$send_provider === TRUE)
                {
                    $pieces = explode(";", $provider['mobile_number']);
                    $email->sendDeleteAppointment($appointment, $provider,
                        $service, $customer, $company_settings, $provider['mobile_number'], new Email($pieces[0]),
                        new Text($this->input->post('delete_reason')), Config::FRONTEND_COLOR);
                        
                                       $today = date("Y-m-d h:i:sa"); 
                 $addemaillog['date_sent'] = $today;
                 $addemaillog['pharmacy'] = Config::COMPANY_FIRST_NAME.' '.Config::COMPANY_LAST_NAME; 
                 $addemaillog['apptid'] = $appointment['id']; 
                 $addemaillog['reason'] = 'Provider Delete Emails-P'; 
                
       $this->appointments_model->sendemaillog($addemaillog); 
                }

                $send_customer = $this->settings_model->get_setting('customer_notifications');

                if ((bool)$send_customer === TRUE)
                {
                                        if (filter_var($customer['email'], FILTER_VALIDATE_EMAIL)){
                    $email->sendDeleteAppointment($appointment, $provider,
                        $service, $customer, $company_settings, $customer['email'], new Email($customer['email']),
                        new Text($this->input->post('delete_reason')), Config::FRONTEND_COLOR);

                                              $today = date("Y-m-d h:i:sa"); 
                 $addemaillog['date_sent'] = $today;
                 $addemaillog['pharmacy'] = Config::COMPANY_FIRST_NAME.' '.Config::COMPANY_LAST_NAME; 
                 $addemaillog['apptid'] = $appointment['id']; 
                 $addemaillog['reason'] = 'Customer Delete Emails-P'; 
                
       $this->appointments_model->sendemaillog($addemaillog); 
                                        }
                    if(!empty($customer['phone_number'])){

                        if($this->lang->line('smsonoff') === 'ON'){             
                    
//require 'twilio-php-main/src/Twilio/autoload.php';

// Use the REST API Client to make requests to the Twilio REST API

// Your Account SID and Auth Token from twilio.com/console
$sid = Config::SID;
$token = Config::AUTH;
$millie1 = explode(" ",$appointment['start_datetime']);
$textmessage = "Appointment has been deleted at"." ".$this->settings_model->get_setting('company_name')." ". "on". " ".$millie1[0]." "."at"." ".date("g:i a", strtotime($appointment['start_datetime'])). " "."for"." ". $customer['first_name']. " ". $this->lang->line('smsdeletemessage');
/*$client = new Client($sid, $token);

// Use the client to do fun stuff like send text messages!
$client->messages->create(
    // the number you'd like to send the message to
    $customer['phone_number'],
    [
        // A Twilio phone number you purchased at twilio.com/console
        'from' => Config::TWILNUM,
        // the body of the text message you'd like to send
        'body' => $textmessage
    ]
);*/

                            $this->sendsms1($sid, $token, Config::TWILNUM, $customer['phone_number'], $textmessage);

                            $today = date("Y-m-d h:i:sa");
$addtextlog['date_sent'] = $today;
                 $addtextlog['pharmacy'] = Config::COMPANY_FIRST_NAME.' '.Config::COMPANY_LAST_NAME; 
                 $addtextlog['apptid'] = $appointment['id']; 
                 $addtextlog['reason'] = 'Customer Delete Text-F'; 
                 $addtextlog['lengthoftext'] = strlen($textmessage); 
$this->appointments_model->sendtextlog($addtextlog); 
                           
}
     }
                
                }
            }
            catch (Exception $exc)
            {
                $warnings[] = exceptionToJavaScript($exc);
            }

            // :: SEND RESPONSE TO CLIENT BROWSER
            if ( ! isset($warnings))
            {
                $this->output
                    ->set_content_type('application/json')
                    ->set_output(json_encode(AJAX_SUCCESS));
            }
            else
            {
                $this->output
                    ->set_content_type('application/json')
                    ->set_output(json_encode(['warnings' => $warnings]));
            }
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * [AJAX] Disable a providers sync setting.
     *
     * This method deletes the "google_sync" and "google_token" settings from the database. After that the provider's
     * appointments will be no longer synced with google calendar.
     *
     * Required POST Parameters:
     *
     * - string $_POST['provider_id'] The selected provider record id.
     */
    public function ajax_disable_provider_sync()
    {
        try
        {
            if ( ! $this->input->post('provider_id'))
            {
                throw new Exception('Provider id not specified.');
            }

            if ($this->privileges[PRIV_USERS]['edit'] == FALSE
                && $this->session->userdata('user_id') != $this->input->post('provider_id'))
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            $this->load->model('providers_model');
            $this->load->model('appointments_model');
            $this->providers_model->set_setting('google_sync', FALSE, $this->input->post('provider_id'));
            $this->providers_model->set_setting('google_token', NULL, $this->input->post('provider_id'));
            $this->appointments_model->clear_google_sync_ids($this->input->post('provider_id'));

            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(AJAX_SUCCESS));
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * [AJAX] Filter the customer records with the given key string.
     *
     * Required POST Parameters:
     *
     * - string $_POST['key'] The filter key string.
     *
     * Outputs the search results.
     */
    public function ajax_filter_customers()
    {
        try
        {
            if ($this->privileges[PRIV_CUSTOMERS]['view'] == FALSE)
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            $this->load->model('appointments_model');
            $this->load->model('services_model');
            $this->load->model('providers_model');
            $this->load->model('customers_model');

            $key = $this->db->escape_str($this->input->post('key'));
            $key = strtoupper($key);

            $where_clause =
                '(first_name LIKE upper("%' . $key . '%") OR ' .
                'last_name  LIKE upper("%' . $key . '%") OR ' .
                'email LIKE upper("%' . $key . '%") OR ' .
                'phone_number LIKE upper("%' . $key . '%") OR ' .
                'address LIKE upper("%' . $key . '%") OR ' .
                'city LIKE upper("%' . $key . '%") OR ' .
                'zip_code LIKE upper("%' . $key . '%") OR ' .
                'notes LIKE upper("%' . $key . '%"))';

            $customers = $this->customers_model->get_batch($where_clause);

            foreach ($customers as &$customer)
            {
                $appointments = $this->appointments_model
                    ->get_batch(['id_users_customer' => $customer['id']]);

                foreach ($appointments as &$appointment)
                {
                    $appointment['service'] = $this->services_model->get_row($appointment['id_services']);
                    $appointment['provider'] = $this->providers_model->get_row($appointment['id_users_provider']);
                }

                $customer['appointments'] = $appointments;
            }

            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode($customers));
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * [AJAX] Delete an Max appt time period from database.
     *
     * Required POST Parameters:
     *
     * - int $_POST['maxappt_id'] Record id to be deleted.
     */
    public function ajax_delete_maxappts()
    {
        try
        {
            if ($this->privileges[PRIV_APPOINTMENTS]['delete'] == FALSE)
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            $this->load->model('appointments_model');
            $this->load->model('services_model');

            $maxappt = $this->appointments_model->get_row($this->input->post('maxappt_id'));
            $service = $this->services_model->get_row($maxappt['id_services']);

            // Delete Maxappt period
            $this->appointments_model->delete_maxappts($maxappt['id']);

            // Google Sync
          /*  try
            {
                $google_sync = $this->providers_model->get_setting('google_sync', $provider['id']);
                if ($google_sync == TRUE)
                {
                    $google_token = json_decode($this->providers_model->get_setting('google_token', $provider['id']));
                    $this->load->library('google_sync');
                    $this->google_sync->refresh_token($google_token->refresh_token);
                    $this->google_sync->delete_unavailable($provider, $unavailable['id_google_calendar']);
                }
            }
            catch (Exception $exc)
            {
                $warnings[] = $exc;
            }*/

            if (isset($warnings))
            {
                $this->output
                    ->set_content_type('application/json')
                    ->set_output(json_encode(['warnings' => $warnings]));
            }
            else
            {
                $this->output
                    ->set_content_type('application/json')
                    ->set_output(json_encode(AJAX_SUCCESS));
            }

        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }


    public function ajax_save_maxappts()
    {
        try
        {

            // Check privileges
            $maxappts = json_decode($this->input->post('maxappts'), TRUE);

            $REQUIRED_PRIV = ( ! isset($maxappts['id']))
                ? $this->privileges[PRIV_APPOINTMENTS]['add']
                : $this->privileges[PRIV_APPOINTMENTS]['edit'];
            if ($REQUIRED_PRIV == FALSE)
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            $this->load->model('appointments_model');
            $this->load->model('providers_model');
            $this->load->model('services_model');

          //  $provider = $this->services_model->get_row($maxappts['id_services']);



            // Add appointment
            $maxappts['id'] = $this->appointments_model->add_maxappts($maxappts);
            $maxappts = $this->appointments_model->get_row($maxappts['id']); // fetch all inserted data


            if (isset($warnings))
            {
                $this->output
                    ->set_content_type('application/json')
                    ->set_output(json_encode(['warnings' => $warnings]));
            }
            else
            {
                $this->output
                    ->set_content_type('application/json')
                    ->set_output(json_encode(AJAX_SUCCESS));
            }

        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }


    /**
     * [AJAX] Insert of update unavailable time period to database.
     *
     * Required POST Parameters:
     *
     * - array $_POST['unavailable'] JSON encoded array that contains the unavailable period data.
     */
    public function ajax_save_unavailable()
    {
        try
        {
            // Check privileges
            $unavailable = json_decode($this->input->post('unavailable'), TRUE);

            $REQUIRED_PRIV = ( ! isset($unavailable['id']))
                ? $this->privileges[PRIV_APPOINTMENTS]['add']
                : $this->privileges[PRIV_APPOINTMENTS]['edit'];
            if ($REQUIRED_PRIV == FALSE)
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            $this->load->model('appointments_model');
            $this->load->model('providers_model');

            $provider = $this->providers_model->get_row($unavailable['id_users_provider']);

            // Add appointment
            $unavailable['id'] = $this->appointments_model->add_unavailable($unavailable);
            $unavailable = $this->appointments_model->get_row($unavailable['id']); // fetch all inserted data

            // Google Sync
            try
            {
                $google_sync = $this->providers_model->get_setting('google_sync',
                    $unavailable['id_users_provider']);

                if ($google_sync)
                {
                    $google_token = json_decode($this->providers_model->get_setting('google_token',
                        $unavailable['id_users_provider']));

                    $this->load->library('google_sync');
                    $this->google_sync->refresh_token($google_token->refresh_token);

                    if ($unavailable['id_google_calendar'] == NULL)
                    {
                        $google_event = $this->google_sync->add_unavailable($provider, $unavailable);
                        $unavailable['id_google_calendar'] = $google_event->id;
                        $this->appointments_model->add_unavailable($unavailable);
                    }
                    else
                    {
                        $google_event = $this->google_sync->update_unavailable($provider, $unavailable);
                    }
                }
            }
            catch (Exception $exc)
            {
                $warnings[] = $exc;
            }

            if (isset($warnings))
            {
                $this->output
                    ->set_content_type('application/json')
                    ->set_output(json_encode(['warnings' => $warnings]));
            }
            else
            {
                $this->output
                    ->set_content_type('application/json')
                    ->set_output(json_encode(AJAX_SUCCESS));
            }

        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * [AJAX] Delete an unavailable time period from database.
     *
     * Required POST Parameters:
     *
     * - int $_POST['unavailable_id'] Record id to be deleted.
     */
    public function ajax_delete_unavailable()
    {
        try
        {
            if ($this->privileges[PRIV_APPOINTMENTS]['delete'] == FALSE)
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            $this->load->model('appointments_model');
            $this->load->model('providers_model');

            $unavailable = $this->appointments_model->get_row($this->input->post('unavailable_id'));
            $provider = $this->providers_model->get_row($unavailable['id_users_provider']);

            // Delete unavailable
            $this->appointments_model->delete_unavailable($unavailable['id']);

            // Google Sync
            try
            {
                $google_sync = $this->providers_model->get_setting('google_sync', $provider['id']);
                if ($google_sync == TRUE)
                {
                    $google_token = json_decode($this->providers_model->get_setting('google_token', $provider['id']));
                    $this->load->library('google_sync');
                    $this->google_sync->refresh_token($google_token->refresh_token);
                    $this->google_sync->delete_unavailable($provider, $unavailable['id_google_calendar']);
                }
            }
            catch (Exception $exc)
            {
                $warnings[] = $exc;
            }

            if (isset($warnings))
            {
                $this->output
                    ->set_content_type('application/json')
                    ->set_output(json_encode(['warnings' => $warnings]));
            }
            else
            {
                $this->output
                    ->set_content_type('application/json')
                    ->set_output(json_encode(AJAX_SUCCESS));
            }

        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * [AJAX] Save (insert or update) a customer record.
     *
     * Require POST Parameters:
     *
     * - array $_POST['customer'] JSON encoded array that contains the customer's data.
     */
    public function ajax_save_customer()
    {
        try
        {
            $this->load->model('customers_model');
            $customer = json_decode($this->input->post('customer'), TRUE);

            $REQUIRED_PRIV = ( ! isset($customer['id']))
                ? $this->privileges[PRIV_CUSTOMERS]['add']
                : $this->privileges[PRIV_CUSTOMERS]['edit'];
            if ($REQUIRED_PRIV == FALSE)
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            $customer_id = $this->customers_model->add($customer);
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode([
                    'status' => AJAX_SUCCESS,
                    'id' => $customer_id
                ]));
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * [AJAX] Delete customer from database.
     *
     * Required POST Parameters:
     *
     * - int $_POST['customer_id'] Customer record id to be deleted.
     */
    public function ajax_delete_customer()
    {
        try
        {
            if ($this->privileges[PRIV_CUSTOMERS]['delete'] == FALSE)
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            $this->load->model('customers_model');
            $this->customers_model->delete($this->input->post('customer_id'));
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(AJAX_SUCCESS));
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * [AJAX] Save (insert or update) service record.
     *
     * Required POST Parameters:
     *
     * - array $_POST['service'] Contains the service data (json encoded).
     */
    public function ajax_save_service()
    {
        try
        {
            $this->load->model('services_model');
                        $this->load->model('appointments_model');
            $service = json_decode($this->input->post('service'), TRUE);
            
        if($service['waitlist_timetobook']>4800){
             throw new Exception('Waitlist time to book (in minutes) must be less than 4800 minutes');
        }
        
        if($service['waitlist_percentage']>100){
             throw new Exception('Waitlist doses multiplier must be less than 100 ');
        }
        
        if($service['waitlist_doses']>100000){
             throw new Exception('Waitlist doses must be less than 100,000: ');
        }

            $REQUIRED_PRIV = ( ! isset($service['id']))
                ? $this->privileges[PRIV_SERVICES]['add']
                : $this->privileges[PRIV_SERVICES]['edit'];
            if ($REQUIRED_PRIV == FALSE)
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            $service_id = $this->services_model->add($service);
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode([
                    'status' => AJAX_SUCCESS,
                    'id' => $service_id
                ]));
            $service['id'] =$service_id;

            if($service['waitlistonofffunction'] == 'ON'){
                $appointmentdoses = $this->appointments_model->getdosesappointments($service);
                $getunexpiredwaitlistpeople = $this->appointments_model->getunexpiredwaitlistpeople($service);
                $getpeopleonwaitlistwithnostatus = $this->appointments_model->getpeopleonwaitlistwithnostatus($service);

//throw new Exception('DOSES '.$service['waitlist_doses'].' APPOINTMENTS'.$appointmentdoses.' UNEXPIRED WAITLIST'.$getunexpiredwaitlistpeople. ' NOSTATUS:'.$getpeopleonwaitlistwithnostatus);
                $availabledoses = $service['waitlist_doses'] - $appointmentdoses - $getunexpiredwaitlistpeople - $getpeopleonwaitlistwithnostatus;


                if($availabledoses > 0){

                    $this->services_model->_updatewaitlistoff($service);

                }else{
                    $this->services_model->_updatewaitliston($service);
                }
            }
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * [AJAX] Delete service record from database.
     *
     * Required POST Parameters:
     *
     * - int $_POST['service_id'] Record id to be deleted.
     */
    public function ajax_delete_service()
    {
        try
        {
            if ($this->privileges[PRIV_SERVICES]['delete'] == FALSE)
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            $this->load->model('services_model');
            $result = $this->services_model->delete($this->input->post('service_id'));

            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode($result ? AJAX_SUCCESS : AJAX_FAILURE));
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * [AJAX] Filter service records by given key string.
     *
     * Required POST Parameters:
     *
     * - string $_POST['key'] Key string used to filter the records.
     *
     * Outputs a JSON encoded array back to client.
     */
    public function ajax_filter_services()
    {
        try
        {
            if ($this->privileges[PRIV_SERVICES]['view'] == FALSE)
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            $this->load->model('services_model');
            $key = $this->db->escape_str($this->input->post('key'));
            $where =
                '(name LIKE "%' . $key . '%" OR duration LIKE "%' . $key . '%" OR ' .
                'price LIKE "%' . $key . '%" OR currency LIKE "%' . $key . '%" OR ' .
                'description LIKE "%' . $key . '%")';
            $services = $this->services_model->get_batch($where);
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode($services));
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * [AJAX] Save (insert or update) category record.
     *
     * Required POST Parameters:
     *
     * - array $_POST['category'] Json encoded array with the category data. If an ID value is provided then the
     * category is going to be updated instead of inserted.
     */
    public function ajax_save_service_category()
    {
        try
        {
            $this->load->model('services_model');
            $category = json_decode($this->input->post('category'), TRUE);

            $REQUIRED_PRIV = ( ! isset($category['id']))
                ? $this->privileges[PRIV_SERVICES]['add']
                : $this->privileges[PRIV_SERVICES]['edit'];
            if ($REQUIRED_PRIV == FALSE)
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            $category_id = $this->services_model->add_category($category);

            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode([
                    'status' => AJAX_SUCCESS,
                    'id' => $category_id
                ]));
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * [AJAX] Delete category record from database.
     *
     * - int $_POST['category_id'] Record id to be deleted.
     */
    public function ajax_delete_service_category()
    {
        try
        {
            if ($this->privileges[PRIV_SERVICES]['delete'] == FALSE)
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            $this->load->model('services_model');
            $result = $this->services_model->delete_category($this->input->post('category_id'));

            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode($result ? AJAX_SUCCESS : AJAX_FAILURE));
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * [AJAX] Filter services categories with key string.
     *
     * Required POST Parameters:
     *
     * - string $_POST['key'] The key string used to filter the records.
     *
     * Outputs a JSON encoded array back to client with the category records.
     */
    public function ajax_filter_service_categories()
    {
        try
        {
            if ($this->privileges[PRIV_SERVICES]['view'] == FALSE)
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            $this->load->model('services_model');
            $key = $this->db->escape_str($this->input->post('key'));
            $where = '(name LIKE "%' . $key . '%" OR description LIKE "%' . $key . '%")';
            $categories = $this->services_model->get_all_categories($where);
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode($categories));
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * [AJAX] Filter admin records with string key.
     *
     * Required POST Parameters:
     *
     * - string $_POST['key'] The key string used to filter the records.
     *
     * Outputs a JSON encoded array back to client with the admin records.
     */
    public function ajax_filter_admins()
    {
        try
        {
            if ($this->privileges[PRIV_USERS]['view'] == FALSE)
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            $this->load->model('admins_model');
            $key = $this->db->escape_str($this->input->post('key'));
            $where =
                '(first_name LIKE "%' . $key . '%" OR last_name LIKE "%' . $key . '%" ' .
                'OR email LIKE "%' . $key . '%" OR mobile_number LIKE "%' . $key . '%" ' .
                'OR phone_number LIKE "%' . $key . '%" OR address LIKE "%' . $key . '%" ' .
                'OR city LIKE "%' . $key . '%" OR state LIKE "%' . $key . '%" ' .
                'OR zip_code LIKE "%' . $key . '%" OR notes LIKE "%' . $key . '%")';
            $admins = $this->admins_model->get_batch($where);
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode($admins));
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * [AJAX] Save (insert or update) admin record into database.
     *
     * Required POST Parameters:
     *
     * - array $_POST['admin'] A json encoded array that contains the admin data. If an 'id'
     * value is provided then the record is going to be updated.
     *
     * Outputs an array with the operation status and the record id that was saved into the database.
     */
    public function ajax_save_admin()
    {
        try
        {
            $this->load->model('admins_model');
            $admin = json_decode($this->input->post('admin'), TRUE);

            $REQUIRED_PRIV = ( ! isset($admin['id']))
                ? $this->privileges[PRIV_USERS]['add']
                : $this->privileges[PRIV_USERS]['edit'];
            if ($REQUIRED_PRIV == FALSE)
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            $admin_id = $this->admins_model->add($admin);

            $response = [
                'status' => AJAX_SUCCESS,
                'id' => $admin_id
            ];

            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode($response));
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * [AJAX] Delete an admin record from the database.
     *
     * Required POST Parameters:
     *
     * - int $_POST['admin_id'] The id of the record to be deleted.
     *
     * Outputs the operation result constant (AJAX_SUCCESS or AJAX_FAILURE).
     */
    public function ajax_delete_admin()
    {
        try
        {
            if ($this->privileges[PRIV_USERS]['delete'] == FALSE)
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            $this->load->model('admins_model');
            $result = $this->admins_model->delete($this->input->post('admin_id'));
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode($result ? AJAX_SUCCESS : AJAX_FAILURE));
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * [AJAX] Filter provider records with string key.
     *
     * Required POST Parameters:
     *
     * - string $_POST['key'] The key string used to filter the records.
     *
     * Outputs a JSON encoded array back to client with the provider records.
     */
    public function ajax_filter_providers()
    {
        try
        {
            if ($this->privileges[PRIV_USERS]['view'] == FALSE)
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            $this->load->model('providers_model');
            $key = $this->db->escape_str($this->input->post('key'));
            $where =
                '(first_name LIKE "%' . $key . '%" OR last_name LIKE "%' . $key . '%" ' .
                'OR email LIKE "%' . $key . '%" OR mobile_number LIKE "%' . $key . '%" ' .
                'OR phone_number LIKE "%' . $key . '%" OR address LIKE "%' . $key . '%" ' .
                'OR city LIKE "%' . $key . '%" OR state LIKE "%' . $key . '%" ' .
                'OR zip_code LIKE "%' . $key . '%" OR notes LIKE "%' . $key . '%")';
            $providers = $this->providers_model->get_batch($where);
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode($providers));
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * [AJAX] Save (insert or update) a provider record into database.
     *
     * Required POST Parameters:
     *
     * - array $_POST['provider'] A json encoded array that contains the provider data. If an 'id'
     * value is provided then the record is going to be updated.
     *
     * Outputs the success constant 'AJAX_SUCCESS' so javascript knows that everything completed successfully.
     */
    public function ajax_save_provider()
    {
        try
        {
            $this->load->model('providers_model');
            $provider = json_decode($this->input->post('provider'), TRUE);

            $REQUIRED_PRIV = ( ! isset($provider['id']))
                ? $this->privileges[PRIV_USERS]['add']
                : $this->privileges[PRIV_USERS]['edit'];
            if ($REQUIRED_PRIV == FALSE)
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            if ( ! isset($provider['settings']['working_plan']))
            {
                $this->load->model('settings_model');
                $provider['settings']['working_plan'] = $this->settings_model
                    ->get_setting('company_working_plan');
            }

            $provider_id = $this->providers_model->add($provider);


            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode([
                    'status' => AJAX_SUCCESS,
                    'id' => $provider_id
                ]));
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * [AJAX] Delete a provider record from the database.
     *
     * Required POST Parameters:
     *
     * - int $_POST['provider_id'] The id of the record to be deleted.
     *
     * Outputs the operation result constant (AJAX_SUCCESS or AJAX_FAILURE).
     */
    public function ajax_delete_provider()
    {
        try
        {
            if ($this->privileges[PRIV_USERS]['delete'] == FALSE)
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            $this->load->model('providers_model');
            $result = $this->providers_model->delete($this->input->post('provider_id'));
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode($result ? AJAX_SUCCESS : AJAX_FAILURE));
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * [AJAX] Filter secretary records with string key.
     *
     * Required POST Parameters:
     *
     * - string $_POST['key'] The key string used to filter the records.
     *
     * Outputs a JSON encoded array back to client with the secretary records.
     */
    public function ajax_filter_secretaries()
    {
        try
        {
            if ($this->privileges[PRIV_USERS]['view'] == FALSE)
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            $this->load->model('secretaries_model');
            $key = $this->db->escape_str($this->input->post('key'));
            $where =
                '(first_name LIKE "%' . $key . '%" OR last_name LIKE "%' . $key . '%" ' .
                'OR email LIKE "%' . $key . '%" OR mobile_number LIKE "%' . $key . '%" ' .
                'OR phone_number LIKE "%' . $key . '%" OR address LIKE "%' . $key . '%" ' .
                'OR city LIKE "%' . $key . '%" OR state LIKE "%' . $key . '%" ' .
                'OR zip_code LIKE "%' . $key . '%" OR notes LIKE "%' . $key . '%")';
            $secretaries = $this->secretaries_model->get_batch($where);
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode($secretaries));
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * [AJAX] Save (insert or update) a secretary record into database.
     *
     * Required POST Parameters:
     *
     * - array $_POST['secretary'] A json encoded array that contains the secretary data.
     * If an 'id' value is provided then the record is going to be updated.
     *
     * Outputs the success constant 'AJAX_SUCCESS' so JavaScript knows that everything completed successfully.
     */
    public function ajax_save_secretary()
    {
        try
        {
            $this->load->model('secretaries_model');
            $secretary = json_decode($this->input->post('secretary'), TRUE);

            $REQUIRED_PRIV = ( ! isset($secretary['id']))
                ? $this->privileges[PRIV_USERS]['add']
                : $this->privileges[PRIV_USERS]['edit'];
            if ($REQUIRED_PRIV == FALSE)
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            $secretary_id = $this->secretaries_model->add($secretary);

            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode([
                    'status' => AJAX_SUCCESS,
                    'id' => $secretary_id
                ]));
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * [AJAX] Delete a secretary record from the database.
     *
     * Required POST Parameters:
     *
     * - int $_POST['secretary_id'] The id of the record to be deleted.
     *
     * Outputs the operation result constant (AJAX_SUCCESS or AJAX_FAILURE).
     */
    public function ajax_delete_secretary()
    {
        try
        {
            if ($this->privileges[PRIV_USERS]['delete'] == FALSE)
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            $this->load->model('secretaries_model');
            $result = $this->secretaries_model->delete($this->input->post('secretary_id'));

            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode($result ? AJAX_SUCCESS : AJAX_FAILURE));
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * [AJAX] Save a setting or multiple settings in the database.
     *
     * This method is used to store settings in the database. It can be either system or user settings, one or many.
     * Use the $_POST variables accordingly.
     *
     * Required POST Parameters:
     *
     * - array $_POST['settings'] Contains an array with settings.
     * - bool $_POST['type'] Determines the settings type, can be either SETTINGS_SYSTEM or SETTINGS_USER.
     */
    public function ajax_save_settings()
    {
        try
        {
            if ($this->input->post('type') == SETTINGS_SYSTEM)
            {
                if ($this->privileges[PRIV_SYSTEM_SETTINGS]['edit'] == FALSE)
                {
                    throw new Exception('You do not have the required privileges for this task.');
                }
                $this->load->model('settings_model');
                $settings = json_decode($this->input->post('settings'), TRUE);
                $this->settings_model->save_settings($settings);
            }
            else
            {
                if ($this->input->post('type') == SETTINGS_USER)
                {
                    if ($this->privileges[PRIV_USER_SETTINGS]['edit'] == FALSE)
                    {
                        throw new Exception('You do not have the required privileges for this task.');
                    }
                    $this->load->model('user_model');
                    $this->user_model->save_settings(json_decode($this->input->post('settings'), TRUE));
                }
            }

            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(AJAX_SUCCESS));
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * [AJAX] This method checks whether the username already exists in the database.
     *
     * Required POST Parameters:
     *
     * - string $_POST['username'] Record's username to validate.
     * - bool $_POST['record_exists'] Whether the record already exists in database.
     */
    public function ajax_validate_username()
    {
        try
        {
            // We will only use the function in the admins_model because it is sufficient
            // for the rest user types for now (providers, secretaries).
            $this->load->model('admins_model');
            $is_valid = $this->admins_model->validate_username($this->input->post('username'),
                $this->input->post('user_id'));
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode($is_valid));
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * [AJAX] Change system language for current user.
     *
     * The language setting is stored in session data and retrieved every time the user visits any of the system pages.
     *
     * Required POST Parameters:
     *
     * - string $_POST['language'] Selected language name.
     */
    public function ajax_change_language()
    {
        try
        {
            // Check if language exists in the available languages.
            $found = FALSE;
            foreach ($this->config->item('available_languages') as $lang)
            {
                if ($lang == $this->input->post('language'))
                {
                    $found = TRUE;
                    break;
                }
            }

            if ( ! $found)
            {
                throw new Exception('Translations for the given language does not exist (' . $this->input->post('language') . ').');
            }

            $this->session->set_userdata('language', $this->input->post('language'));
            $this->config->set_item('language', $this->input->post('language'));

            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(AJAX_SUCCESS));

        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * This method will return a list of the available google calendars.
     *
     * The user will need to select a specific calendar from this list to sync his appointments with. Google access must
     * be already granted for the specific provider.
     *
     * Required POST Parameters:
     *
     * - string $_POST['provider_id'] Provider record id.
     */
    public function ajax_get_google_calendars()
    {
        try
        {
            $this->load->library('google_sync');
            $this->load->model('providers_model');

            if ( ! $this->input->post('provider_id'))
            {
                throw new Exception('Provider id is required in order to fetch the google calendars.');
            }

            // Check if selected provider has sync enabled.
            $google_sync = $this->providers_model->get_setting('google_sync', $this->input->post('provider_id'));
            if ($google_sync)
            {
                $google_token = json_decode($this->providers_model->get_setting('google_token',
                    $this->input->post('provider_id')));
                $this->google_sync->refresh_token($google_token->refresh_token);
                $calendars = $this->google_sync->get_google_calendars();
                $this->output
                    ->set_content_type('application/json')
                    ->set_output(json_encode($calendars));
            }
            else
            {
                $this->output
                    ->set_content_type('application/json')
                    ->set_output(json_encode(AJAX_FAILURE));
            }
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }

    /**
     * Select a specific google calendar for a provider.
     *
     * All the appointments will be synced with this particular calendar.
     *
     * Required POST Parameters:
     *
     * - int $_POST['provider_id'] Provider record id.
     * - string $_POST['calendar_id'] Google calendar's id.
     */
    public function ajax_select_google_calendar()
    {
        try
        {
            if ($this->privileges[PRIV_USERS]['edit'] == FALSE
                && $this->session->userdata('user_id') != $this->input->post('provider_id'))
            {
                throw new Exception('You do not have the required privileges for this task.');
            }

            $this->load->model('providers_model');
            $result = $this->providers_model->set_setting('google_calendar', $this->input->post('calendar_id'),
                $this->input->post('provider_id'));

            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode($result ? AJAX_SUCCESS : AJAX_FAILURE));
        }
        catch (Exception $exc)
        {
            $this->output
                ->set_content_type('application/json')
                ->set_output(json_encode(['exceptions' => [exceptionToJavaScript($exc)]]));
        }
    }
}
