#This file is part of Tryton.  The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
from __future__ import with_statement
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
import email.utils
import logging
from trytond.model import ModelSQL, ModelView, fields
from trytond.tools import get_smtp_server
from trytond.transaction import Transaction


class Event(ModelSQL, ModelView):
    _name = 'calendar.event'

    organizer_schedule_status = fields.Selection([
            ('', ''),
            ('1.0', '1.0'),
            ('1.1', '1.1'),
            ('1.2', '1.2'),
            ('3.7', '3.7'),
            ('3.8', '3.8'),
            ('5.1', '5.1'),
            ('5.2', '5.2'),
            ('5.3', '5.3'),
            ], 'Schedule Status')
    organizer_schedule_agent = fields.Selection([
            ('', ''),
            ('NONE', 'None'),
            ('SERVER', 'Server'),
            ('CLIENT', 'Client'),
            ], 'Schedule Agent')

    def default_schedule_agent(self):
        return 'SERVER'

    def __init__(self):
        super(Event, self).__init__()
        self._error_messages.update({
            'new_subject': 'Invitation: %s @ %s',
            'new_body': 'You have been invited to the following event.\n\n',
            'update_subject': 'Updated Invitation: %s @ %s',
            'update_body': 'This event has been changed.\n\n',
            'cancel_subject': 'Cancelled Event: %s @ %s',
            'cancel_body': 'This event has been canceled.\n\n',
            'no_subject': "(No Subject)",
            'separator': ':',
            'bullet': '    * ',
            'when': 'When',
            })

    def ical2values(self, event_id, ical, calendar_id, vevent=None):
        res = super(Event, self).ical2values(event_id, ical, calendar_id,
                vevent=vevent)

        if not vevent:
            vevent = ical.vevent

        if not hasattr(vevent, 'organizer'):
            return res

        for key in ('status', 'agent'):
            field = 'organizer_schedule_' + key
            param = 'SCHEDULE-' + key.upper()
            if param in vevent.organizer.params and vevent.organizer[param] \
                    in dict(self[field].selection):
                res[field] = vevent.organizer[param]

        return res

    def event2ical(self, event):
        """
        Override default event2ical to add schedule-status and
        schedule-agent properties

        If key skip_schedule_agent is present and associated to True, will
        prevent the schedule-status to be add to the organiser property. This
        is needed when one want to generate an ical that will be used for
        scheduling message.
        """

        ical = super(Event, self).event2ical(event)
        if isinstance(event, (int, long)):
            event = self.browse(event)

        vevent = ical.vevent

        if event.organizer_schedule_status:
            if not hasattr(vevent, 'organizer'):
                vevent.add('organizer')
            vevent.organizer.params['SCHEDULE-STATUS'] = \
                (event.organizer_schedule_status,)

        if Transaction().context.get('skip_schedule_agent'):
            return ical

        if event.organizer_schedule_agent:
            if not hasattr(vevent, 'organizer'):
                vevent.add('organizer')
            vevent.organizer.params['SCHEDULE-AGENT'] = \
                event.organizer_schedule_agent

        return ical

    def subject_body(self, type, event, owner):
        lang_obj = self.pool.get('ir.lang')

        if not (event and owner):
            return "", ""
        lang = owner.language
        if not lang:
            lang_ids = lang_obj.search([
                ('code', '=', 'en_US'),
                ], limit=1)
            lang = lang_obj.browse(lang_ids[0])

        with Transaction().set_context(language=lang.code):
            summary = event.summary
            if not summary:
                summary = self.raise_user_error('no_subject',
                        raise_exception=False)

        date = lang_obj.strftime(event.dtstart, lang.code, lang.date)
        if not event.all_day:
            date += ' ' + lang_obj.strftime(event.dtstart, lang.code, '%H:%M')
            if event.dtend:
                date += ' -'
                if event.dtstart.date() != event.dtend.date():
                    date += ' ' + lang_obj.strftime(event.dtend, lang.code,
                            lang.date)
                date += ' ' + lang_obj.strftime(event.dtend, lang.code,
                        '%H:%M')
        else:
            if event.dtend and event.dtstart.date() != event.dtend.date():
                date += ' - ' + lang_obj.strftime(event.dtend, lang.code,
                        lang.date)
        if event.timezone:
            date += ' ' + event.timezone

        with Transaction().set_context(language=lang.code):
            subject = self.raise_user_error(type + '_subject',
                    (summary, date), raise_exception=False)
            body = self.raise_user_error(type + '_body', (summary, ),
                    raise_exception=False)
            separator = self.raise_user_error('separator',
                    raise_exception=False)
            bullet = self.raise_user_error('bullet', raise_exception=False)

        fields_names = ['summary', 'dtstart', 'location', 'attendees']
        if type == 'cancel':
            fields_names.remove('attendees')
        with Transaction().set_context(language=lang.code):
            fields = self.fields_get(fields_names=fields_names)
            fields['dtstart']['string'] = self.raise_user_error('when',
                    raise_exception=False)
        for field in fields_names:
            if field == 'attendees':
                if lang.direction == 'ltr':
                    body += fields['attendees']['string'] + separator + '\n'
                    body += bullet + owner.email + '\n'
                    for attendee in event.attendees:
                        body += bullet + attendee.email + '\n'
                else:
                    body += separator + fields['attendees']['string'] + '\n'
                    body += owner.email + bullet + '\n'
                    for attendee in event.attendees:
                        body += attendee.email + bullet + '\n'
            elif event[field]:
                if field == 'summary':
                    value = summary
                elif field == 'dtstart':
                    value = date
                elif field == 'location':
                    value = event.location.name
                else:
                    value = event[field]
                if lang.direction == 'ltr':
                    body += fields[field]['string'] + separator + ' ' \
                            + value + '\n'
                else:
                    body += value + ' ' + separator \
                            + fields[field]['string'] + '\n'
        return subject, body

    def create_msg(self, from_addr, to_addrs, subject, body, ical=None):

        if not to_addrs:
            return None

        msg = MIMEMultipart()
        msg['To'] = ', '.join(to_addrs)
        msg['From'] = from_addr
        msg['Subject'] = subject

        inner = MIMEMultipart('alternative')

        msg_body = MIMEBase('text', 'plain')
        msg_body.set_payload(body.encode('UTF-8'), 'UTF-8')
        inner.attach(msg_body)

        attachment = MIMEBase('text', 'calendar')
        attachment.set_payload(ical.serialize())
        attachment.add_header('Content-Transfer-Encoding', 'quoted-printable',
                charset='UTF-8',
                method=ical.method.value.lower())
        inner.attach(attachment)

        msg.attach(inner)

        attachment = MIMEBase('application', 'ics')
        attachment.set_payload(ical.serialize(), 'UTF-8')
        attachment.add_header('Content-Disposition', 'attachment',
                filename='invite.ics', name='invite.ics')

        msg.attach(attachment)

        return msg

    def send_msg(self, from_addr, to_addrs, msg, type, event_id):
        '''
        Send message

        :param from_addr: a from-address string
        :param to_addrs: a list of to-address strings
        :param msg: a message string
        :param type: the type (new, update, cancel)
        :param event_id: the id of the calendar.event
        :return: list of email addresses sent
        '''
        user_obj = self.pool.get('res.user')

        if not to_addrs:
            return to_addrs
        to_addrs = list(set(to_addrs))

        user_ids = user_obj.search([
            ('email', 'in', to_addrs),
            ])
        for user in user_obj.browse(user_ids):
            if not user['calendar_email_notification_' + type]:
                to_addrs.remove(user.email)

        success = False
        try:
            server = get_smtp_server()
            server.sendmail(from_addr, to_addrs, msg.as_string())
            server.quit()
            success = to_addrs
        except Exception:
            logging.getLogger('calendar_scheduling').error(
                    'Unable to deliver scheduling mail for event %s' % event_id)
        return success

    @staticmethod
    def attendees_to_notify(event):
        if not event.calendar.owner:
            return [], None
        attendees = event.attendees
        organizer = event.organizer
        owner = event.calendar.owner
        if event.parent:
            if not attendees:
                attendees = event.parent.attendees
                organizer = event.parent.organizer
                owner = event.parent.calendar.owner
            elif not organizer:
                organizer = event.parent.organizer
                owner = event.parent.calendar.owner

        if organizer != owner.email:
            return [], None

        to_notify = []
        for attendee in attendees:
            if attendee.email == organizer:
                continue
            if attendee.schedule_agent and\
                    attendee.schedule_agent != 'SERVER':
                continue
            to_notify.append(attendee)

        return to_notify, owner

    def create(self, values):
        attendee_obj = self.pool.get('calendar.event.attendee')
        res = super(Event, self).create(values)

        if Transaction().user == 0:
            # user is 0 means create is triggered by another one
            return res

        event = self.browse(res)

        to_notify, owner = self.attendees_to_notify(event)
        if not to_notify:
            return res

        with Transaction().set_context(skip_schedule_agent=True):
            ical = self.event2ical(event)
        ical.add('method')
        ical.method.value = 'REQUEST'

        attendee_emails = [a.email for a in to_notify]

        subject, body = self.subject_body('new', event, owner)
        msg = self.create_msg(owner.email, attendee_emails, subject, body,
                ical)
        sent = self.send_msg(owner.email, attendee_emails, msg, 'new',
                event.id)

        vals = {'status': 'needs-action'}
        if sent:
            vals['schedule_status'] = '1.1' #successfully sent
        else:
            vals['schedule_status'] = '5.1' #could not complete delivery
        attendee_obj.write([a.id for a in to_notify], vals)

        return res

    def write(self, ids, values):
        attendee_obj = self.pool.get('calendar.event.attendee')

        if Transaction().user == 0:
            # user is 0 means write is triggered by another one
            return super(Event, self).write(ids, values)

        if isinstance(ids, (int, long)):
            ids = [ids]

        if not values or not ids:
            return super(Event, self).write(ids, values)

        event_edited = False
        for k in values:
            if k != 'attendees':
                event_edited = True
                break

        # store old attendee info
        events = self.browse(ids)
        event2former_emails = {}
        former_organiser_mail = {}
        former_organiser_lang = {}
        for event in events:
            to_notify, owner = self.attendees_to_notify(event)
            event2former_emails[event.id] = [a.email for a in to_notify]
            former_organiser_mail[event.id] = owner and owner.email
            former_organiser_lang[event.id] = owner and owner.language \
                and owner.language.code

        res = super(Event, self).write(ids, values)

        events = self.browse(ids)

        for event in events:
            current_attendees, owner = self.attendees_to_notify(event)
            owner_email = owner and owner.email
            current_emails = [a.email for a in current_attendees]
            former_emails = event2former_emails.get(event.id, [])
            missing_mails = filter(lambda mail: mail not in current_emails,
                    former_emails)

            if missing_mails:
                with Transaction().set_context(skip_schedule_agent=True):
                    ical = self.event2ical(event)
                ical.add('method')
                ical.method.value = 'CANCEL'

                subject, body = self.subject_body('cancel', event, owner)
                msg = self.create_msg(former_organiser_mail[event.id],
                        missing_mails, subject, body, ical)
                sent = self.send_msg(former_organiser_mail[event.id],
                        missing_mails, msg, 'cancel', event.id)

            new_attendees = filter(lambda a: a.email not in former_emails,
                current_attendees)
            old_attendees = filter(lambda a: a.email in former_emails,
                current_attendees)
            with Transaction().set_context(skip_schedule_agent=True):
                ical = self.event2ical(event)
            if not hasattr(ical, 'method'):
                ical.add('method')

            sent_succes = []
            sent_fail = []
            if event_edited:
                if event.status == 'cancelled':
                    ical.method.value = 'CANCEL'
                    #send cancel to old attendee
                    subject, body = self.subject_body('cancel', event, owner)
                    msg = self.create_msg(owner_email,
                            [a.email for a in old_attendees],
                            subject, body, ical)
                    sent = self.send_msg(owner_email,
                            [a.email for a in old_attendees],
                            msg, 'cancel', event.id)
                    if sent:
                        sent_succes += old_attendees
                    else:
                        sent_fail += old_attendees

                else:
                    ical.method.value = 'REQUEST'
                    #send update to old attendees
                    subject, body = self.subject_body('update', event, owner)
                    msg = self.create_msg(owner_email,
                            [a.email for a in old_attendees],
                            subject, body, ical)
                    sent = self.send_msg(owner_email,
                            [a.email for a in old_attendees],
                            msg, 'update', event.id)
                    if sent:
                        sent_succes += old_attendees
                    else:
                        sent_fail += old_attendees
                    #send new to new attendees
                    subject, body = self.subject_body('new', event, owner)
                    msg = self.create_msg(owner_email,
                            [a.email for a in new_attendees],
                            subject, body, ical)
                    sent = self.send_msg(owner_email,
                            [a.email for a in new_attendees],
                            msg, 'new', event.id)
                    if sent:
                        sent_succes += new_attendees
                    else:
                        sent_fail += new_attendees

            else:
                if event.status != 'cancelled':
                    ical.method.value = 'REQUEST'
                    #send new to new attendees
                    subject, body = self.subject_body('new', event, owner)
                    msg = self.create_msg(owner_email,
                            [a.email for a in new_attendees],
                            subject, body, ical)
                    sent = self.send_msg(owner_email,
                            [a.email for a in new_attendees],
                            msg, 'new', event.id)
                    if sent:
                        sent_succes += new_attendees
                    else:
                        sent_fail += new_attendees

                vals = {'status': 'needs-action'}
                vals['schedule_status'] = '1.1' #successfully sent
                attendee_obj.write([a.id for a in sent_succes], vals)
                vals['schedule_status'] = '5.1' #could not complete delivery
                attendee_obj.write([a.id for a in sent_fail], vals)
        return res

    def delete(self, ids):
        if Transaction().user == 0:
            # user is 0 means the deletion is triggered by another one
            return super(Event, self).delete(ids)

        if isinstance(ids, (int, long)):
            ids = [ids]

        events = self.browse(ids)
        send_list = []
        for event in events:
            if event.status == 'cancelled':
                continue
            to_notify, owner = self.attendees_to_notify(event)
            if not to_notify:
                continue

            with Transaction().set_context(skip_schedule_agent=True):
                ical = self.event2ical(event)
            ical.add('method')
            ical.method.value = 'CANCEL'

            attendee_emails = [a.email for a in to_notify]
            subject, body = self.subject_body('cancel', event, owner)
            msg = self.create_msg(owner.email, attendee_emails, subject, body,
                    ical)

            send_list.append((owner.email, attendee_emails, msg, event.id,))

        res = super(Event, self).delete(ids)
        for args in send_list:
            owner_email, attendee_emails, msg, event_id = args
            self.send_msg(owner_email, attendee_emails, msg, 'cancel',
                    event_id)
        return res

Event()


class Attendee(ModelSQL, ModelView):
    _name = 'calendar.attendee'

    schedule_status = fields.Selection([
            ('', ''),
            ('1.0', '1.0'),
            ('1.1', '1.1'),
            ('1.2', '1.2'),
            ('3.7', '3.7'),
            ('3.8', '3.8'),
            ('5.1', '5.1'),
            ('5.2', '5.2'),
            ('5.3', '5.3'),
            ], 'Schedule Status')
    schedule_agent = fields.Selection([
            ('', ''),
            ('NONE', 'None'),
            ('SERVER', 'Server'),
            ('CLIENT', 'Client'),
            ], 'Schedule Agent')

    def default_schedule_agent(self):
        return 'SERVER'

    def attendee2values(self, attendee):
        res = super(Attendee, self).attendee2values(attendee)
        if hasattr(attendee, 'schedule_status'):
            if attendee.schedule_status in dict(self.schedule_status.selection):
                res['schedule_status'] = attendee.schedule_status
        if hasattr(attendee, 'schedule_agent'):
            if attendee.schedule_agent in dict(self.schedule_agent.selection):
                res['schedule_agent'] = attendee.schedule_agent
        return res

    def attendee2attendee(self, attendee):
        res = super(Attendee, self).attendee2attendee(attendee)

        if attendee.schedule_status:
            if hasattr(res, 'schedule_status_param'):
                if res.schedule_status_param in dict(self.schedule_status.selection):
                    res.schedule_status_param = attendee.schedule_status
            else:
                res.schedule_status_param = attendee.schedule_status
        elif hasattr(res, 'schedule_status_param'):
            if res.schedule_status_param in dict(self.schedule_status.selection):
                del res.schedule_status_param

        if Transaction().context.get('skip_schedule_agent'):
            return res

        if attendee.schedule_agent:
            if hasattr(res, 'schedule_agent_param'):
                if res.schedule_agent_param in dict(self.schedule_agent.selection):
                    res.schedule_agent_param = attendee.schedule_agent
            else:
                res.schedule_agent_param = attendee.schedule_agent
        elif hasattr(res, 'schedule_agent_param'):
            if res.schedule_agent_param in dict(self.schedule_agent.selection):
                del res.schedule_agent_param

        return res

Attendee()


class EventAttendee(ModelSQL, ModelView):
    _name = 'calendar.event.attendee'

    def __init__(self):
        super(EventAttendee, self).__init__()
        self._error_messages.update({
                'subject': '%s: %s @ %s',
                'body': '%s (%s) changed his/her participation status to: %s\n\n',
                'accepted_body': '%s (%s) has accepted this invitation:\n\n',
                'declined_body': '%s (%s) has declined this invitattion:\n\n',
                'no_subject': "(No Subject)",
                'separator': ':',
                'bullet': '    * ',
                'when': 'When',
                })

    def subject_body(self, status, event, owner):
        lang_obj = self.pool.get('ir.lang')
        event_obj = self.pool.get('calendar.event')

        if not (event and owner):
            return "", ""
        lang = owner.language
        if not lang:
            lang_ids = lang_obj.search([
                ('code', '=', 'en_US'),
                ], limit=1)
            lang = lang_obj.browse(lang_ids[0])

        summary = event.summary
        if not summary:
            with Transaction().set_context(language=lang.code):
                summary = self.raise_user_error('no_subject',
                        raise_exception=False)

        date = lang_obj.strftime(event.dtstart, lang.code, lang.date)
        if not event.all_day:
            date += ' ' + lang_obj.strftime(event.dtstart, lang.code, '%H:%M')
            if event.dtend:
                date += ' -'
                if event.dtstart.date() != event.dtend.date():
                    date += ' ' + lang_obj.strftime(event.dtend, lang.code,
                            lang.date)
                date += ' ' + lang_obj.strftime(event.dtend, lang.code,
                        '%H:%M')
        else:
            if event.dtend and event.dtstart.date() != event.dtend.date():
                date += ' - ' + lang_obj.strftime(event.dtend, lang.code,
                        lang.date)
        if event.timezone:
            date += ' ' + event.timezone

        status_string = status
        fields_names = ['status']
        with Transaction().set_context(language=lang.code):
            fields = self.fields_get(fields_names=fields_names)
        for k,v in  fields['status']['selection']:
            if k == status:
                status_string = v

        with Transaction().set_context(language=lang.code):
            subject = self.raise_user_error('subject', (status, summary, date),
                    raise_exception=False)

            if status + '_body' in self._error_messages:
                body = self.raise_user_error(status + '_body',
                        (owner.name, owner.email), raise_exception=False)
            else:
                body = self.raise_user_error('body',
                        (owner.name, owner.email, status_string),
                        raise_exception=False)

            separator = self.raise_user_error('separator',
                    raise_exception=False)
            bullet = self.raise_user_error('bullet', raise_exception=False)

            fields_names = ['summary', 'dtstart', 'location', 'attendees']
            fields = event_obj.fields_get(fields_names=fields_names)
            fields['dtstart']['string'] = self.raise_user_error('when',
                    raise_exception=False)
        for field in fields_names:
            if field == 'attendees':
                organizer = event.organizer or event.parent.organizer
                if lang.direction == 'ltr':
                    body += fields['attendees']['string'] + separator + '\n'
                    if organizer:
                        body += bullet + organizer + '\n'
                    for attendee in event.attendees:
                        body += bullet + attendee.email + '\n'
                else:
                    body += separator + fields['attendees']['string'] + '\n'
                    if organizer:
                        body += owner.email + bullet + '\n'
                    for attendee in event.attendees:
                        body += attendee.email + bullet + '\n'
            elif event[field]:
                if field == 'summary':
                    value = summary
                elif field == 'dtstart':
                    value = date
                elif field == 'location':
                    value = event.location.name
                else:
                    value = event[field]
                if lang.direction == 'ltr':
                    body += fields[field]['string'] + separator + ' ' \
                            + value + '\n'
                else:
                    body += value + ' ' + separator \
                            + fields[field]['string'] + '\n'
        return subject, body

    def create_msg(self, from_addr, to_addr, subject, body, ical=None):

        if not to_addr:
            return None

        msg = MIMEMultipart()
        msg['To'] = to_addr
        msg['From'] = from_addr
        msg['Subject'] = subject

        inner = MIMEMultipart('alternative')

        msg_body = MIMEBase('text', 'plain')
        msg_body.set_payload(body.encode('UTF-8'), 'UTF-8')
        inner.attach(msg_body)

        attachment = MIMEBase('text', 'calendar')
        attachment.set_payload(ical.serialize())
        attachment.add_header('Content-Transfer-Encoding', 'quoted-printable',
                charset='UTF-8',
                method=ical.method.value.lower())
        inner.attach(attachment)

        msg.attach(inner)
        attachment = MIMEBase('application', 'ics')
        attachment.set_payload(ical.serialize(), 'UTF-8')
        attachment.add_header('Content-Disposition', 'attachment',
                filename='invite.ics', name='invite.ics')

        msg.attach(attachment)

        return msg

    def send_msg(self, from_addr, to_addr, msg, event):
        '''
        Send message

        :param from_addr: a from-address string
        :param to_addr: recipient address
        :param msg: a message string
        :param event_id: the id of the calendar.event
        :return: a boolean (True if the mail has been sent)
        '''
        success = False
        try:
            server = get_smtp_server()
            server.sendmail(from_addr, to_addr, msg.as_string())
            server.quit()
            success = True
        except Exception:
            logging.getLogger('calendar_scheduling').error(
                    'Unable to deliver reply mail for event %s' % event_id)
        return success

    @staticmethod
    def organiser_to_notify(attendee):
        event = attendee.event
        organizer = event.organizer or event.parent.organizer
        if not organizer:
            return None
        if event.organizer_schedule_agent \
                and event.organizer_schedule_agent != 'SERVER':
            return None
        if organizer == event.calendar.owner.email:
            return None
        if attendee.email != event.calendar.owner.email:
            return None

        return organizer

    def write(self, ids, values):
        event_obj = self.pool.get('calendar.event')

        if Transaction().user == 0:
            # user is 0 means write is triggered by another one
            return super(EventAttendee, self).write(ids, values)

        if 'status' not in values:
            return super(EventAttendee, self).write(ids, values)

        if isinstance(ids, (int, long)):
            ids = [ids]

        att2status = {}
        attendees = self.browse(ids)
        for attendee in attendees:
            att2status[attendee.id] = attendee.status

        res = super(EventAttendee, self).write(ids, values)

        attendees = self.browse(ids)
        for attendee in attendees:
            owner = attendee.event.calendar.owner
            if not owner.calendar_email_notification_partstat:
                continue
            organizer = self.organiser_to_notify(attendee)
            if not organizer:
                continue

            old, new = (att2status.get(attendee.id) or 'needs-action',
                        attendee.status or 'needs-action')

            if old == new:
                continue

            with Transaction().set_context(skip_schedule_agent=True):
                ical = event_obj.event2ical(attendee.event)
            if not hasattr(ical, 'method'):
                ical.add('method')
            ical.method.value = 'REPLY'

            subject, body = self.subject_body(new, attendee.event, owner)
            msg = self.create_msg(owner.email, organizer, subject, body, ical)

            sent = self.send_msg(owner.email, organizer, msg,
                    attendee.event.id)

            vals = {'organizer_schedule_status': sent and '1.1' or '5.1'}
            event_obj.write(attendee.event.id, vals)

        return res

    def delete(self, ids):
        event_obj = self.pool.get('calendar.event')

        if Transaction().user == 0:
            # user is 0 means the deletion is triggered by another one
            return super(EventAttendee, self).delete(ids)

        if isinstance(ids, (int, long)):
            ids = [ids]
        attendees = self.browse(ids)
        send_list = []
        for attendee in attendees:
            owner = attendee.event.calendar.owner

            if attendee.status == 'declined':
                continue
            if not owner.calendar_email_notification_partstat:
                continue
            organizer = self.organiser_to_notify(attendee)
            if not organizer:
                continue

            with Transaction().set_context(skip_schedule_agent=True):
                ical = event_obj.event2ical(attendee.event)
            if not hasattr(ical, 'method'):
                ical.add('method')
            ical.method.value = 'REPLY'

            subject, body = self.subject_body('declined', attendee.event,
                    owner)
            msg = self.create_msg(owner.email, organizer, subject, body, ical)

            send_list.append((owner.email, organizer, msg, attendee.event.id))


        res = super(EventAttendee, self).delete(ids)
        for args in send_list:
            owner_email, organizer, msg, event_id = args
            sent = self.send_msg(owner_email, organizer, msg, event_id)
            vals = {'organizer_schedule_status': sent and '1.1' or '5.1'}
            event_obj.write(event_id, vals)

        return res

    def create(self, values):
        event_obj = self.pool.get('calendar.event')

        res_id = super(EventAttendee, self).create(values)
        if Transaction().user == 0:
            # user is 0 means create is triggered by another one
            return res_id

        attendee = self.browse(res_id)

        owner = attendee.event.calendar.owner

        if (not attendee.status) or attendee.status in ('', 'needs-action'):
            return res_id
        if not owner.calendar_email_notification_partstat:
            return res_id
        organizer = self.organiser_to_notify(attendee)
        if not organizer:
            return res_id

        with Transaction().set_context(skip_schedule_agent=True):
            ical = event_obj.event2ical(attendee.event)
        if not hasattr(ical, 'method'):
            ical.add('method')
        ical.method.value = 'REPLY'

        subject, body = self.subject_body(attendee.status, attendee.event,
                owner)
        msg = self.create_msg(owner.email, organizer, subject, body, ical)

        sent = self.send_msg(owner.email, organizer, msg, attendee.event.id)

        vals = {'organizer_schedule_status': sent and '1.1' or '5.1'}
        event_obj.write(attendee.event.id, vals)

        return res_id

EventAttendee()
