#!/usr/bin/python
# -*- coding: utf-8 -*-
### BEGIN LICENSE
# Copyright (C) 2009 Jono Bacon <jono@ubuntu.com>
# Copyright (C) 2010 Michael Budde <mbudde@gmail.com>
# Copyright (c) 2011 John S Gruber <johnsgruber@gmail.com>
#
#This program is free software: you can redistribute it and/or modify it
#under the terms of the GNU General Public License version 3, as published
#by the Free Software Foundation.
#
#This program is distributed in the hope that it will be useful, but
#WITHOUT ANY WARRANTY; without even the implied warranties of
#MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
#PURPOSE.  See the GNU General Public License for more details.
#
#You should have received a copy of the GNU General Public License along
#with this program.  If not, see <http://www.gnu.org/licenses/>.
### END LICENSE

import re
import vobject
import urllib2
import logging

import lernid.DateTime as dt
import io
import gio, glib
from datetime import timedelta

half_year = timedelta(days = 183)

class Session(object):

    PAST, NOW, FUTURE = range(3)

    def __init__(self, **kwargs):
        for k, v in kwargs.iteritems():
            if k in ('uid', 'title', 'description', 'instructors', 
               'helpers', 'local_start', 'local_end', 'slides', 
               'event', 'locale', 
               'question_token'):
                    setattr(self, '_'+k, v)

    @property
    def uid(self):
        return self._uid

    @property
    def title(self):
        return self._title

    @property
    def description(self):
        return self._description

    @property
    def instructors(self):
        return getattr(self, '_instructors', [])

    @property
    def helpers(self):
        return getattr(self, '_helpers', [])

    @property
    def locale(self):
        return getattr(self, '_locale', None)

    @property
    def question_token(self):
        return getattr(self, '_question_token', None)

    @property
    def start_local_date(self):
        # Translators: Local date representation
        return self._local_start.strftime(_('%d %B %Y'))

    @property
    def start_local_time(self):
        # Translators: Local time representation
        return self._local_start.strftime(_('%H:%M'))

    @property
    def end_local_time(self):
        # Translators: Local time representation
        return self._local_end.strftime(_('%H:%M'))

    @property
    def start_datetime(self):
        return self._local_start

    @property
    def end_datetime(self):
        return self._local_end

    @property
    def slides(self):
        return getattr(self, '_slides', None)

    @property
    def event(self):
        return getattr(self, '_event', None)

    @property
    def state(self):
        now, start, end = dt.now_local(), self.start_datetime, self.end_datetime
        if now < start:
            return self.FUTURE
        if start < now < end:
            return self.NOW
        if now > end:
            return self.PAST

def start_read_ical(url, when_done_cb, cancellable, data):
    gfile = gio.File(uri=url)
    r = gfile.load_contents_async(when_done_cb, cancellable=cancellable,
        user_data=data)

def read_ical(gfile,result,userdata):
    event, old_ical = userdata
    default_cal_error = (
u"""BEGIN:VCALENDAR
BEGIN:VEVENT
DTSTART:20100101T000000Z
DTEND:20100101T000000Z
DESCRIPTION:CalendarError
SUMMARY: """ +   _('Unable to load calendar %s\n') + 
u"""END:VEVENT
END:VCALENDAR""")   % event.icalurl

    try:
        
        ical = unicode(gfile.load_contents_finish(result)[0])
        icf = io.StringIO(ical)
        ical_output = io.StringIO()
        # DTSTAMP is changed for each calendar entry each time, but isn't
        # significant--remove them
        for i in icf.readlines():
            if i[0:8] != u'DTSTAMP:':
                ical_output.write(i)
        ical = ical_output.getvalue()
        icf.close()
        ical_output.close()
    except gio.Error, information:
        if information.code == gio.ERROR_CANCELLED:
            logging.debug('Ignoring the completion of a cancelled cal. load')
            return ''
        logging.error('Unable to open calendar %s' % gfile.get_uri())
        logging.error(information)
        if old_ical:
            ical = old_ical
        else:
            ical = default_cal_error
    except glib.GError, information:
        logging.error('Unable to open calendar %s' % gfile.get_uri())
        logging.error(information)
        if old_ical:
            ical = old_ical
        else:
            ical = default_cal_error
    return ical


def parse_ical(event, ical):
    default_cal_error = (
u"""BEGIN:VCALENDAR
BEGIN:VEVENT
DTSTART:20100101T000000Z
DTEND:20100101T000000Z
DESCRIPTION:CalendarError
SUMMARY: """ + _('Unable to parse calendar %s\n') + 
u"""END:VEVENT
END:VCALENDAR""") % event.icalurl

    try:
        cal = vobject.readOne(io.StringIO(ical))
    except:
        logging.critical('Error parsing calendar at %s' % event.icalurl)
        cal = vobject.readOne(io.StringIO(default_cal_error))

    sessions = []

    eventstart_local = dt.parse_time(event.eventstart, '%Y-%m-%d %H:%M:%S')
    eventend_local = dt.parse_time(event.eventend, '%Y-%m-%d %H:%M:%S')

    for session in cal.vevent_list:
        session_data = {}
        if not hasattr(session, "dtstart") or not hasattr(session, "dtend"):
            logging.debug("Missing or invalid time for event")
            local_start = eventend_local
            local_end = eventend_local
        else:
            local_start = dt.as_local(session.dtstart.value)
            local_end = dt.as_local(session.dtend.value)
            if hasattr(session, "description"):
                if session.description.value == "CalendarError":
                    local_start = eventend_local
                    local_end = eventend_local
                session_data = parse_ical_description(session.description.value)
        if hasattr(session, "summary"):
            summary = session.summary.value
        else:
            summary = _('Missing Session Name')
        if hasattr(session, "uid"):
            uid = session.uid
        else:
            uid = 0
        if eventstart_local <= local_start <= eventend_local:
            if local_start > dt.now_local() - half_year:
                sessions.append(Session(
                    title = summary,
                    local_start = local_start,
                    local_end = local_end,
                    uid = uid,
                    **session_data))

    # reverse the list to get the events in chronological order
    sessions.sort(lambda x,y: cmp(x.start_datetime, y.start_datetime))
    return sessions

def parse_ical_description(desc):
    session = {}
    for line in desc.splitlines():
        items = [s.strip() for s in line.split(None, 1)]
        if len(items) != 2:
            continue
        key, value = items
        key = key[:-1].lower()
        if key in ('description', 'slides', 'event', 'locale', 'question_token'):
            session[str(key)] = value
        elif key == 'instructor' or key == 'instructors':
            instructors = [s.strip() for s in value.split(',')]
            session['instructors'] = instructors
        elif key == 'helper' or key == 'helpers':
            helpers = [s.strip() for s in value.split(',')]
            session['helpers'] = helpers
    return session
