#!/usr/bin/env python
# vim: sw=3 et:
'''
Copyright (C) 2011, Digium, Inc.
Matt Jordan <mjordan@digium.com>

This program is free software, distributed under the terms of
the GNU General Public License Version 2.
'''

import sys
import os
import shutil
import logging

from twisted.internet import reactor

sys.path.append("lib/python")

from asterisk.asterisk import Asterisk
from asterisk.test_case import TestCase
from asterisk.voicemail import VoiceMailMailboxManagement

logger = logging.getLogger(__name__)

class LeaveVoicemailExternNotify(TestCase):

    """
    The parent directory that this test resides in
    """
    testParentDir = "tests/apps/voicemail"

    def __init__(self):
        super(LeaveVoicemailExternNotify, self).__init__()

        self.reactor_timeout = 120
        self.testCounter = 0
        self.expectedValues = {'1234': False, '9000': False, '9001': False, '9002': False, '9003': False}
        self.create_asterisk(2)
        self.senderAmi = None

    def ami_connect(self, ami):
        super(LeaveVoicemailExternNotify, self).ami_connect(ami)

        self.audioFile = os.path.join(os.getcwd(), "%s/sounds/talking" % (self.testParentDir))
        ami.registerEvent('UserEvent', self.user_event)

        if (ami.id != 0):
            self.ast[ami.id].cli_exec("dialplan set global TALK_AUDIO " + self.audioFile)
            self.senderAmi = ami
            self.executeTest()

    def validateMail1234(self, filePath):
        expectedMailTokens = {'From': False, 'To': False, 'Subject': False, 'Body': False, 'Attachment': False}
        for line in open(filePath, 'r'):
            if "From: \"Asterisk\"" in line:
                expectedMailTokens['From'] = True
            elif "To: \"Mark Spencer\" <mark@spencer.com>" in line:
                expectedMailTokens['To'] = True
            elif "Subject: New message in your mailbox" in line:
                expectedMailTokens['Subject'] = True
            elif "You have a new message in your mailbox" in line:
                expectedMailTokens['Body'] = True
            elif "Content-Description: Voicemail sound attachment." in line:
                expectedMailTokens['Attachment'] = True

        for k, v in expectedMailTokens.items():
            if not v:
                logger.warn("Failed to find token " + k + " in mailfile " + filePath)
                return False
        return True

    def validateMail9000(self, filePath):
        expectedMailTokens = {'From': False, 'To': False, 'Subject': False, 'Body': False}
        for line in open(filePath):
            if "From: \"Asterisk\"" in line:
                expectedMailTokens['From'] = True
            elif "To: \"User 0\" <pager@address.com>" in line:
                expectedMailTokens['To'] = True
            elif "Subject: New message" in line:
                expectedMailTokens['Subject'] = True
            elif "You have a new message" in line:
                expectedMailTokens['Body'] = True

        for k, v in expectedMailTokens.items():
            if not v:
                logger.warn(" Failed to find token " + k + " in mailfile " + filePath)
                return False
        return True

    def validateMail9001(self, filePath):
        expectedMailTokens = {'From': False, 'To': False, 'Subject': False, 'Body': False, 'Attachment': False}
        for line in open(filePath):
            if "From: \"Asterisk\"" in line:
                expectedMailTokens['From'] = True
            elif "To: \"User 1\" <email@address.com>" in line:
                expectedMailTokens['To'] = True
            elif "Subject: New message in your mailbox" in line:
                expectedMailTokens['Subject'] = True
            elif "You have a new message in your mailbox" in line:
                expectedMailTokens['Body'] = True
            elif "Content-Description: Voicemail sound attachment." in line:
                expectedMailTokens['Attachment'] = True

        for k, v in expectedMailTokens.items():
            if not v:
                logger.warn("Failed to find token " + k + " in mailfile " + filePath)
                return False
        return True

    def validateMail9002(self, filePath):
        """
        Technically, 9002 does not have a mail file.  The outputted file gets copied over as 9002's file;
        however, since 9002 does not have a pager or email address, this is actually the remains of 9001's
        mailfile.  This is a limitation of the test, and not app_voicemail - since we've overriden the mail
        command to use cat > mailfile.txt, we're responsible for the duplication.  Check the file anyways -
        if it is 9001's mail file, then this worked properly.
        """
        return self.validateMail9001(filePath)

    def validateMail9003(self, filePath):
        expectedMailTokens = {'From': False, 'To': False, 'Subject': False, 'Body': False, 'Attachment': True}
        for line in open(filePath):
            if "From: \"Asterisk\" <test@test.com>" in line:
                expectedMailTokens['From'] = True
            elif "To: \"User 3\" <email@address.com>" in line:
                expectedMailTokens['To'] = True
            elif "Subject: New message in your mailbox" in line:
                expectedMailTokens['Subject'] = True
            elif "You have a new message in your mailbox" in line:
                expectedMailTokens['Body'] = True
            elif "Content-Description: Voicemail sound attachment." in line:
                """
                We should not have an attachment - if we find this token, fail
                """
                expectedMailTokens['Attachment'] = False

        for k, v in expectedMailTokens.items():
            if not v:
                logger.warn("Failed to find token " + k + " in mailfile " + filePath)
                return False
        return True

    def executeTest(self):
        if self.testCounter == 0:
            logger.debug("Executing test [" + str(self.testCounter) + "] - send to 1234")
            df = self.senderAmi.originate("sip/ast1/1234", "sendvoicemail", "1234", 1)
            df.addErrback(self.handle_originate_failure)
        elif self.testCounter == 1:
            logger.debug("Executing test [" + str(self.testCounter) + "] - send to 9000")
            df = self.senderAmi.originate("sip/ast1/9000", "sendvoicemail", "1234", 1)
            df.addErrback(self.handle_originate_failure)
        elif self.testCounter == 2:
            logger.debug("Executing test [" + str(self.testCounter) + "] - send to 9001")
            df = self.senderAmi.originate("sip/ast1/9001", "sendvoicemail", "1234", 1)
            df.addErrback(self.handle_originate_failure)
        elif self.testCounter == 3:
            logger.debug("Executing test [" + str(self.testCounter) + "] - send to 9002")
            df = self.senderAmi.originate("sip/ast1/9002", "sendvoicemail", "1234", 1)
            df.addErrback(self.handle_originate_failure)
        elif self.testCounter == 4:
            logger.debug("Executing test [" + str(self.testCounter) + "] - send to 9003")
            df = self.senderAmi.originate("sip/ast1/9003", "sendvoicemail", "1234", 1)
            df.addErrback(self.handle_originate_failure)

        self.testCounter += 1

    def validateTest(self, extension):
        logger.debug("Validating for extension: " + extension)
        """
        Verify the mail file
        """
        mailFileName = '/tmp/asterisk-testsuite/mailoutput.txt'
        if os.path.exists(mailFileName):
            copiedFileName = '/tmp/asterisk-testsuite/mailoutput_' + extension + '.txt'
            shutil.copy(mailFileName, copiedFileName)

            logger.debug("Verifying mail file: " + copiedFileName)
            if extension == '1234':
                self.expectedValues[extension] = self.validateMail1234(copiedFileName)
            elif extension == '9000':
                self.expectedValues[extension] = self.validateMail9000(copiedFileName)
            elif extension == '9001':
                self.expectedValues[extension] = self.validateMail9001(copiedFileName)
            elif extension == '9002':
                self.expectedValues[extension] = self.validateMail9002(copiedFileName)
            elif extension == '9003':
                self.expectedValues[extension] = self.validateMail9003(copiedFileName)

            if (os.path.exists(copiedFileName)):
                os.unlink(copiedFileName)
        else:
            logger.warn("Expected e-mail or page file not found: " + mailFileName)
            self.expectedValues[extension] = False

        """
        Verify that the external notify script executed and that the mailbox counts are correct
        """
        if extension == '9000':
            newvoicemails = '0'
            urgentvoicemails = '1'
        elif extension == '9001':
            newvoicemails = '0'
            urgentvoicemails = '0'
        else:
            newvoicemails = '1'
            urgentvoicemails = '0'
        oldvoicemails = '0'

        expectedFileTokens = {'Context': 'default', 'Extension': extension, 'NewVoicemails': newvoicemails, 'OldVoicemails': oldvoicemails, 'UrgentVoicemails': urgentvoicemails}
        expectedFileTokenTracking = {'Context': False, 'Extension': False, 'NewVoicemails': False, 'OldVoicemails': False, 'UrgentVoicemails': False}
        externFileName = "/tmp/asterisk-testsuite/default_" + extension + ".txt"
        if os.path.exists(externFileName):
            logger.info("Verifying externNotify file: " + externFileName)
            f = open(externFileName, 'r')
            for line in f:
                tokens = line.partition('=')
                if tokens[0] in expectedFileTokens:
                    if expectedFileTokens[tokens[0]] != tokens[2].strip(' \n\r\t'):
                        logger.warn("File token mismatch for " + tokens[0] + ": Expected [" + expectedFileTokens[tokens[0]] + "]; actual [" + tokens[2].strip(' \n\r\t') + "]")
                        self.expectedValues[extension] = False
                    else:
                        expectedFileTokenTracking[tokens[0]] = True
                else:
                    logger.warn("Unknown file token: " + tokens[0])
                    self.expectedValues[extension] = False

            f.close()
            os.unlink(externFileName)

            for k, v in expectedFileTokenTracking.items():
                if not v:
                    logger.warn("Failed to find file token " + k + " in file " + externFileName)
                    self.expectedValues[extension] = False
        else:
            logger.error("Expected external file (created from externnotify script) not found: " + externFileName)
            self.expectedValues[extension] = False

    def user_event(self, ami, event):

        if event['userevent'] != 'TestResult':
            return

        if event['result'] != 'fail':
            logger.info("Received non-failure result " + event['result'])
            if event['result'] in self.expectedValues:
                self.expectedValues[event['result']] = True
                self.validateTest(event['result'])
            else:
                logger.warn("Unsupported or unexpected result: " + event['result'])
        else:
            self.passed = False
            logger.warn("VoiceMail did not successfully exit:")
            logger.warn("result: %s" % (event['result'],))
            logger.warn("error: %s" % (event['status'],))

        if (self.testCounter == 5):
            logger.info("Received all 5 responses; ending test")
            self.stop_reactor()
        else:
            logger.debug("Executing next test: [" + str(self.testCounter) + "]")
            self.executeTest()

    def run(self):
        super(LeaveVoicemailExternNotify, self).run()

        """ Create the AMI factories
        """
        self.create_ami_factory(2)

def main():

    """
    Copy the extern-notify-script to /tmp/asterisk-testsuite/.  This assumes that this is the base directory
    for the tests, which is currently hardcoded and not configurable
    """
    scriptFile = os.path.join(os.getcwd(), "tests/apps/voicemail/leave_voicemail_external_notification/extern-notify-script.py")
    shutil.copy(scriptFile, "/tmp/asterisk-testsuite/extern-notify-script.py")

    test = LeaveVoicemailExternNotify()
    voicemailManager = VoiceMailMailboxManagement(test.ast[0])

    test.start_asterisk()

    reactor.run()

    test.stop_asterisk()

    """
    Remove the extern-notify-script and mail text file, if they exist
    """
    if os.path.exists("/tmp/asterisk-testsuite/extern-notify-script.py"):
        os.unlink("/tmp/asterisk-testsuite/extern-notify-script.py")
    if os.path.exists("/tmp/asterisk-testsuite/mailoutput.txt"):
        os.unlink("/tmp/asterisk-testsuite/mailoutput.txt")

    """
    Assume we passed - check the expectedValues to see if we have any failures
    """
    test.passed = True
    for k,v in test.expectedValues.items():
        if not v:
            logger.warn("Test failed for extension: " + k)
            test.passed = False

    """
    Verify that messages were left in the appropriate places
    """
    if test.passed:
        formats = ["ulaw","wav","WAV"]
        if not voicemailManager.check_voicemail_exists("default","1234",0,formats):
            logger.warn("No voicemail left for default/1234")
            test.passed = False
        if not voicemailManager.check_voicemail_exists("default","9000",0,formats, "Urgent"):
            logger.warn("No Urgent voicemail left for default/9000")
            test.passed = False
        if voicemailManager.check_voicemail_exists("default","9001",0,formats):
            logger.warn("Voicemail left for default/9001 - delete flag was set and ignored")
            test.passed = False
        if not voicemailManager.check_voicemail_exists("default","9002",0,formats):
            logger.warn("No voicemail left for default/9002")
            test.passed = False
        if not voicemailManager.check_voicemail_exists("default","9003",0,formats):
            logger.warn("No voicemail left for default/9003")
            test.passed = False

    if not test.passed:
        return 1

    return 0

if __name__ == "__main__":
   sys.exit(main() or 0)
