# coding: utf-8
from icalendar import Calendar
from icalendar import Event
from icalendar import Parameters
from icalendar import vCalAddress
from icalendar.tests import unittest

import icalendar
import re


class TestPropertyParams(unittest.TestCase):

    def test_property_params(self):
        # Property parameters with values containing a COLON character, a
        # SEMICOLON character or a COMMA character MUST be placed in quoted
        # text.
        cal_address = vCalAddress('mailto:john.doe@example.org')
        cal_address.params["CN"] = "Doe, John"
        ical = Calendar()
        ical.add('organizer', cal_address)

        ical_str = Calendar.to_ical(ical)
        exp_str = b"""BEGIN:VCALENDAR\r\nORGANIZER;CN="Doe, John":"""\
                  b"""mailto:john.doe@example.org\r\nEND:VCALENDAR\r\n"""

        self.assertEqual(ical_str, exp_str)

        # other way around: ensure the property parameters can be restored from
        # an icalendar string.
        ical2 = Calendar.from_ical(ical_str)
        self.assertEqual(ical2.get('ORGANIZER').params.get('CN'), 'Doe, John')

    def test_unicode_param(self):
        cal_address = vCalAddress('mailto:john.doe@example.org')
        cal_address.params["CN"] = "Джон Доу"
        vevent = Event()
        vevent['ORGANIZER'] = cal_address
        self.assertEqual(
            vevent.to_ical().decode('utf-8'),
            u'BEGIN:VEVENT\r\n'
            u'ORGANIZER;CN="Джон Доу":mailto:john.doe@example.org\r\n'
            u'END:VEVENT\r\n'
        )

        self.assertEqual(vevent['ORGANIZER'].params['CN'],
                         'Джон Доу')

    def test_quoting(self):
        # not double-quoted
        self._test_quoting(u"Aramis", u'Aramis')
        # if a space is present - enclose in double quotes
        self._test_quoting(u"Aramis Alameda", u'"Aramis Alameda"')
        # a single quote in parameter value - double quote the value
        self._test_quoting(u"Aramis d'Alameda", u'"Aramis d\'Alameda"')
        # double quote is replaced with single quote
        self._test_quoting(u"Aramis d\"Alameda", u'"Aramis d\'Alameda"')
        self._test_quoting(u"Арамис д'Аламеда", u'"Арамис д\'Аламеда"')

    def _test_quoting(self, cn_param, cn_quoted):
        """
        @param cn_param: CN parameter value to test for quoting
        @param cn_quoted: expected quoted parameter in icalendar format
        """
        vevent = Event()
        attendee = vCalAddress('test@mail.com')
        attendee.params['CN'] = cn_param
        vevent.add('ATTENDEE', attendee)
        self.assertEqual(
            vevent.to_ical(),
            b'BEGIN:VEVENT\r\nATTENDEE;CN=' + cn_quoted.encode('utf-8') +
            b':test@mail.com\r\nEND:VEVENT\r\n'
        )

    def test_escaping(self):
        # verify that escaped non safe chars are decoded correctly
        NON_SAFE_CHARS = u',\\;:'
        for char in NON_SAFE_CHARS:
            cn_escaped = u"Society\\%s 2014" % char
            cn_decoded = u"Society%s 2014" % char
            vevent = Event.from_ical(
                u'BEGIN:VEVENT\r\n'
                u'ORGANIZER;CN=%s:that\r\n'
                u'END:VEVENT\r\n' % cn_escaped
            )
            self.assertEqual(vevent['ORGANIZER'].params['CN'], cn_decoded)

        vevent = Event.from_ical(
            'BEGIN:VEVENT\r\n'
            'ORGANIZER;CN=that\\, that\\; %th%%at%\\\\ that\\:'
            ':это\\, то\\; that\\\\ %th%%at%\\:\r\n'
            'END:VEVENT\r\n'
        )
        self.assertEqual(
            vevent['ORGANIZER'].params['CN'],
            r'that, that; %th%%at%\ that:'
        )
        self.assertEqual(
            vevent['ORGANIZER'].to_ical().decode('utf-8'),
            u'это, то; that\\ %th%%at%:'
        )

    def test_parameters_class(self):

        # Simple parameter:value pair
        p = Parameters(parameter1='Value1')
        self.assertEqual(p.to_ical(), b'PARAMETER1=Value1')

        # keys are converted to upper
        self.assertEqual(list(p.keys()), ['PARAMETER1'])

        # Parameters are case insensitive
        self.assertEqual(p['parameter1'], 'Value1')
        self.assertEqual(p['PARAMETER1'], 'Value1')

        # Parameter with list of values must be seperated by comma
        p = Parameters({'parameter1': ['Value1', 'Value2']})
        self.assertEqual(p.to_ical(), b'PARAMETER1=Value1,Value2')

        # Multiple parameters must be seperated by a semicolon
        p = Parameters({'RSVP': 'TRUE', 'ROLE': 'REQ-PARTICIPANT'})
        self.assertEqual(p.to_ical(), b'ROLE=REQ-PARTICIPANT;RSVP=TRUE')

        # Parameter values containing ',;:' must be double quoted
        p = Parameters({'ALTREP': 'http://www.wiz.org'})
        self.assertEqual(p.to_ical(), b'ALTREP="http://www.wiz.org"')

        # list items must be quoted seperately
        p = Parameters({'MEMBER': ['MAILTO:projectA@host.com',
                                   'MAILTO:projectB@host.com']})
        self.assertEqual(
            p.to_ical(),
            b'MEMBER="MAILTO:projectA@host.com","MAILTO:projectB@host.com"'
        )

        # Now the whole sheebang
        p = Parameters({'parameter1': 'Value1',
                        'parameter2': ['Value2', 'Value3'],
                        'ALTREP': ['http://www.wiz.org', 'value4']})
        self.assertEqual(
            p.to_ical(),
            (b'ALTREP="http://www.wiz.org",value4;PARAMETER1=Value1;'
             b'PARAMETER2=Value2,Value3')
        )

        # We can also parse parameter strings
        self.assertEqual(
            Parameters.from_ical('PARAMETER1=Value 1;param2=Value 2'),
            Parameters({'PARAMETER1': 'Value 1', 'PARAM2': 'Value 2'})
        )

        # Including empty strings
        self.assertEqual(Parameters.from_ical('param='),
                         Parameters({'PARAM': ''}))

        # We can also parse parameter strings
        self.assertEqual(
            Parameters.from_ical(
                'MEMBER="MAILTO:projectA@host.com","MAILTO:projectB@host.com"'
            ),
            Parameters({'MEMBER': ['MAILTO:projectA@host.com',
                                   'MAILTO:projectB@host.com']})
        )

        # We can also parse parameter strings
        self.assertEqual(
            Parameters.from_ical('ALTREP="http://www.wiz.org",value4;'
                                 'PARAMETER1=Value1;PARAMETER2=Value2,Value3'),
            Parameters({'PARAMETER1': 'Value1',
                        'ALTREP': ['http://www.wiz.org', 'value4'],
                        'PARAMETER2': ['Value2', 'Value3']})
        )

    def test_parse_and_access_property_params(self):
        """Parse an ics string and access some property parameters then.
        This is a follow-up of a question recieved per email.

        """
        ics = """BEGIN:VCALENDAR
VERSION:2.0
PRODID://RESEARCH IN MOTION//BIS 3.0
METHOD:REQUEST
BEGIN:VEVENT
SEQUENCE:2
X-RIM-REVISION:0
SUMMARY:Test meeting from BB
X-MICROSOFT-CDO-ALLDAYEVENT:TRUE
CLASS:PUBLIC
ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN="RembrandXS":MAILTO:rembrand@xs4all.nl
ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN="RembrandDX":MAILTO:rembrand@daxlab.com
ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN="RembrandSB":MAILTO:rembspam@xs4all.nl
UID:XRIMCAL-628059586-522954492-9750559
DTSTART;VALUE=DATE:20120814
DTEND;VALUE=DATE:20120815
DESCRIPTION:Test meeting from BB
DTSTAMP:20120813T151458Z
ORGANIZER:mailto:rembrand@daxlab.com
END:VEVENT
END:VCALENDAR"""

        cal = icalendar.Calendar.from_ical(ics)
        event = cal.walk("VEVENT")[0]
        event['attendee'][0]
        self.assertEqual(event['attendee'][0].to_ical(),
                         b'MAILTO:rembrand@xs4all.nl')
        self.assertEqual(event['attendee'][0].params.to_ical(),
                         b'CN=RembrandXS;PARTSTAT=NEEDS-ACTION;RSVP=TRUE')
        self.assertEqual(event['attendee'][0].params['cn'], u'RembrandXS')

    def test_repr(self):
        """Test correct class representation.
        """
        it = Parameters(parameter1='Value1')
        self.assertTrue(
            re.match("Parameters\({u?'PARAMETER1': 'Value1'}\)", str(it))
        )
