#!/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 == 'PLAYBACK':
            message = event.get('message')

            if message == 'vm-options':
                if not self.voice_mail_test.get_test_condition('recordUnavailble'):
                    """ Tell it we want to record the unavailable greeting (1) """
                    self.voice_mail_test.send_dtmf("1")
                else:
                    """ Leave the menu """
                    self.voice_mail_test.send_dtmf("*")
            elif message == 'instructions':
                """ Back at main menu; exit """
                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-unv':
                self.voice_mail_test.set_test_condition('recordUnavailble', 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)
        else:
            self.handle_default_state(event)

    def get_state_name(self):
        return "VMOPTIONS"


"""
The TestCase class that executes the test
"""
class CheckVoicemailRecordUnavailable(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(CheckVoicemailRecordUnavailable, self).__init__()

        """
        This merely passes back the value to the test condition, as all of our conditions
        in this test are pass / fail tests
        """
        def checkTrueCondition(value, testCondition):
            return value
        self.add_test_condition("recordUnavailble", TestCondition(checkTrueCondition, False))

        self.reactor_timeout = 60
        self.create_asterisk(2)

    def ami_connect(self, ami):
        super(CheckVoicemailRecordUnavailable, 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(CheckVoicemailRecordUnavailable, self).run()
        self.create_ami_factory(2)

def main():

    test = CheckVoicemailRecordUnavailable()
    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 left all of the greetings """
        formats = ["ulaw","wav","WAV"]
        if not voicemailManager.check_greeting_exists("default","1234","unavail",formats):
            logger.warn("Failed to find unavail greeting for 1234@default")
            test.passed = False

    if not test.passed:
        return 1

    return 0

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