#! /usr/bin/python
#
# Copyright (c) 2014 - Adjacent Link LLC, Bridgewater, New Jersey
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
#   notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
#   notice, this list of conditions and the following disclaimer in
#   the documentation and/or other materials provided with the
#   distribution.
# * Neither the name of Adjacent Link LLC nor the names of its
#   contributors may be used to endorse or promote products derived
#   from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#

import re
import sys
import time
from emanesh import EMANEShell
from emanesh import ControlPortException
from optparse import OptionParser

class TimeStampedLog:
   def __init__(self, stream):
       self._stream = stream
       self._buffer = ""

   def write(self, data):
       self._buffer  =  self._buffer + data
       
       index = self._buffer.find("\n")
       
       if index != -1:
           now = time.time()

           print >>self._stream, \
              "%s:%06d" % (time.strftime("%H:%M:%S",time.localtime(now)), \
                           (now - int(now)) * 1000000), self._buffer[:index]

           self._stream.flush()
           self._buffer = self._buffer[index:].lstrip()

   def __getattr__(self, attr):
       return getattr(self._stream, attr)

usage = "%prog [OPTION].. EELINPUTFILE"

description="""Specialized EEL generator that can issue Remote Control Port API
requests using the emanesh.EMANEShell python module. This generator will only
process 'emanesh' EEL commands and will ignore all others. The EMANE EEL event
generator plugin will safely ignore 'emanesh' commands, allowing you to use a
single EEL scenario file to generate events and commands."""

epilog="""
Sample EEL statements:
 0.0 localhost:47000 emanesh set config * phy txpower=1.0
 10.0 localhost:47000 emanesh clear stat * all
 12.0 localhost:47000 emanesh clear table * all
 12.0 localhost:47000 emanesh loglevel 3
 15.0 10.99.0.1:47000 emanesh set config * phy txpower=0
 15.0 10.99.0.2:47000 emanesh set config * phy txpower=0

"""

class LocalParser(OptionParser):
    def format_epilog(self, formatter):
        return self.epilog

optionParser =  LocalParser(usage=usage,
                            description=description,
                            epilog=epilog)

optionParser.add_option("", 
                        "--starttime",
                        action="store",
                        type="string",
                        dest="starttime",
                        help="Specify test start time HH:MM:SS")

optionParser.add_option("", 
                        "--logfile",
                        action="store",
                        type="string",
                        dest="logfile",
                        help="Write logs to file")      

(options, args) = optionParser.parse_args()

if len(args) != 1:
    print >>sys.stderr,"invalid number of arguments"
    exit(1)

scenario = open(args[0], 'r')

if options.starttime:
    m = re.match("(\d\d):(\d\d):(\d\d)",options.starttime)

    if m:
        now = time.localtime()
        
        startTime = time.mktime((now.tm_year,
                                now.tm_mon,
                                now.tm_mday,
                                int(m.group(1)),
                                int(m.group(2)),
                                int(m.group(3)),
                                now.tm_wday,
                                now.tm_yday,
                                -1))

        if startTime < time.time():
           print >>sys.stderr,"start time in the past"
           exit(1) 

    else:
        print >>sys.stderr,"invalid start time format"
        exit(1)

else:
    startTime = time.time()

if options.logfile:
    sys.stdout = open(options.logfile, 'w')

sys.stdout = TimeStampedLog(sys.stdout)
sys.stderr = sys.stdout

# parse the input file and create/send events 
for line in scenario:
   m = re.match("^(\d+(\.\d+){0,1})\s+([^:]+):(\d+)\s+emanesh+(.+)$",line)
   
   if m:
      eventTime =  float(m.group(1))
      node =  m.group(3)
      port =  int(m.group(4))
      remainder = m.group(5)
      shell = None
      try:
         # connect to emulator instance
         shell = EMANEShell(node,port)
         
         currentTime = time.time()
         absEventTime = eventTime + startTime
         
         # wait for the event time
         if absEventTime > currentTime:
            time.sleep(absEventTime - currentTime)
            
         print "emanesh %s:%d" % (node,port),remainder
         shell.onecmd(remainder)
         shell.onecmd('exit')
      except ControlPortException, exp:
         print >>sys.stderr,exp
         exit(1)
      except KeyboardInterrupt:
         if shell:
            shell.onecmd('exit')
         break
