#!/usr/bin/env python
'''
Copyright (C) 2012, Digium, Inc.
Mark Michelson <mmichelson@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

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

from asterisk.asterisk import Asterisk
from asterisk.test_case import TestCase
from asterisk.sipp import SIPpScenario
from twisted.internet import reactor

LOGGER = logging.getLogger(__name__)
TEST_DIR = os.path.dirname(os.path.realpath(__file__))

SIPP_SCENARIO =  {
    'scenario' : 'multiple.xml',
    '-p' : '5061',
    '-s' : 'bob',
}

class MultipleStateChange(TestCase):
    def __init__(self):
        TestCase.__init__(self)
        self.create_asterisk()
        self.sipTest = SIPpScenario(TEST_DIR, SIPP_SCENARIO)
        self.sippPassed = False
        self.notifyPassed = False
        self.notifyFailed = False
        self.num_notifies = 0

    def sippComplete(self, result):
        LOGGER.info("SIPp scenario passed successfully. Raising sippPassed "
                    "flag.")
        self.sippPassed = True
        self.stop_reactor()

    def originateComplete(self, result):
        LOGGER.info("Completed originated call for setting presence to "
                    "custom away status, originating call to set status to "
                    "available.")
        df = self.ami[0].originate(channel = "Local/available@default",
                application = "Echo")
        df.addErrback(self.handle_originate_failure)

    def runSippTest(self):
        LOGGER.info("Running SIPp scenario")
        df = self.sipTest.run(self)
        df.addCallback(self.sippComplete)

        LOGGER.info("Originating call to set presence to custom away status.")
        df = self.ami[0].originate(channel = "Local/away@default", application = "Echo")
        df.addErrback(self.handle_originate_failure)
        df.addCallback(self.originateComplete)

    def inspectPresence(self, ami, event):
        # For this test, we expect three of these events.
        # First one is when the presence is changed to "away"
        # Second one is when the presence is changed to "available"
        # Third one is when the subscribption is terminated.
        # The final two  should have the same values present.
        if event.get("state") != "DIGIUM_PRESENCE_SENT":
            return

        self.num_notifies = self.num_notifies + 1

        LOGGER.info("Received notify %d of 3" % self.num_notifies)

        if self.num_notifies > 3:
            LOGGER.error("Too many NOTIFYs! Dropping notifyPassed flag.")
            self.notifyPassed = False

        if self.num_notifies == 1:
            if event.get("presencestate") != "away":
                LOGGER.error("Incorrect presencestate value!")
                self.notifyFailed = True
                ami.unregisterEvent("TestEvent")
            if event.get("subtype") != "down the hall":
                LOGGER.error("Incorrect presence subtype!")
                self.notifyFailed = True
                ami.unregisterEvent("TestEvent")
            if event.get("message") != "Quarterly financial meeting":
                LOGGER.error("Incorrect presence message!")
                self.notifyFailed = True
                ami.unregisterEvent("TestEvent")
        else:
            if event.get("presencestate") != "available":
                LOGGER.error("Incorrect presencestate value!")
                self.notifyFailed = True
                ami.unregisterEvent("TestEvent")
            if event.get("subtype") != "":
                LOGGER.error("Incorrect presence subtype!")
                self.notifyFailed = True
                ami.unregisterEvent("TestEvent")
            if event.get("message") != "":
                LOGGER.error("Incorrect presence message!")
                self.notifyFailed = True
                ami.unregisterEvent("TestEvent")

        if self.num_notifies == 3 and not self.notifyFailed:
            LOGGER.info("Received expected notify events. Raising "
                        "notifyPassed flag.")
            self.notifyPassed = True

    def ami_connect(self, ami):
        self.ast[ami.id].cli_exec("sip set debug on")
        ami.registerEvent("TestEvent", self.inspectPresence)
        self.runSippTest()

    def stop_asterisk(self):
        ''' Kill the SIPp test if it didn't exit '''
        if not self.sipTest.exited:
            self.sipTest.kill()

    def run(self):
        TestCase.run(self)
        self.create_ami_factory()

def main():
    test = MultipleStateChange()
    reactor.run()
    if not test.sippPassed or not test.notifyPassed:
        if not test.sippPassed and not test.notifyPassed:
            LOGGER.error("Test failed because the SIPp scenario failed to "
                         "complete expected notify events weren't received.")
        elif not test.sippPassed:
            LOGGER.error("Test failed because the SIPp scenario failed.")
        else:
            LOGGER.error("Test failed because expected notify events weren't "
                         "received.")
        return 1
    return 0

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