from flask import Flask, url_for, jsonify, render_template, request, redirect
from waitress import serve # For running server on windows
import flask
from flask_httpauth import HTTPBasicAuth

import json
import logging
import telnetlib
import xmltodict, json
import time

try:
  import xml.etree.cElementTree as ET
except ImportError:
  import xml.etree.ElementTree as ET

verbose = True
spirent_simulator_HOST = "192.168.25.1"
spirent_simulator_PORT = "15650" # According to SimREMOTE User Manual, the client program must connect to port 15650.
web_port = 10002

scenario_file = 'D:\posapp\Scenarios\AugerTesting\AugerTesting.scn'
scenario_file_reduced_signal = 'D:\posapp\Scenarios\AugerTesting_low_power\AugerTesting_low_power.scn'

logging.basicConfig(level=logging.DEBUG)

app = Flask(__name__)
LOG = flask.logging.create_logger(app)

auth = HTTPBasicAuth()
user = {'username':'username','password':'password'}

status_dict = {0: 'No scenario specified',
              1: 'Loading',
              2: 'Ready',
              3: 'Arming',
              4: 'Armed',
              5: 'Running',
              6: 'Paused',
              7: 'Ended'}

@auth.verify_password
def verify_password(username, password):
    if username == user['username'] and password == user['password']:
      return True
    else:
      return False

def telnet_to_spirent(cmd, HOST = "192.168.25.1", PORT = "15650", timeout = 3):
  ''' Send and Receive message using telnet.
      Inputs: cmd = SimREMOTE cmd
              HOST = SimREMOTE host ip
              PORT = SimREMOTE port
              timeout = quit after sec
      Return: rst = returned message as byte data
  '''
  tn = telnetlib.Telnet()
  if verbose: print("\nOpen",HOST,':',PORT)
  tn.open(HOST, PORT, timeout)

  # send cmd
  if verbose: print("Send cmd",cmd)
  tn.write(cmd)

  if verbose: print("Reading response ...")
  rst = tn.read_until(b"</msg>", timeout )
  if verbose: print('Return msg:', rst)

  tn.close()

  return rst


@app.route('/api/v1/spirentTool/getStatus/', methods=['GET'])
def getStatus():
  timeout = 3
  cmd = b'NULL'
  try:
    msg = telnet_to_spirent(cmd, spirent_simulator_HOST, spirent_simulator_PORT, timeout)
  except Exception as e:
    d = {}
    d['Error'] = str(e)
    return json.dumps( d )  

  d = xmltodict.parse(msg)
  d['status description'] = status_dict[ int( d['msg']['status'] ) ]
  return json.dumps( d )

@app.route('/api/v1/spirentTool/getScenarioName/', methods=['GET'])
def getScenarioName():
  timeout = 3
  cmd = b'SC_NAME'
  try:
    msg = telnet_to_spirent(cmd, spirent_simulator_HOST, spirent_simulator_PORT, timeout)
  except Exception as e:
    d = {}
    d['Error'] = str(e)
    return json.dumps( d )

  d = xmltodict.parse(msg)
  d['status description'] = status_dict[ int( d['msg']['status'] ) ]
  return json.dumps( d )

@app.route('/api/v1/spirentTool/getTOW/', methods=['GET'])
def getTOW():
  timeout = 3
  cmd = b'-,ZCNT_TOW'
  try:
    msg = telnet_to_spirent(cmd, spirent_simulator_HOST, spirent_simulator_PORT, timeout)
  except Exception as e:
    d = {}
    d['Error'] = str(e)
    return json.dumps( d )

  d = xmltodict.parse(msg)
  return json.dumps( d )


@app.route('/api/v1/spirentTool/getLLH/', methods=['GET'])
def getLLH():
  """
  Read latitude, longitude and height from Spirent.
    # VEH_LAT     - Returns the latitude of the vehicle
    # VEH_LONG    - Returns the longitude of the vehicle
    # VEH_HEIGHT  - Returns the height of the vehicle (GPS Ellipsoid)
  """
  timeout = 3
  d = {}
  for key,cmd in zip(['lat','lon','h','elev'],[b'-,VEH_LAT,v1', b'-,VEH_LONG,v1', b'-,VEH_HEIGHT,v1',b'-,VEH_ELEVATION,v1']):
    try:
      msg = telnet_to_spirent(cmd, spirent_simulator_HOST, spirent_simulator_PORT, timeout)
    except Exception as e:
      d = {}
      d['Error'] = str(e)
      return json.dumps( d )
    d.update( {key: xmltodict.parse(msg)} )
  return json.dumps( d )

@app.route('/api/v1/spirentTool/closeScenario/', methods=['GET', 'POST'])
@auth.login_required
def closeScenario():
  timeout = 3
  cmd = b'SC_CLOSE'
  try:
    msg = telnet_to_spirent(cmd, spirent_simulator_HOST, spirent_simulator_PORT, timeout)
  except Exception as e:
    d = {}
    d['Error'] = str(e)
    return json.dumps( d )  

  d = xmltodict.parse(msg)
  d['status description'] = status_dict[ int( d['msg']['status'] ) ]
  return json.dumps( d )

@app.route('/api/v1/spirentTool/getScenario/', methods=['GET'])
def getScenario():
  """
  Return start_time of scenario xml file
  """
  f = open(scenario_file)
  data = f.read()
  f.close()
  d = xmltodict.parse(data)
  return d

@app.route('/api/v1/spirentTool/loadScenario/', methods=['GET', 'POST'])
@auth.login_required
def loadScenario():
  timeout = 3  
  scnfile = request.args.get('scenario_file')
  if scnfile and scnfile == 'reduced_signal':
    cmd =b'SC,' + str.encode(scenario_file_reduced_signal)
  else: 
    cmd =b'SC,' + str.encode(scenario_file)
  try:
    msg = telnet_to_spirent(cmd, spirent_simulator_HOST, spirent_simulator_PORT, timeout)
  except Exception as e:
    d = {}
    d['Error'] = str(e)
    return json.dumps( d )  

  d = xmltodict.parse(msg)
  d['status description'] = status_dict[ int( d['msg']['status'] ) ]
  return json.dumps( d )

@app.route('/api/v1/spirentTool/startScenario/', methods=['GET', 'POST'])
@auth.login_required
def startScenario():
  timeout = 15 # It needs additional time to Arm then Run
  cmd =b'RU'
  try:
    msg = telnet_to_spirent(cmd, spirent_simulator_HOST, spirent_simulator_PORT, timeout)
  except Exception as e:
    d = {}
    d['Error'] = str(e)
    return json.dumps( d )  
  
  time.sleep(5)
  return redirect(url_for("getStatus"))

@app.route('/api/v1/spirentTool/startScenarioWithoutStatus/', methods=['GET', 'POST'])
@auth.login_required
def startScenarioWithoutStatus():
  timeout = 15 # It needs additional time to Arm then Run
  cmd =b'RU'
  try:
    msg = telnet_to_spirent(cmd, spirent_simulator_HOST, spirent_simulator_PORT, timeout)
  except Exception as e:
    d = {}
    d['Error'] = str(e)
    return json.dumps( d )
  return 'OK'

@app.route('/api/v1/spirentTool/stopScenario/', methods=['GET', 'POST'])
@auth.login_required
def stopScenario():
  timeout = 3
  cmd =b'-,EN'
  try:
    msg = telnet_to_spirent(cmd, spirent_simulator_HOST, spirent_simulator_PORT, timeout)
  except Exception as e:
    d = {}
    d['Error'] = str(e)
    return json.dumps( d )  

  d = xmltodict.parse(msg)
  d['status description'] = status_dict[ int( d['msg']['status'] ) ]
  return json.dumps( d )

@app.route("/site-map", methods=['GET'])
def siteMap():
    data_dict = {}
    for rule in app.url_map.iter_rules():
      options = {}
      for arg in rule.arguments:
        options[arg] = "[{0}]".format(arg)
      methods = ','.join(rule.methods)
      url = url_for(rule.endpoint, **options)
      line = str("{:s} {:s} {:s}".format(url, rule.endpoint, methods))
      key =  str("{:s}".format(url))
      if len(key) > 4 and key[:4] == '/api':
        data_dict[ str("{:s}".format(url)) ] = str("{:s}".format(methods))
    host_url = request.host_url
    return render_template('site_map.html', data=data_dict, host=host_url[:-1])


if __name__ == "__main__":
  serve(app, host='0.0.0.0', port=web_port, threads=1) #WAITRESS!

