<?php

Yii::setPathOfAlias('Sabre', Yii::getPathOfAlias('application.components.Sabre'));

class iCalConverter
{

    public $iCal;
    public $Data;
    public $calendar;
    public $timezone;
    public $calendarEvent;
    public $start;
    public $end;
    public $DateTimezone;
    public $attributes = array();

    public function convertICalToEvent($file)
    {
        Log::d('Converting iCal to calendar event.', 'iCloudSync [iCalConverter]');
        $iCal = Sabre\VObject\Reader::read($file);

        if (!empty($iCal)) {
            $this->iCal = $iCal;
            $this->VEVENT = $this->iCal->VEVENT;

            $this->initializeICalData();

            $this->Data = $this->iCal->serialize();

            return true;
        }
        return false;
    }

    private function initializeICalData()
    {
        if ($this->setEventTime()) {
            $this->attributes['title'] = $this->getTitle();
            $this->attributes['description'] = $this->getDescription();
            $this->attributes['location'] = $this->getLocation();
            return;
        }
    }

    private function setEventTime()
    {
        $this->attributes['start'] = $this->getStartTime();
        $this->attributes['end'] = $this->getEndTime();

        if (!empty($this->attributes['start']) && !empty($this->attributes['end'])) {
            return true;
        }

        return false;
    }

    private function getStartTime()
    {
        if (is_object($this->VEVENT) && !empty($this->VEVENT->DTSTART)) {
            $DTSTART = $this->VEVENT->DTSTART->getParts();
            $startTime = $this->checkValue($DTSTART);

            if (!empty($startTime)) {
                return $this->getNewTimeFormat($startTime);
            }
        }
        return null;
    }

    private function getEndTime()
    {
        if (is_object($this->VEVENT) && !empty($this->VEVENT->DTEND)) {
            $DTEND = $this->VEVENT->DTEND->getParts();
            $endTime = $this->checkValue($DTEND);

            if (!empty($endTime)) {
                return $this->getNewTimeFormat($endTime);
            }
        }
        return null;
    }

    private function getNewTimeFormat($time)
    {
        $DateTimeParser = new \Sabre\VObject\DateTimeParser();
        $convertedTime = (array) $DateTimeParser->parse($time);

        if (isset($convertedTime['date'])) {
            $newTime = $convertedTime['date'];
            if (strpos('.', $newTime) !== false || strpos('.', $newTime) == 0) {
                $explodeTime = explode('.', $newTime);
            }

            $newServerDate = PhonalisaDate::newServerDate($explodeTime[0]);

            return $newServerDate;
        }
        return null;
    }

    private function getTitle()
    {
        $title = $this->VEVENT->SUMMARY->getParts();
        return $this->checkValue($title);
    }

    private function getDescription()
    {
        if (!empty($this->VEVENT->DESCRIPTION)) {
            $description = $this->VEVENT->DESCRIPTION->getParts();
            return $this->checkValue($description);
        }
        return null;
    }

    private function getLocation()
    {
        if (!empty($this->VEVENT->LOCATION)) {
            $location = $this->VEVENT->LOCATION->getParts();
            return $this->checkValue($location);
        }
        return null;
    }

    private function checkValue($value)
    {
        if (is_array($value)) {
            return $value[0];
        }
        return null;
    }

    public function convertEventToICal($calendarEvent, $iCloudData)
    {
        Log::d('Converting calendar Event to iCal.', 'iCloudSync [iCalConverter]');        
        if ($this->checkForCalendarEventData($calendarEvent)) {
            if (empty($iCloudData['id'])) {
                return $this->initializeNewICal($calendarEvent);
            } else {
                $data = json_decode($iCloudData->data);
                if ($data) {
                    $this->iCal = Sabre\VObject\Reader::read($data);

                    $iCal = $this->overwriteExistingVEvent($calendarEvent);

                    $this->Data = $this->iCal->serialize();
                    return $iCal;
                }
                Log::e("Could not get Data from iCloudData", '[iCalConverter]');
                return;
            }
        }
        Log::e("Invalid Calendar Event Data", '[iCalConverter]');
    }

    private function checkForCalendarEventData($calendarEvent)
    {
        if (isset($calendarEvent['start']) && isset($calendarEvent['end'])) {
            if (isset($calendarEvent['title']) && isset($calendarEvent['description']) && isset($calendarEvent['id'])) {
                return true;
            }
        }
        return false;
    }

    private function initializeNewICal($calendarEvent)
    {
        $this->iCal = new \Sabre\VObject\Component\VCalendar();

        $this->iCal->PRODID = '-//Apple Inc.//Mac OS X 10.10.5//EN';
        $this->iCal->CALSCALE = 'GREGORIAN';

        if ($this->createNewVTimezone()) {
            if ($this->createNewVEvent($calendarEvent)) {
                $this->Data = $this->iCal->serialize();
                return $this->Data;
            }
        }
        Log::e('Failed');
        return false;
    }

    private function createNewVEvent($calendarEvent)
    {
        $start = new DateTime($calendarEvent['start'], new DateTimeZone('GMT'));
        $end = new DateTime($calendarEvent['end'], new DateTimeZone('GMT'));

        $timezone = $this->getTimezone();

        if (isset($timezone['timezone'])) {
            $start = $start->setTimezone(new DateTimeZone($timezone['timezone']));
            $end = $end->setTimezone(new DateTimeZone($timezone['timezone']));

            $this->iCal->add('VEVENT', [
                'DTSTAMP' => $this->getGMTStamp(),
                'CREATED' => $this->getGMTStamp(),
                'DTEND' => $end,
                'SUMMARY' => $calendarEvent['title'],
                'DTSTART' => $start,
                'LOCATION' => $calendarEvent['location'],
                'SEQUENCE' => 0,
                'DESCRIPTION' => $calendarEvent['description'],
                'TRANSP' => 'OPAQUE',
                'UID' => $calendarEvent['id'],
            ]);

            $this->iCal->VEVENT->DTSTART['TZID'] = $timezone['timezone'];
            $this->iCal->VEVENT->DTEND['TZID'] = $timezone['timezone'];

            $this->setAllDay($start, $end);

            return true;
        }

        return false;
    }

    private function overwriteExistingVEvent($calendarEvent)
    {
        if ($this->setICalTime($calendarEvent)) {
            $this->iCal->VEVENT->SUMMARY = $calendarEvent['title'];
            $this->iCal->VEVENT->DESCRIPTION = $calendarEvent['description'];
            $this->iCal->VEVENT->LOCATION = $calendarEvent['location'];
            return $this->iCal->serialize();
        }
    }

    private function setICalTime($calendarEvent)
    {
        $start = new DateTime($calendarEvent['start'], new DateTimeZone('GMT'));
        $end = new DateTime($calendarEvent['end'], new DateTimeZone('GMT'));

        $timezone = $this->getTimezone();
        $DateTimezone = $timezone['timezone'];

        if (!empty($DateTimezone)) {
            $start = $start->setTimezone(new DateTimeZone($DateTimezone));
            $end = $end->setTimezone(new DateTimeZone($DateTimezone));

            $this->iCal->VEVENT->DTSTART = $start;
            $this->iCal->VEVENT->DTEND = $end;

            $this->iCal->VEVENT->DTSTART['TZID'] = $DateTimezone;
            $this->iCal->VEVENT->DTEND['TZID'] = $DateTimezone;

            $this->iCal->VEVENT->DTSTAMP = $this->getGMTStamp();

            $this->checkVTimezone();
            $this->setAllDay($start, $end);

            return true;
        }

        return false;
    }

    private function getTimezone()
    {
        $timezone = PhonalisaDate::userTimeZone();

        if (is_object($timezone)) {
            $timezone = get_object_vars($timezone);
        }

        return $timezone;
    }

    private function getGMTStamp()
    {
        return gmdate("Ymd\THis\Z");
    }

    private function checkVTimezone()
    {
        if (isset($this->iCal->VTIMEZONE)) {
            unset($this->iCal->VTIMEZONE);
            $this->createNewVTimezone();
        }
    }

    private function createNewVTimezone()
    {
        $timezone = $this->getTimezone();

        if (isset($timezone['timezone'])) {

            $vtimezone = $this->iCal->add('VTIMEZONE', [
                'TZID' => $timezone['timezone'],
            ]);

            $vtimezone->add('X-LIC-LOCATION', $timezone['timezone']);

            return true;
        }
        return false;
    }

    private function setAllDay($start, $end)
    {
        if (is_object($start) && is_object($end)) {
            $startArray = (array) $start;
            $endArray = (array) $end;
        }

        if (isset($startArray['date']) && isset($endArray['date'])) {
            $startDate = $startArray['date'];
            $endDate = $endArray['date'];
        }

        if (isset($startDate) && isset($endDate)) {
            if ($this->checkForDate($startDate) && $this->checkForDate($endDate)) {
                $this->iCal->VEVENT->DTSTART['VALUE'] = 'DATE';
                $this->iCal->VEVENT->DTEND['VALUE'] = 'DATE';
                return true;
            }
        }

        return false;
    }

    private function checkForDate($date)
    {
        if (strpos($date, ' ') !== false || strpos($date, ' ') == 0) {
            $explodeDate = explode(' ', $date);
        }
        if (isset($explodeDate[1]) && is_string($explodeDate[1])) {
            $explodeTime = explode('.', $explodeDate[1]);
            if ($explodeTime[0] == "00:00:00") {
                return true;
            }
        }
    }
}
