#!/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 logging

from twisted.internet import reactor

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

from asterisk.asterisk import Asterisk
from asterisk.test_case import TestCase
from asterisk.test_state import TestStateController
from asterisk.test_state import TestState
from asterisk.test_state import FailureTestState
from asterisk.voicemail import VoiceMailMailboxManagement
from asterisk.voicemail import TestCondition
from asterisk.voicemail import VoiceMailTest
from asterisk.voicemail import VoiceMailState

logger = logging.getLogger(__name__)

"""
TestState that is the entry point for the VoiceMailMain application
"""
class StartVoiceMailState(VoiceMailState):

    userMailbox = "1234#"

    userPassword = "1234#"

    def __init__(self, controller, voiceMailTest):
        VoiceMailState.__init__(self, controller, voiceMailTest)

    def handle_state_change(self, ami, event):
        state = event['state']

        if state == 'PLAYBACK':

            message = event.get('message')
            if message == 'vm-login':
                self.voice_mail_test.send_dtmf(self.userMailbox)
            elif message == 'vm-password':
                self.voice_mail_test.send_dtmf(self.userPassword)
        elif state == 'AUTHENTICATED':
            self.change_state(AuthenticatedTestState(self.controller, self.voice_mail_test))
        else:
            self.handle_default_state(event)

    def get_state_name(self):
        return "START"

"""
TestState that occurs after a user has been authenticated
"""
class AuthenticatedTestState(VoiceMailState):

    def __init__(self, controller, voiceMailTest):
        VoiceMailState.__init__(self, controller, voiceMailTest)

    def handle_state_change(self, ami, event):
        state = event['state']

        if state == 'AUTHENTICATED':
            logger.error("Received two authenticated events?")
            self.change_state(FailureTestState(self.controller))
        elif state == 'NEWUSER':
            logger.error("New user state received; user credentials must have failed")
            self.change_state(FailureTestState(self.controller))
        elif state == 'INTRO':
            self.change_state(IntroTestState(self.controller, self.voice_mail_test))
        else:
            self.handle_default_state(event)

    def get_state_name(self):
        return "AUTHENTICATED"


"""
TestState that occurs after when the user is being presented with the initial message counts and the main
voicemail menu
"""
class IntroTestState(VoiceMailState):

    def __init__(self, controller, voiceMailTest):
        VoiceMailState.__init__(self, controller, voiceMailTest)

    def handle_state_change(self, ami, event):
        state = event['state']

        if state == 'PLAYBACK':
            message = event.get('message')

            if message == 'instructions':
                """ Tell it to go to the voicemail options """
                self.voice_mail_test.send_dtmf("0")
        elif state == 'VMOPTIONS':
            self.change_state(VoicemailOptionsTestState(self.controller, self.voice_mail_test))
        else:
            self.handle_default_state(event)

    def get_state_name(self):
        return "INTRO"


"""
TestState that occurs when the user can change their options
"""
class VoicemailOptionsTestState(VoiceMailState):

    def __init__(self, controller, voiceMailTest):
        VoiceMailState.__init__(self, controller, voiceMailTest)

    def handle_state_change(self, ami, event):
        state = event['state']

        if state == 'AUTHENTICATED':
           logger.error("Received authentication event after NEWUSER?")
           self.change_state(FailureTestState(self.controller))
        elif state == 'NEWUSER':
            logger.error("Received a NEWUSER state in VMOPTIONS?")
            self.change_state(FailureTestState(self.controller))
        elif state == 'INTRO':
            self.change_state(IntroTestState(self.controller, self.voice_mail_test))
        elif state == 'TEMPGREETING':
            self.change_state(TempGreetingTestState(self.controller, self.voice_mail_test))
        elif state == 'PLAYBACK':
            message = event.get('message')

            if message == 'vm-options':
                if not self.voice_mail_test.get_test_condition('recordTemp'):
                    """ Tell it we want to record the temp greeting (4) """
                    self.voice_mail_test.send_dtmf("4")
                else:
                    """ Leave the menu """
                    self.voice_mail_test.send_dtmf("*")
            elif message == 'instructions':
                """ Back at main menu; exit """
                self.voice_mail_test.send_dtmf("#")
            else:
                self.handle_default_state(event)
        else:
            self.handle_default_state(event)

    def get_state_name(self):
        return "VMOPTIONS"


"""
TestState that occurs when the user needs to manage a temp recording
NOTE: the voice prompts are a little screwy with this menu, as what gets played after you
record a temp greeting is not the same as what happens after you delete it.  Whats more, it
automatically pushes you back out to the options menu, as opposed to keeping you in the temp
greetings menu.
"""
class TempGreetingTestState(VoiceMailState):

    def __init__(self, controller, voiceMailTest):
        VoiceMailState.__init__(self, controller, voiceMailTest)

    def handle_state_change(self, ami, event):
        state = event['state']

        if state == 'AUTHENTICATED':
           logger.error("Received authentication event in TEMPGREETING?")
           self.change_state(FailureTestState(self.controller))
        elif state == 'NEWUSER':
            logger.error("Received a NEWUSER state in TEMPGREETING?")
            self.change_state(FailureTestState(self.controller))
        elif state == 'INTRO':
            logger.error("Received a INTRO state in TEMPGREETING?")
            self.change_state(FailureTestState(self.controller))
        elif state == 'VMOPTIONS':
            self.change_state(VoicemailOptionsTestState(self.controller, self.voice_mail_test))
        elif state == 'PLAYBACK':
            message = event.get('message')
            if message == 'vm-tmpexists':
                """ Go back into the temp menu """
                if self.voice_mail_test.get_test_condition("recordTemp") and not self.voice_mail_test.get_test_condition("deletedTemp"):
                    self.voice_mail_test.send_dtmf("4")
                else:
                    """ Select * to leave the menu """
                    self.voice_mail_test.send_dtmf("*")
                    self.change_state(VoicemailOptionsTestState(self.controller, self.voice_mail_test))
            if message == 'vm-tempgreeting2' or message == 'vm-tempgreeting':
                """ If we haven't recorded the message yet, select 1 to record """
                if not self.voice_mail_test.get_test_condition("recordTemp"):
                    self.voice_mail_test.send_dtmf("1")
                elif self.voice_mail_test.get_test_condition("recordTemp") and not self.voice_mail_test.get_test_condition("deletedTemp"):
                    """ Verify that we actually left the file """
                    self.voice_mail_test.set_test_condition('checkRecordedTempFile', None)
                    """ Tell ourselves to delete the file """
                    self.voice_mail_test.send_dtmf("2")
                elif self.voice_mail_test.get_test_condition("recordTemp") and self.voice_mail_test.get_test_condition("deletedTemp"):
                    """ Select * to leave the menu """
                    self.voice_mail_test.send_dtmf("*")
                else:
                    logger.error("Somehow we didn't leave the temp greeting and then delete it")
                    self.change_state(FailureTestState(self.controller))
            elif message == 'vm-tempremoved':
                self.voice_mail_test.set_test_condition("deletedTemp", True)
            elif message == 'vm-options':
                self.change_state(VoicemailOptionsTestState(self.controller, self.voice_mail_test))
                self.voice_mail_test.send_dtmf("*")
            elif message == 'vm-review':
                """ If we review a message, tell it to save the message """
                self.voice_mail_test.send_dtmf("1")
            elif message == 'vm-rec-temp':
                self.voice_mail_test.set_test_condition('recordTemp', True)
            elif message == 'beep':
                """ A beep indicates we need to stream some sound file over - use the same sound file for everything """
                audioFile = os.path.join(os.getcwd(), "%s/sounds/talking" % (self.voice_mail_test.testParentDir))
                self.voice_mail_test.send_sound_file_with_dtmf(audioFile, "#")
        else:
            self.handle_default_state(event)

    def get_state_name(self):
        return "TEMPGREETING"


"""
The TestCase class that executes the test
"""
class CheckVoicemailRecordTemp(VoiceMailTest):

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

    """
    The channel to connect to that acts as the voicemail server
    """
    channel = "sip/ast1/8052"

    """
    The voicemail manager object
    """
    voicemailManager = None

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

        """
        This merely passes back the value to the test condition - turning it into a true / false
        condition check
        """
        def checkTrueCondition(value, testCondition):
            return value

        self.add_test_condition("recordTemp", TestCondition(checkTrueCondition, False))
        self.add_test_condition("deletedTemp", TestCondition(checkTrueCondition, False))

        """
        This will check that the temp greeting actually exists on the file system.  This occurs during
        the test, as after we've created the file, we will attempt to delete it.
        """
        def checkTempFileExists(value, testCondition):
            """ Note that when we're called to evaluate ourselves, value will be None, and
            the testCondition's data object will have a reference back to ourselves. """

            formats = ["ulaw","wav","WAV"]
            voicemailManager = VoiceMailMailboxManagement(testCondition.test_condition_data.ast[0])
            return voicemailManager.check_greeting_exists("default","1234","temp",formats)

        self.add_test_condition("checkRecordedTempFile", TestCondition(checkTempFileExists, self))

        self.reactor_timeout = 60
        self.create_asterisk(2)


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

        """ Record which AMI instance we've received and attempt to set up the test controller """
        if (ami.id == 0):
            self.ami_receiver = ami
        elif (ami.id == 1):
            self.ami_sender = ami
            self.ast_sender = self.ast[self.ami_sender.id]

        self.create_test_controller()
        if (self.test_state_controller != None):
            startObject = StartVoiceMailState(self.test_state_controller, self)
            self.test_state_controller.change_state(startObject)
            self.test_state_controller.add_assert_handler(self.handleAssert)

        """ Now do specific processing on the AMI instances """
        if (ami.id == 0):

            ami.registerEvent('UserEvent', self.user_event)

        else:
            logger.debug("Originating call to " + self.channel)
            df = ami.originate(self.channel, "voicemailCaller", "wait", 1)
            df.addErrback(self.handle_originate_failure)

    def handleAssert(self, event):
        self.passed = False
        logger.error("Test Failed - Assert received")
        logger.error("\t\t AppFunction: " + event['appfunction'])
        logger.error("\t\t AppLine: " + event['appline'])
        logger.error("\t\t Expression: " + event['expression'])

        self.stop_reactor()

    def user_event(self, ami, event):
        if event['userevent'] != 'TestResult':
            return

        if event['result'] == "pass":
            self.passed = True
            logger.info("VoiceMail successfully exited")
        else:
            logger.warn("VoiceMail did not successfully exit:")
            logger.warn("result: %s" % (event['result'],))
            logger.warn("error: %s" % (event['error'],))

        self.stop_reactor()

    def run(self):
        super(CheckVoicemailRecordTemp, self).run()
        self.create_ami_factory(2)

def main():

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

    test.start_asterisk()

    reactor.run()

    test.stop_asterisk()

    """
    Post-test processing - verify that we listened to all the messages we wanted to listen to, that
    we saved the messages, and that the messages were moved successfully
    """
    if test.passed:

        if not test.check_test_conditions():
            logger.warn("Test failed condition checks")
            test.passed = False

        """ Verify that we deleted the temp greeting """
        formats = ["ulaw","wav","WAV"]
        if voicemailManager.check_greeting_exists("default","1234","temp",formats):
            logger.warn("Temp greeting was not deleted for 1234@default")
            test.passed = False

    if not test.passed:
        return 1

    return 0

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