# -*- coding: utf-8 -*-
from datetime import datetime, timedelta
import unittest

from messaging.sms import SmsSubmit, SmsDeliver
from messaging.utils import (timedelta_to_relative_validity as to_relative,
                             datetime_to_absolute_validity as to_absolute,
                             FixedOffset)


class TestEncodingFunctions(unittest.TestCase):

    def test_converting_timedelta_to_validity(self):
        self.assertEqual(to_relative(timedelta(minutes=5)), 0)
        self.assertEqual(to_relative(timedelta(minutes=6)), 0)
        self.assertEqual(to_relative(timedelta(minutes=10)), 1)

        self.assertEqual(to_relative(timedelta(hours=12)), 143)
        self.assertEqual(to_relative(timedelta(hours=13)), 145)
        self.assertEqual(to_relative(timedelta(hours=24)), 167)

        self.assertEqual(to_relative(timedelta(days=2)), 168)
        self.assertEqual(to_relative(timedelta(days=30)), 196)

    def test_converting_datetime_to_validity(self):
        # http://www.dreamfabric.com/sms/scts.html
        # 12. Feb 1999 05:57:30 GMT+3
        when = datetime(1999, 2, 12, 5, 57, 30, 0,
                         FixedOffset(3 * 60, "GMT+3"))
        expected = [0x99, 0x20, 0x21, 0x50, 0x75, 0x03, 0x21]
        self.assertEqual(to_absolute(when, "GMT+3"), expected)

        when = datetime(1999, 2, 12, 5, 57, 30, 0)
        expected = [0x99, 0x20, 0x21, 0x50, 0x75, 0x03, 0x0]
        self.assertEqual(to_absolute(when, "UTC"), expected)

        when = datetime(1999, 2, 12, 5, 57, 30, 0,
                         FixedOffset(-3 * 60, "GMT-3"))
        expected = [0x99, 0x20, 0x21, 0x50, 0x75, 0x03, 0x29]
        self.assertEqual(to_absolute(when, "GMT-3"), expected)


class TestSmsSubmit(unittest.TestCase):

    def test_encoding_validity(self):
        # no validity
        number = '2b3334363136353835313139'.decode('hex')
        text = "hola"
        expected = "0001000B914316565811F9000004E8373B0C"

        sms = SmsSubmit(number, text)
        sms.ref = 0x0

        pdu = sms.to_pdu()[0]
        self.assertEqual(pdu.pdu, expected)

        # absolute validity
        number = '2b3334363136353835313139'.decode('hex')
        text = "hola"
        expected = "0019000B914316565811F900000170520251930004E8373B0C"

        sms = SmsSubmit(number, text)
        sms.ref = 0x0
        sms.validity = datetime(2010, 7, 25, 20, 15, 39)

        pdu = sms.to_pdu()[0]
        self.assertEqual(pdu.pdu, expected)

        # relative validity
        number = '2b3334363136353835313139'.decode('hex')
        text = "hola"
        expected = "0011000B914316565811F90000AA04E8373B0C"
        expected_len = 18

        sms = SmsSubmit(number, text)
        sms.ref = 0x0
        sms.validity = timedelta(days=4)

        pdu = sms.to_pdu()[0]
        self.assertEqual(pdu.pdu, expected)
        self.assertEqual(pdu.length, expected_len)

    def test_encoding_csca(self):
        number = '2b3334363136353835313139'.decode('hex')
        text = "hola"
        csca = "+34646456456"
        expected = "07914346466554F601000B914316565811F9000004E8373B0C"
        expected_len = 17

        sms = SmsSubmit(number, text)
        sms.csca = csca
        sms.ref = 0x0

        pdu = sms.to_pdu()[0]
        self.assertEqual(pdu.pdu, expected)
        self.assertEqual(pdu.length, expected_len)
        self.assertEqual(pdu.cnt, 1)
        self.assertEqual(pdu.seq, 1)

    def test_encoding_class(self):
        number = '2b3334363534313233343536'.decode('hex')
        text = "hey yo"
        expected_0 = "0001000B914356143254F6001006E8721E947F03"
        expected_1 = "0001000B914356143254F6001106E8721E947F03"
        expected_2 = "0001000B914356143254F6001206E8721E947F03"
        expected_3 = "0001000B914356143254F6001306E8721E947F03"

        sms = SmsSubmit(number, text)
        sms.ref = 0x0
        sms.klass = 0

        pdu = sms.to_pdu()[0]
        self.assertEqual(pdu.pdu, expected_0)

        sms.klass = 1
        pdu = sms.to_pdu()[0]
        self.assertEqual(pdu.pdu, expected_1)

        sms.klass = 2
        pdu = sms.to_pdu()[0]
        self.assertEqual(pdu.pdu, expected_2)

        sms.klass = 3
        pdu = sms.to_pdu()[0]
        self.assertEqual(pdu.pdu, expected_3)

    def test_encoding_request_status(self):
        # tested with pduspy.exe and http://www.rednaxela.net/pdu.php
        number = '2b3334363534313233343536'.decode('hex')
        text = "hey yo"
        expected = "0021000B914356143254F6000006E8721E947F03"

        sms = SmsSubmit(number, text)
        sms.ref = 0x0
        sms.request_status = True

        pdu = sms.to_pdu()[0]
        self.assertEqual(pdu.pdu, expected)

    def test_encoding_message_with_latin1_chars(self):
        # tested with pduspy.exe
        number = '2b3334363534313233343536'.decode('hex')
        text = u"Hölä"
        expected = "0011000B914356143254F60000AA04483E7B0F"

        sms = SmsSubmit(number, text)
        sms.ref = 0x0
        sms.validity = timedelta(days=4)

        pdu = sms.to_pdu()[0]
        self.assertEqual(pdu.pdu, expected)

        # tested with pduspy.exe
        number = '2b3334363534313233343536'.decode('hex')
        text = u"BÄRÇA äñ@"
        expected = "0001000B914356143254F6000009C2AD341104EDFB00"

        sms = SmsSubmit(number, text)
        sms.ref = 0x0

        pdu = sms.to_pdu()[0]
        self.assertEqual(pdu.pdu, expected)

    def test_encoding_8bit_message(self):
        number = "01000000000"
        csca = "+44000000000"
        text = "Hi there..."
        expected = "07914400000000F001000B811000000000F000040B48692074686572652E2E2E"

        sms = SmsSubmit(number, text)
        sms.ref = 0x0
        sms.csca = csca
        sms.fmt = 0x04  # 8 bits

        pdu = sms.to_pdu()[0]
        self.assertEqual(pdu.pdu, expected)

    def test_encoding_ucs2_message(self):
        number = '2b3334363136353835313139'.decode('hex')
        text = u'あ叶葉'
        csca = '+34646456456'
        expected = "07914346466554F601000B914316565811F9000806304253F68449"

        sms = SmsSubmit(number, text)
        sms.ref = 0x0
        sms.csca = csca

        pdu = sms.to_pdu()[0]
        self.assertEqual(pdu.pdu, expected)

        text = u"Русский"
        number = '363535333435363738'.decode('hex')
        expected = "001100098156355476F80008AA0E0420044304410441043A04380439"

        sms = SmsSubmit(number, text)
        sms.ref = 0x0
        sms.validity = timedelta(days=4)

        pdu = sms.to_pdu()[0]
        self.assertEqual(pdu.pdu, expected)

    def test_encoding_multipart_7bit(self):
        # text encoded with umts-tools
        text = "Or walk with Kings - nor lose the common touch, if neither foes nor loving friends can hurt you, If all men count with you, but none too much; If you can fill the unforgiving minute With sixty seconds' worth of distance run, Yours is the Earth and everything thats in it, And - which is more - you will be a Man, my son"
        number = '363535333435363738'.decode('hex')
        expected = [
            "005100098156355476F80000AAA00500038803019E72D03DCC5E83EE693A1AB44CBBCF73500BE47ECB41ECF7BC0CA2A3CBA0F1BBDD7EBB41F4777D8C6681D26690BB9CA6A3CB7290F95D9E83DC6F3988FDB6A7DD6790599E2EBBC973D038EC06A1EB723A28FFAEB340493328CC6683DA653768FCAEBBE9A07B9A8E06E5DF7516485CA783DC6F7719447FBF41EDFA18BD0325CDA0FCBB0E1A87DD",
            "005100098156355476F80000AAA005000388030240E6349B0DA2A3CBA0BADBFC969FD3F6B4FB0C6AA7DD757A19744DD3D1A0791A4FCF83E6E5F1DB4D9E9F40F7B79C8E06BDCD20727A4E0FBBC76590BCEE6681B2EFBA7C0E4ACF41747419540CCBE96850D84D0695ED65799E8E4EBBCF203A3A4C9F83D26E509ACE0205DD64500B7447A7C768507A0E6ABFE565500B947FD741F7349B0D129741",
            "005100098156355476F80000AA14050003880303C2A066D8CD02B5F3A0F9DB0D",
        ]

        sms = SmsSubmit(number, text)
        sms.ref = 0x0
        sms.rand_id = 136
        sms.validity = timedelta(days=4)

        ret = sms.to_pdu()
        cnt = len(ret)
        for i, pdu in enumerate(ret):
            self.assertEqual(pdu.pdu, expected[i])
            self.assertEqual(pdu.seq, i + 1)
            self.assertEqual(pdu.cnt, cnt)

    def test_encoding_bad_number_raises_error(self):
        self.assertRaises(ValueError, SmsSubmit, "032BADNUMBER", "text")

    def test_encoding_bad_csca_raises_error(self):
        sms = SmsSubmit("54342342", "text")
        self.assertRaises(ValueError, setattr, sms, 'csca', "1badcsca")


class TestSubmitPduCounts(unittest.TestCase):

    DEST = "+3530000000"
    GSM_CHAR = "x"
    EGSM_CHAR = u"€"
    UNICODE_CHAR = u"ő"

    def test_gsm_1(self):
        sms = SmsSubmit(self.DEST, self.GSM_CHAR * 160)
        self.assertEqual(len(sms.to_pdu()), 1)

    def test_gsm_2(self):
        sms = SmsSubmit(self.DEST, self.GSM_CHAR * 161)
        self.assertEqual(len(sms.to_pdu()), 2)

    def test_gsm_3(self):
        sms = SmsSubmit(self.DEST, self.GSM_CHAR * 153 * 2)
        self.assertEqual(len(sms.to_pdu()), 2)

    def test_gsm_4(self):
        sms = SmsSubmit(self.DEST,
                        self.GSM_CHAR * 153 * 2 + self.GSM_CHAR)
        self.assertEqual(len(sms.to_pdu()), 3)

    def test_gsm_5(self):
        sms = SmsSubmit(self.DEST, self.GSM_CHAR * 153 * 3)
        self.assertEqual(len(sms.to_pdu()), 3)

    def test_gsm_6(self):
        sms = SmsSubmit(self.DEST,
                        self.GSM_CHAR * 153 * 3 + self.GSM_CHAR)
        self.assertEqual(len(sms.to_pdu()), 4)

    def test_egsm_1(self):
        sms = SmsSubmit(self.DEST, self.EGSM_CHAR * 80)
        self.assertEqual(len(sms.to_pdu()), 1)

    def test_egsm_2(self):
        sms = SmsSubmit(self.DEST,
                        self.EGSM_CHAR * 79 + self.GSM_CHAR)
        self.assertEqual(len(sms.to_pdu()), 1)

    def test_egsm_3(self):
        sms = SmsSubmit(self.DEST, self.EGSM_CHAR * 153)  # 306 septets
        self.assertEqual(len(sms.to_pdu()), 3)

    def test_egsm_4(self):
        sms = SmsSubmit(self.DEST,
                          self.EGSM_CHAR * 229 + self.GSM_CHAR)  # 459 septets
        self.assertEqual(len(sms.to_pdu()), 4)

    def test_unicode_1(self):
        sms = SmsSubmit(self.DEST, self.UNICODE_CHAR * 70)
        self.assertEqual(len(sms.to_pdu()), 1)

    def test_unicode_2(self):
        sms = SmsSubmit(self.DEST, self.UNICODE_CHAR * 70 + self.GSM_CHAR)
        self.assertEqual(len(sms.to_pdu()), 2)

    def test_unicode_3(self):
        sms = SmsSubmit(self.DEST, self.UNICODE_CHAR * 67 * 2)
        self.assertEqual(len(sms.to_pdu()), 2)

    def test_unicode_4(self):
        sms = SmsSubmit(self.DEST, self.UNICODE_CHAR * 67 * 2 + self.GSM_CHAR)
        self.assertEqual(len(sms.to_pdu()), 3)

    def test_unicode_5(self):
        sms = SmsSubmit(self.DEST, self.UNICODE_CHAR * 67 * 3)
        self.assertEqual(len(sms.to_pdu()), 3)

    def test_unicode_6(self):
        sms = SmsSubmit(self.DEST, self.UNICODE_CHAR * 67 * 3 + self.GSM_CHAR)
        self.assertEqual(len(sms.to_pdu()), 4)


class TestSmsDeliver(unittest.TestCase):

    def test_decoding_7bit_pdu(self):
        pdu = "07911326040000F0040B911346610089F60000208062917314080CC8F71D14969741F977FD07"
        text = "How are you?"
        csca = "+31624000000"
        number = '2b3331363431363030393836'.decode('hex')

        sms = SmsDeliver(pdu)
        self.assertEqual(sms.text, text)
        self.assertEqual(sms.csca, csca)
        self.assertEqual(sms.number, number)

    def test_decoding_ucs2_pdu(self):
        pdu = "07914306073011F0040B914316709807F2000880604290224080084E2D5174901A8BAF"
        text = u"中兴通讯"
        csca = "+34607003110"
        number = '2b3334363130373839373032'.decode('hex')

        sms = SmsDeliver(pdu)
        self.assertEqual(sms.text, text)
        self.assertEqual(sms.csca, csca)
        self.assertEqual(sms.number, number)

    def test_decoding_7bit_pdu_data(self):
        pdu = "07911326040000F0040B911346610089F60000208062917314080CC8F71D14969741F977FD07"
        text = "How are you?"
        csca = "+31624000000"
        number = '2b3331363431363030393836'.decode('hex')

        data = SmsDeliver(pdu).data
        self.assertEqual(data['text'], text)
        self.assertEqual(data['csca'], csca)
        self.assertEqual(data['number'], number)
        self.assertEqual(data['pid'], 0)
        self.assertEqual(data['fmt'], 0)
        self.assertEqual(data['date'], datetime(2002, 8, 26, 19, 37, 41))

    def test_decoding_datetime_gmtplusone(self):
        pdu = "0791447758100650040C914497716247010000909010711423400A2050EC468B81C4733A"
        text = "  1741 bst"
        number = '2b343437393137323637343130'.decode('hex')
        date = datetime(2009, 9, 1, 16, 41, 32)

        sms = SmsDeliver(pdu)
        self.assertEqual(sms.text, text)
        self.assertEqual(sms.number, number)
        self.assertEqual(sms.date, date)

    def test_decoding_datetime_gmtminusthree(self):
        pdu = "0791553001000001040491578800000190115101112979CF340B342F9FEBE536E83D0791C3E4F71C440E83E6F53068FE66A7C7697A781C7EBB4050F99BFE1EBFD96F1D48068BC16030182E66ABD560B41988FC06D1D3F03768FA66A7C7697A781C7E83CCEF34282C2ECBE96F50B90D8AC55EB0DC4B068BC140B1994E16D3D1622E"
        date = datetime(2010, 9, 11, 18, 10, 11)  # 11/09/10 15:10 GMT-3.00

        sms = SmsDeliver(pdu)
        self.assertEqual(sms.date, date)

    def test_decoding_number_alphanumeric(self):
        # Odd length test
        pdu = "07919471060040340409D0C6A733390400009060920173018093CC74595C96838C4F6772085AD6DDE4320B444E9741D4B03C6D7EC3E9E9B71B9474D3CB727799DEA286CFE5B9991DA6CBC3F432E85E9793CBA0F09A9EB6A7CB72BA0B9474D3CB727799DE72D6E9FABAFB0CBAA7E56490BA4CD7D34170F91BE4ACD3F575F7794E0F9F4161F1B92C2F8FD1EE32DD054AA2E520E3D3991C82A8E5701B"
        number = "FONIC"
        text = "Lieber FONIC Kunde, die Tarifoption Internet-Tagesflatrate wurde aktiviert. Internet-Nutzung wird jetzt pro Nutzungstag abgerechnet. Ihr FONIC Team"
        csca = "+491760000443"

        sms = SmsDeliver(pdu)
        self.assertEqual(sms.text, text)
        self.assertEqual(sms.csca, csca)
        self.assertEqual(sms.number, number)

        # Even length test
        pdu = "07919333852804000412D0F7FBDD454FB75D693A0000903002801153402BCD301E9F0605D9E971191483C140412A35690D52832063D2F9040599A058EE05A3BD6430580E"
        number = "www.tim.it"
        text = 'Maxxi Alice 100 ATTIVATA FINO AL 19/04/2009'
        csca = '+393358824000'

        sms = SmsDeliver(pdu)
        self.assertEqual(sms.text, text)
        self.assertEqual(sms.csca, csca)
        self.assertEqual(sms.number, number)

    def test_decode_sms_confirmation(self):
        pdu = "07914306073011F006270B913426565711F7012081111345400120811174054043"
        csca = "+34607003110"
        date = datetime(2010, 2, 18, 11, 31, 54)
        number = "SR-UNKNOWN"
        # XXX: the number should be +344626575117, is the prefix flipped ?
        text = "+43626575117|10/02/18 11:31:54|"

        sms = SmsDeliver(pdu)
        self.assertEqual(sms.text, text)
        self.assertEqual(sms.csca, csca)
        self.assertEqual(sms.number, number)
        self.assertEqual(sms.date, date)

    def test_decode_weird_multipart_german_pdu(self):
        pdus = [
            "07919471227210244405852122F039F101506271217180A005000319020198E9B2B82C0759DFE4B0F9ED2EB7967537B9CC02B5D37450122D2FCB41EE303DFD7687D96537881A96A7CD6F383DFD7683F46134BBEC064DD36550DA0D22A7CBF3721BE42CD3F5A0198B56036DCA20B8FC0D6A0A4170767D0EAAE540433A082E7F83A6E5F93CFD76BB40D7B2DB0D9AA6CB2072BA3C2F83926EF31BE44E8FD17450BB8C9683CA",
            "07919471227210244405852122F039F1015062712181804F050003190202E4E8309B5E7683DAFC319A5E76B340F73D9A5D7683A6E93268FD9ED3CB6EF67B0E5AD172B19B2C2693C9602E90355D6683A6F0B007946E8382F5393BEC26BB00",
        ]
        texts = [
            u"Lieber Vodafone-Kunde, mit Ihrer nationalen Tarifoption zahlen Sie in diesem Netz 3,45 € pro MB plus 59 Ct pro Session. Wenn Sie diese Info nicht mehr e",
            u"rhalten möchten, wählen Sie kostenlos +4917212220. Viel Spaß im Ausland.",
        ]

        for i, sms in enumerate(map(SmsDeliver, pdus)):
            self.assertEqual(sms.text, texts[i])
            self.assertEqual(sms.udh.concat.cnt, len(pdus))
            self.assertEqual(sms.udh.concat.seq, i + 1)
            self.assertEqual(sms.udh.concat.ref, 25)

    def test_decoding_odd_length_pdu_strict_raises_valueerror(self):
        # same pdu as in test_decoding_number_alpha1 minus last char
        pdu = "07919471060040340409D0C6A733390400009060920173018093CC74595C96838C4F6772085AD6DDE4320B444E9741D4B03C6D7EC3E9E9B71B9474D3CB727799DEA286CFE5B9991DA6CBC3F432E85E9793CBA0F09A9EB6A7CB72BA0B9474D3CB727799DE72D6E9FABAFB0CBAA7E56490BA4CD7D34170F91BE4ACD3F575F7794E0F9F4161F1B92C2F8FD1EE32DD054AA2E520E3D3991C82A8E5701"
        self.assertRaises(ValueError, SmsDeliver, pdu)

    def test_decoding_odd_length_pdu_no_strict(self):
        # same pdu as in test_decoding_number_alpha1 minus last char
        pdu = "07919471060040340409D0C6A733390400009060920173018093CC74595C96838C4F6772085AD6DDE4320B444E9741D4B03C6D7EC3E9E9B71B9474D3CB727799DEA286CFE5B9991DA6CBC3F432E85E9793CBA0F09A9EB6A7CB72BA0B9474D3CB727799DE72D6E9FABAFB0CBAA7E56490BA4CD7D34170F91BE4ACD3F575F7794E0F9F4161F1B92C2F8FD1EE32DD054AA2E520E3D3991C82A8E5701"
        text = "Lieber FONIC Kunde, die Tarifoption Internet-Tagesflatrate wurde aktiviert. Internet-Nutzung wird jetzt pro Nutzungstag abgerechnet. Ihr FONIC Tea"

        sms = SmsDeliver(pdu, strict=False)
        self.assertEqual(sms.text, text)

    def test_decoding_delivery_status_report(self):
        pdu = "0791538375000075061805810531F1019082416500400190824165004000"
        sr = {
            'status': 0,
            'scts': datetime(2010, 9, 28, 14, 56),
            'dt': datetime(2010, 9, 28, 14, 56),
            'recipient': '50131'
        }

        sms = SmsDeliver(pdu)
        self.assertEqual(sms.csca, "+353857000057")
        data = sms.data
        self.assertEqual(data['ref'], 24)
        self.assertEqual(sms.sr, sr)

    def test_decoding_delivery_status_report_without_smsc_address(self):
        pdu = "00060505810531F1010150610000400101506100004000"
        sr = {
            'status': 0,
            'scts': datetime(2010, 10, 5, 16, 0),
            'dt': datetime(2010, 10, 5, 16, 0),
            'recipient': '50131'
        }

        sms = SmsDeliver(pdu)
        self.assertEqual(sms.csca, None)
        data = sms.data
        self.assertEqual(data['ref'], 5)
        self.assertEqual(sms.sr, sr)

# XXX: renable when support added
#    def test_decoding_submit_status_report(self):
#        # sent from SMSC to indicate submission failed or additional info
#        pdu = "07914306073011F001000B914306565711F9000007F0B2FC0DCABF01"
#        csca = "+34607003110"
#        number = "SR-UNKNOWN"
#
#        sms = SmsDeliver(pdu)
#        self.assertEqual(sms.csca, csca)
#        self.assertEqual(sms.number, number)
