#!/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.voicemail import VoiceMailTest, VoiceMailState

logger = logging.getLogger(__name__)

"""
TestState that attempts a login with a mailbox / incorrect password
"""
class LoginWithMailbox(VoiceMailState):

    def __init__(self, controller, voiceMailTest):
        VoiceMailState.__init__(self, controller, voiceMailTest)
        self.mailbox = "0"
        self.password = "0"

    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.mailbox)
            elif message == 'vm-password':
                self.voice_mail_test.send_dtmf(self.password)
                self.voice_mail_test.reset_timeout()
            elif message == 'vm-incorrect-mailbox':
                logger.warn("Failed to authenticate properly, failing test")
                self.voice_mail_test.passed = False
                self.voice_mail_test.stop_reactor()
            elif message == 'vm-goodbye':
                self.voice_mail_test.hangup()
            else:
                self.handle_default_state(event)
        elif state == 'AUTHENTICATED':
            logger.info("Authenticated for %s/%s, hanging up" % (self.mailbox, self.password))
            self.voice_mail_test.hangup()
        else:
            self.handle_default_state(event)

    def get_state_name(self):
        return "LOGIN WITH MAILBOX"

"""
TestState that attempts a login with an incorrect password only
"""
class LoginWithoutMailbox(VoiceMailState):

    def __init__(self, controller, voiceMailTest):
        VoiceMailState.__init__(self, controller, voiceMailTest)
        self.mailbox = "0"
        self.password = "0"

    def handle_state_change(self, ami, event):
        state = event['state']
        if state == 'PLAYBACK':
            message = event.get('message')
            if message == 'vm-password':
                self.voice_mail_test.send_dtmf(self.password)
                self.voice_mail_test.reset_timeout()
            elif message == 'vm-incorrect-mailbox' or message == 'vm-incorrect':
                logger.warn("Failed to authenticate properly, failing test")
                self.voice_mail_test.passed = False
                self.voice_mail_test.stop_reactor()
            elif message == 'vm-goodbye':
                self.voice_mail_test.hangup()
            else:
                self.handle_default_state(event)
        elif state == 'AUTHENTICATED':
            logger.info("Authenticated for %s/%s, hanging up" % (self.mailbox, self.password))
            self.voice_mail_test.hangup()
        else:
            self.handle_default_state(event)

    def get_state_name(self):
        return "LOGIN WITHOUT MAILBOX"



class AuthenticateNominal(VoiceMailTest):



    """ Test results for each iterations """
    results = [False, False, False]

    def __init__(self):
        super(AuthenticateNominal, self).__init__()
        """ Each tuple consists of the mailbox, password, expected channel and test controller object type """
        self.credentials = [
            ("9000", "1234", "SIP/ast1-00000000", LoginWithMailbox),
            ("9001", "0", "SIP/ast1-00000001", LoginWithoutMailbox),
            ("9002", "1234567890", "SIP/ast1-00000002", LoginWithoutMailbox)]
        self.create_asterisk(2)
        self.test_counter = 0

    def ami_connect(self, ami):
        super(AuthenticateNominal, 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
            ami.registerEvent('UserEvent', self.user_event)
        elif (ami.id == 1):
            self.ami_sender = ami
            self.ast_sender = self.ast[self.ami_sender.id]
            #ami.registerEvent('Hangup', self.hangup_event_handler)
        self.create_test_controller()

        if (self.ami_receiver != None and self.ami_sender != None):
            self.executeTest()

    def executeTest(self):
        tuple = self.credentials[self.test_counter]
        startObject = tuple[3](self.test_state_controller, self)
        startObject.mailbox = tuple[0]
        startObject.password = tuple[1]
        self.test_state_controller.change_state(startObject)
        self.sender_channel = tuple[2]

        logger.debug("Originating call to sip/ast1/" + tuple[0])
        df = self.ami_sender.originate("sip/ast1/" + tuple[0], "voicemailCaller", "wait", 1)
        df.addErrback(self.handle_originate_failure)
        self.test_counter += 1

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

        if ami.id == 0:
            """
            Note that by the time we receive this response, the test counter will have
            incremented to the next test
            """
            if event['result'] == 'pass':
                logger.info("VMAuthenticate successfully exited")
                self.results[self.test_counter - 1] = True
            else:
                self.results[self.test_counter - 1] = False
                logger.warn("VMAuthenticate did not successfully exit:")
                logger.warn("result: %s" % (event['result'],))
                logger.warn("error: %s" % (event['status'],))
            """ If we've seen all the responses, stop the test """
            if self.test_counter >= 3:
                logger.info("All responses received - Stopping Test")
                self.stop_reactor()

            if self.test_counter < 3:
                super(AuthenticateNominal, self).reset_timeout()
                self.executeTest()

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


def main():

    test = AuthenticateNominal()
    test.passed = True

    test.start_asterisk()

    reactor.run()

    test.stop_asterisk()

    for result in test.results:
        test.passed = test.passed and result

    if not test.passed:
        return 1

    return 0

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