#!/usr/bin/env python

import xml.etree.cElementTree as ET
from glob import glob
import datetime
import time
import flask
from flask import Flask, render_template, request, send_from_directory, jsonify, after_this_request, make_response
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required
import json
import sqlite3
import pycurl
import requests
from io import BytesIO
import sys
import multiprocessing
import multiprocessing.connection as mp_con
from collections import OrderedDict
import re
import gzip, functools
import os
import logging
import schedule
import subprocess
import threading
import shlex

base_dir = '../../'
output_dir = base_dir + 'OutputResults/'
ServeFilePath = 'RFPlaybackRegression'
SamplesPath = ServeFilePath + '/Samples/'
DataPath = ServeFilePath + '/DataDir/'
OutputPath = ServeFilePath + '/OutputResults/'

run_weekly_regression = True

sys.path.append(base_dir)
from ProcessResultsStorage import get_db, read_configs, dRUN_TYPE, read_rx_descs, read_segs, read_signals, read_pos_types, read_partials

logging.basicConfig(level=logging.DEBUG)

# Get Spirent IP
config_XML = ET.parse(base_dir+"config.xml")
spirent_IP = config_XML.find('Spirent/IP').text
control_port = int(config_XML.find('control_port').text)
web_port = int(config_XML.find('web_addr').text.split(':')[-1])
web_notes = config_XML.find('web_notes').text

# Communicate with RunPlayback.py
msg_addr = ('localhost',control_port)

app = Flask(__name__)
LOG = flask.logging.create_logger(app)

# make sure OrderedDict() is honored by jsonify()
app.config["JSON_SORT_KEYS"] = False

app.config["SECRET_KEY"] = "SOME_SECRET"
login_manager = LoginManager(app)
login_manager.init_app(app)

def find_config_ID(all_config, desc):
    for conf_ID,(conf_filename,conf_desc) in all_config.items():
        if conf_desc == desc:
            return conf_ID
    return -1

def run_type_text_to_num(test_type):
    return dRUN_TYPE[test_type]


def find_pos_ID(all_pos_types, desc):
    for pos_ID,pos_desc in all_pos_types.items():
        if pos_desc == desc:
            return pos_ID
    return -1

def find_seg_ID(all_segs, desc):
    for seg_ID,seg_desc in all_segs.items():
        if seg_desc == desc:
            return seg_ID
    return -1

def find_partial_ID(all_partials, desc):
    for partial_ID,partial_desc in all_partials.items():
        if partial_desc == desc:
            return partial_ID
    return -1

class User(UserMixin):
    # proxy for a database of user:password
    user_database = {"Stinger": "StingerTeam"}

    def __init__(self, username, password):
        self.id = username
        self.name = username
        self.password = password

    @classmethod
    def get_valid_user(cls, username, password):
        try:
            if cls.user_database[username] == password:
                return cls(username, password)
        except:
            pass
        return None

    @classmethod
    def get(cls,name):
        return cls.user_database.get(name)

@login_manager.user_loader
def load_user(user_id):
    pw = User.get(user_id)
    if pw is None:
        return None
    else:
        return User(user_id,pw)

@app.route('/logout')
def logout():
    logout_user()
    return flask.redirect(flask.url_for('main'))

@app.route('/login', methods=['GET', 'POST'])
def login():
    try:
        username = request.form['Username']
        password = request.form['Password']
    except:
        return flask.render_template('login.html')

    user = User.get_valid_user(username,password)
    if user is not None:
        flask.session.permanent = True
        login_user(user)
        return flask.redirect(flask.url_for('main'))
    return flask.redirect(flask.url_for('login'))

def listGitBranches():
    """
    List all branches in the git repository.
    """
    branch_list = []
    try:
        p = subprocess.Popen(['bash','-c','cd ../../Builds/git-coreBuild; git branch -r'],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
        p.wait()
        out,err = p.communicate()
        branches = out.decode('utf-8').split('\n')
        for branch in branches:
            branch = branch.strip()
            if branch.startswith('origin/'):
                branch = branch[7:]
            if branch.startswith('HEAD -> '):
                continue
            # Skipping main here so we can put it at the front of the list
            if branch == 'main':
                continue
            if branch:
                branch_list.append(branch)
        branch_list = sorted(branch_list)
    except:
        LOG.warning("error listing git branches")

    branch_list.insert(0, 'main')
    return branch_list


@app.route("/showAgReceiverInfo")
def showAgReceiverInfo():
    ip = request.args.get('ip')
    content = msg_send_recv(['getAgReceiverInfo', ip])
    return make_response(json.dumps(content).replace(r'\u0000', ''))

@app.route("/stopAgReceiverLog")
def stopAgReceiverLog():
    ip = request.args.get('ip')
    content = msg_send_recv(['stopAgReceiverLog', ip])
    return make_response(json.dumps(content).replace(r'\u0000', ''))

def msg_send_recv( x ):
    result = {}
    try:
        client = mp_con.Client(msg_addr)
        client.send(x)
        result = client.recv()
        client.close()
    except:
        LOG.warning("msg_send_recv error {}".format(x))
        pass
    return result
def msg_send( x ):
    client = mp_con.Client(msg_addr)
    client.send(x)
    client.close()

def gzipped(f):
    @functools.wraps(f)
    def view_func(*args, **kwargs):
        @after_this_request
        def zipper(response):
            accept_encoding = request.headers.get('Accept-Encoding', '')

            if 'gzip' not in accept_encoding.lower():
                return response

            response.direct_passthrough = False

            if (response.status_code < 200 or
                response.status_code >= 300 or
                'Content-Encoding' in response.headers):
                return response
            gzip_buffer = BytesIO()
            gzip_file = gzip.GzipFile(mode='wb',
                                      compresslevel=1,
                                      fileobj=gzip_buffer)
            gzip_file.write(response.data)
            gzip_file.close()

            response.data = gzip_buffer.getvalue()
            response.headers['Content-Encoding'] = 'gzip'
            response.headers['Vary'] = 'Accept-Encoding'
            response.headers['Content-Length'] = len(response.data)

            return response

        return f(*args, **kwargs)

    return view_func

@app.route("/")
def main():
    user_status = msg_send_recv(['getUserInfo'])
    regression_status = msg_send_recv(['getRegressionInfo'])
    receiver_status = msg_send_recv(['getReceiverInfo'])

    if run_weekly_regression == True:
        weekly_run_str = "Weekly Regression Enabled"
    else:
        weekly_run_str = "Weekly Regression Disabled"

    return render_template("index.html",
                           web_notes=web_notes,
                           spirent_IP=spirent_IP,
                           url_root=request.url_root,
                           usage=user_status,
                           receiver=receiver_status,
                           regression=regression_status,
                           weekly_run=weekly_run_str)
    #return send_from_directory("static","index.html")

@app.route("/getUserInfo")
def getUserInfo():
    """Return the currently logged in user."""
    user_status = msg_send_recv(['getUserInfo'])
    return jsonify(user_status)

@app.route("/manual.html")
def manual():
    return render_template("manual.html",
                           url_root=request.url_root,
                           SamplesPath=SamplesPath
                          )

# render receivers top to bottom then left to right, assuming 2 columns.
def rearrange_devices(devs):
    n = len(devs)
    y = range(n)
    return [x for x,_ in sorted(zip(devs,y), key=lambda xy: [xy[1]%(n/2), xy[1]] )]

@app.route("/semi_manual.html")
def semi_manual():
    receiver_status = msg_send_recv(['getReceiverInfo'])
    receiver_status['devs'] = rearrange_devices(receiver_status['devs'])
    return render_template("semi_manual.html",
                           url_root=request.url_root,
                           SamplesPath=SamplesPath,
                           receiver=receiver_status,
                           branches = listGitBranches()
                          )

# given getTestFileInfoAll(), add 'last_date'/'last_dir' info to
# each test so we know when it last ran.
def mergeLatestRunInfo(tests):
    d = {}
    with get_db() as db:
        db_c = db.cursor()
        all_rx_descs = read_rx_descs(db_c)
        all_configs = read_configs(db_c)
        for config_ID,(config_filename,_) in all_configs.items():
            db_c.execute("""select * from LATEST_RUNS_SUMMARY
                          WHERE config_ID=?""",
                         (config_ID,))
            row = db_c.fetchone()
            if row == None: # Partial run has info in CONFIGS, but not in LATEST_RUNS_SUMMARY
                continue
            d[config_filename] = {}
            d[config_filename]['date'] = row['run_date']
            d[config_filename]['dir'] = 'RX%d-%d'%(row['rx_num'],row['run_num'])
    for test in tests:
        k = test['filename']
        try:
            test['last_date'] = d[k]['date']
            test['last_dir'] = d[k]['dir']
        except:
            pass

@app.route("/getTestFileInfoAll")
def getTestFileInfoAll():
    t1 = time.time()
    tests = msg_send_recv(['getTestFileInfoAll'])
    LOG.info('getTestFileInfoAll %.1f' % (time.time() - t1))
    t1 = time.time()
    mergeLatestRunInfo( tests['tests'] )
    LOG.info('mergeLatestRunInfo %.1f' % (time.time() - t1))
    return jsonify(tests)

@app.route("/regression.html")
def regression():
    status = msg_send_recv(['getRegressionInfo'])
    progress = msg_send_recv(['getRegressionProgress'])
    return render_template("regression.html", status=status, progress=progress)


def form_results(filename):
    raw = ET.parse(output_dir+filename).getroot()
    d = []
    for tst in raw.findall('test'):
        has_All = False
        segs = []
        for s in tst.findall('segment'):
            if s.text == 'All':
                has_All = True
            else:
                segs.append(s.text)
        segs.sort()
        if has_All:
            segs.insert(0,'All')
        d.append( (tst.find('desc').text, segs) )
    d.sort()
    d = [{'desc':x, 'segment':y} for x,y in d]
    info = OrderedDict({'data':OrderedDict()})
    info['data']['test'] = d
    return info

def get_run_summary(run_type_text):
    with get_db() as db:
        db_c = db.cursor()
        all_configs = read_configs(db_c)
        all_pos_types = read_pos_types(db_c)
        all_segs = read_segs(db_c)
        run_type = run_type_text_to_num(run_type_text)
        db_c.execute("select * from ALL_RUNS_POS_SUMMARY where run_type=?",(run_type,))
        data = {}
        for row in db_c.fetchall():
            config_desc = all_configs[row['config_ID']][1]
            data.setdefault(config_desc,{})
            data[config_desc].setdefault('segment',{})
            data[config_desc].setdefault('fixtype',{})
            fix_desc = all_pos_types[row['pos_ID']]
            seg_desc = all_segs[row['seg_ID']]
            data[config_desc]['segment'][seg_desc] = 1
            data[config_desc]['fixtype'][fix_desc] = 1
    data_sorted = OrderedDict({})
    for key in sorted(data.keys()):
        data_sorted[key] = data[key]
    return data_sorted

@app.route("/results.html")
def results():
    info = {}
    info['test_type'] = 'SBAS'
    info['CDF'] = request.args.get('CDF')
    info['desc'] = request.args.get('desc')
    info['fixtype'] = request.args.get('fixtype')
    info['segtype'] = request.args.get('segtype')
    info['partialrun'] = request.args.get('partialrun')
    info['tests'] = get_run_summary(info['test_type'])
    return render_template("results.html",
                           info=info,
                           url_root=request.url_root,
                           DataPath=DataPath,
                           OutputPath=OutputPath
                          )

@app.route("/rtk_results.html")
def rtk_results():
    info = {}
    info['test_type'] = 'RTK'
    info['CDF'] = request.args.get('CDF')
    info['desc'] = request.args.get('desc')
    info['fixtype'] = request.args.get('fixtype')
    info['segtype'] = request.args.get('segtype')
    info['partialrun'] = request.args.get('partialrun')
    info['tests'] = get_run_summary(info['test_type'])
    return render_template("results.html",
                           info=info,
                           url_root=request.url_root,
                           DataPath=DataPath,
                           OutputPath=OutputPath)

@app.route("/rtx_results.html")
def rtx_results():
    info = {}
    info['test_type'] = 'RTX'
    info['CDF'] = request.args.get('CDF')
    info['desc'] = request.args.get('desc')
    info['fixtype'] = request.args.get('fixtype')
    info['segtype'] = request.args.get('segtype')
    info['partialrun'] = request.args.get('partialrun')
    info['tests'] = get_run_summary(info['test_type'])
    return render_template("results.html",
                           info=info,
                           url_root=request.url_root,
                           DataPath=DataPath,
                           OutputPath=OutputPath)

@app.route("/dgnss_results.html")
def dgnss_results():
    info = {}
    info['test_type'] = 'DGNSS'
    info['CDF'] = request.args.get('CDF')
    info['desc'] = request.args.get('desc')
    info['fixtype'] = request.args.get('fixtype')
    info['segtype'] = request.args.get('segtype')
    info['partialrun'] = request.args.get('partialrun')
    info['tests'] = get_run_summary(info['test_type'])
    return render_template("results.html",
                           info=info,
                           url_root=request.url_root,
                           DataPath=DataPath,
                           OutputPath=OutputPath)

@app.route("/nopi_results.html")
def nopi_results():
    info = {}
    info['desc'] = request.args.get('desc')
    # TODO: fill in all_nopi_tests.json data
    with get_db() as db:
        db_c = db.cursor()
        all_configs = read_configs(db_c)
        db_c.execute("select * from ALL_RUNS_NOPI_SUMMARY")
        info['tests'] = {}
        for row in db_c.fetchall():
            config_desc = all_configs[row['config_ID']][1]
            info['tests'].setdefault(config_desc,{})
    return render_template("nopi_results.html",
                           info=info,
                           url_root=request.url_root,
                           OutputPath=OutputPath
                          )

@app.route("/ag_results.html")
def ag_results():
    info = {}
    info['test_type'] = 'AG'
    info['CRT'] = request.args.get('CRT')
    info['desc'] = request.args.get('desc')
    info['fixtype'] = request.args.get('fixtype')
    info['segtype'] = request.args.get('segtype')
    info['partialrun'] = request.args.get('partialrun')
    info['tests'] = get_run_summary(info['test_type'])
    return render_template("ag_results.html",
                           info=info,
                           url_root=request.url_root,
                           DataPath=DataPath,
                           OutputPath=OutputPath)


@app.route("/getResults", methods=['GET','POST'])
@gzipped
def getResults():
    """Each element in all_data.json is for a single receiver and a single test.
    The web wants data for a single test but _all_ receivers.  This function
    goes through the data and formats it for web display.
    """
    all_d = []
    want_test_type = request.form['test_type']
    want_fixtype = request.form['fixtype']
    want_seg = request.form['segtype']
    want_desc = request.form['desc']
    want_cdf = int(request.form['cdf'])
    want_partialrun = request.form['partialrun']
    t1 = time.time()
    n_rec = 0

    with get_db() as db:
        db_c = db.cursor()
        all_config = read_configs(db_c)
        all_pos_types = read_pos_types(db_c)
        all_rx_descs = read_rx_descs(db_c)
        all_segs = read_segs(db_c)
        all_partials = read_partials(db_c)
        config_ID = find_config_ID(all_config, want_desc)
        pos_ID = find_pos_ID(all_pos_types, want_fixtype)
        seg_ID = find_seg_ID(all_segs, want_seg)
        partial_ID = find_partial_ID(all_partials, want_partialrun)
        db_c.execute("""select * from %s_POS_RESULTS
                      where config_ID=? and pos_ID=? and seg_ID=? and partial_ID=?
                      order by run_date"""%want_test_type,
                     (config_ID,pos_ID,seg_ID,partial_ID))

        d = {}
        for row in db_c.fetchall():
            n_rec += 1
            rx_ID = row['rx_desc_ID']
            rx_num = row['rx_num']
            rx_desc = all_rx_descs[rx_ID]
            unit_desc = '%d: %s' % (rx_num,rx_desc)
            if unit_desc in d:
                d_unit = d[unit_desc]
            else:
                d[unit_desc] = {}
                d_unit = d[unit_desc]
                d_unit['text'] = []
                d_unit['x'] = []
                d_unit['marker'] = []
            # Normally we try to avoid repeated 'run_date' entries
            # because they are harder to visualize.  But.. it's better
            # to display the data than drop it completely.
            if True: #row['run_date'] not in d_unit['x']:
                d_unit['x'].append( row['run_date'] )
                d_unit['text'].append( 'RX%d-%d'%(rx_num,row['run_num']) )
                if row['custom_fw']:
                    d_unit['marker'].append( 'cross' )
                else:
                    d_unit['marker'].append( 'circle' )
            d_unit.setdefault('segment',OrderedDict())
            d_unit['segment'].setdefault('n_err',[])
            d_unit['segment'].setdefault('errors',[])
            d_unit['segment'].setdefault('warnings',[])

            seg_name = all_segs[row['seg_ID']]
            d_unit['segment']['n_err'].append( -1 )
            d_unit['segment']['errors'].append( [] )
            d_unit['segment']['warnings'].append( [] )

            num_pos = row['num_pos']
            if num_pos is None:
                num_pos = 0.1 # so it shows up on a log-scale graph
            d_unit['segment'].setdefault('len',[]).append(num_pos)

            cdf_1d = row['cdf_1d_%d'%want_cdf]
            cdf_2d = row['cdf_2d_%d'%want_cdf]
            cdf_3d = row['cdf_3d_%d'%want_cdf]
            d_unit['segment'].setdefault('1d',[])
            d_unit['segment'].setdefault('2d',[])
            d_unit['segment'].setdefault('3d',[])
            d_unit['segment']['1d'].append(cdf_1d)
            d_unit['segment']['2d'].append(cdf_2d)
            d_unit['segment']['3d'].append(cdf_3d)
        LOG.info('getResults1 time %.2f' % (time.time() - t1))

        db_c.execute('select * from ERR_LOGS where config_ID=?',
                     (config_ID,))
        for row in db_c.fetchall():
            rx_ID = row['rx_desc_ID']
            rx_num = row['rx_num']
            rx_desc = all_rx_descs[rx_ID]
            unit_desc = '%d: %s' % (rx_num,rx_desc)
            if unit_desc not in d:
                continue
            d_unit = d[unit_desc]
            try:
                idx = d_unit['x'].index(row['run_date'])
                d_unit['segment']['n_err'][idx] = row['n_logs']
                d_unit['segment']['errors'][idx] = json.loads(row['err_str'])
                d_unit['segment']['warnings'][idx] = json.loads(row['warn_str'])
            except:
                pass
    LOG.info('getResults2 time %.2f len %d' % (time.time() - t1,n_rec))

    return jsonify(d)


@app.route("/getAgResults", methods=['GET','POST'])
@gzipped
def getAgResults():
    """Each element in all_data.json is for a single receiver and a single test.
    The web wants data for a single test but _all_ receivers.  This function
    goes through the data and formats it for web display.
    """
    all_d = []
    want_test_type = request.form['test_type']
    want_fixtype = request.form['fixtype']
    want_seg = request.form['segtype']
    want_desc = request.form['desc']
    # want_cdf = int(request.form['cdf']) # FIXME
    want_crt = request.form['crt']
    want_partialrun = request.form['partialrun']
    t1 = time.time()
    n_rec = 0

    with get_db() as db:
        db_c = db.cursor()
        all_config = read_configs(db_c)
        all_pos_types = read_pos_types(db_c)
        all_rx_descs = read_rx_descs(db_c)
        all_segs = read_segs(db_c)
        all_partials = read_partials(db_c)
        config_ID = find_config_ID(all_config, want_desc)
        pos_ID = find_pos_ID(all_pos_types, want_fixtype)
        seg_ID = find_seg_ID(all_segs, want_seg)
        partial_ID = find_partial_ID(all_partials, want_partialrun)
        db_c.execute("""select * from %s_POS_RESULTS
                      where config_ID=? and pos_ID=? and seg_ID=? and partial_ID=?
                      order by run_date"""%want_test_type,
                     (config_ID,pos_ID,seg_ID,partial_ID))

        d = {}
        for row in db_c.fetchall():
            n_rec += 1
            rx_ID = row['rx_desc_ID']
            rx_num = row['rx_num']
            rx_desc = all_rx_descs[rx_ID]
            unit_desc = '%d: %s' % (rx_num,rx_desc)
            if unit_desc in d:
                d_unit = d[unit_desc]
            else:
                d[unit_desc] = {}
                d_unit = d[unit_desc]
                d_unit['text'] = []
                d_unit['x'] = []
                d_unit['marker'] = []
            # Normally we try to avoid repeated 'run_date' entries
            # because they are harder to visualize.  But.. it's better
            # to display the data than drop it completely.
            if True: #row['run_date'] not in d_unit['x']:
                d_unit['x'].append( row['run_date'] )
                d_unit['text'].append( 'RX%d-%d'%(rx_num,row['run_num']) )
                if row['custom_fw']:
                    d_unit['marker'].append( 'cross' )
                else:
                    d_unit['marker'].append( 'circle' )
            d_unit.setdefault('segment',OrderedDict())
            d_unit['segment'].setdefault('n_err',[])
            d_unit['segment'].setdefault('errors',[])
            d_unit['segment'].setdefault('warnings',[])

            seg_name = all_segs[row['seg_ID']]
            d_unit['segment']['n_err'].append( -1 )
            d_unit['segment']['errors'].append( [] )
            d_unit['segment']['warnings'].append( [] )

            num_pos = row['num_pos']
            if num_pos is None:
                num_pos = 0.1 # so it shows up on a log-scale graph
            d_unit['segment'].setdefault('len',[]).append(num_pos)

            crt_2d = row['above_%s'%want_crt]
            # cdf_3d = row['cdf_3d_%d'%want_cdf]
            d_unit['segment'].setdefault('2d',[])
            # d_unit['segment'].setdefault('3d',[])
            d_unit['segment']['2d'].append(crt_2d)
            # d_unit['segment']['3d'].append(cdf_3d)
        LOG.info('getResults1 time %.2f' % (time.time() - t1))

        db_c.execute('select * from ERR_LOGS where config_ID=?',
                     (config_ID,))
        for row in db_c.fetchall():
            rx_ID = row['rx_desc_ID']
            rx_num = row['rx_num']
            rx_desc = all_rx_descs[rx_ID]
            unit_desc = '%d: %s' % (rx_num,rx_desc)
            if unit_desc not in d:
                continue
            d_unit = d[unit_desc]
            try:
                idx = d_unit['x'].index(row['run_date'])
                d_unit['segment']['n_err'][idx] = row['n_logs']
                d_unit['segment']['errors'][idx] = json.loads(row['err_str'])
                d_unit['segment']['warnings'][idx] = json.loads(row['warn_str'])
            except:
                pass
    LOG.info('getResults2 time %.2f len %d' % (time.time() - t1,n_rec))

    return jsonify(d)

@app.route("/getNoPiResults", methods=['GET','POST'])
@gzipped
def getNoPiResults():
    all_d = []
    want_desc = request.form['desc']
    t1 = time.time()
    with get_db() as db:
        db_c = db.cursor()
        all_config = read_configs(db_c)
        all_rx_descs = read_rx_descs(db_c)
        all_signals = read_signals(db_c)
        config_ID = find_config_ID(all_config, want_desc)
        db_c.execute("""select * from NOPI_RESULTS
                      where config_ID=? and num_meas!=0
                      order by run_date""",
                     (config_ID,))
        d = {}
        for row in db_c.fetchall():
            rx_ID = row['rx_desc_ID']
            rx_num = row['rx_num']
            rx_desc = all_rx_descs[rx_ID]
            unit_desc = '%d: %s' % (rx_num,rx_desc)
            if unit_desc in d:
                d_unit = d[unit_desc]
            else:
                d[unit_desc] = {}
                d_unit = d[unit_desc]
                d_unit['text'] = []
                d_unit['x'] = []
            if row['run_date'] not in d_unit['x']:
                d_unit.setdefault('x', [])
                d_unit.setdefault('text', [])
                d_unit['x'].append( row['run_date'] )
                d_unit['text'].append( 'RX%d-%d'%(rx_num,row['run_num']) )

            d_unit.setdefault('nopi',OrderedDict())
            sig_name = all_signals[row['signal_ID']]
            d_unit['nopi'].setdefault(sig_name,OrderedDict())
            d_unit['nopi'][sig_name].setdefault('length',[]).append( row["num_meas"] )
            d_unit['nopi'][sig_name].setdefault('car',[]).append( row["carr_std"] )
            d_unit['nopi'][sig_name].setdefault('code',[]).append( row["code_std"] )
    LOG.info('getNoPiResults time %.1f' % (time.time() - t1))
    return jsonify(d)

@app.route("/getRegressionDetail")
def getRegressionDetail():
    d = {}
    d.update( msg_send_recv(['getRegressionInfo']) )
    d.update( msg_send_recv(['getRegressionProgress']) )
    return jsonify(d)

@app.route("/getRegressionDiag")
def getRegressionDiag():
    return msg_send_recv(['getRegressionDiag'])

@app.route("/putUserRequest", methods=['POST'])
@login_required
def putUserRequest():
    d = {}
    d['name'] = request.form['name']
    d['desc'] = request.form['desc']
    seconds_left = 0
    try:
        seconds_left = int(request.form['hours'])*60*60
    except:
        pass
    d['end_time'] = time.strftime("%a, %d %b %Y %H:%M:%S",
                                  time.localtime(time.time()+seconds_left))
    msg_send(['userInfo',d])
    return "This is some response.  Hello " + json.dumps(d)

@app.route("/getUserStatus")
def getUserStatus():
    d = msg_send_recv(['getUserInfo'])
    return jsonify(d)

@app.route("/getPlaybackStatus")
def getPlaybackStatus():
    # Note - if we want to do this directly from index.html with AJAX, then
    # we'd need to figure out how to turn off the cross-domain security limit.
    # buffer = BytesIO()
    # c = pycurl.Curl()
    # c.setopt(c.URL, 'http://{}/shm_get.shtml?-m'.format(spirent_IP))
    # c.setopt(c.WRITEDATA, buffer)
    # c.perform()
    # c.close()
    # return buffer.getvalue().decode().replace("-m","")

    url_str = 'http://{}/shm_get.shtml?-m'.format(spirent_IP)
    r = requests.get(url_str, timeout=10)
    return r.text.replace("-m","")

@app.route("/putSerialToggle", methods=['POST'])
@login_required
def putSerialToggle():
    msg_send(['toggleSerial'])
    return "OK"

@app.route("/putMountUsbToggle", methods=['POST'])
@login_required
def putMountUsbToggle():
    msg_send_recv(['mountUsbToggle'])
    return "OK"

@app.route("/getSerialToggle")
def getSerialToggle():
    return jsonify( msg_send_recv(['getSerialInfo']) )

@app.route("/getMountStatus")
def getMountStatus():
    return jsonify( msg_send_recv(['getMountUsbStatus']) )

@app.route("/putRegression", methods=['POST'])
@login_required
def putRegression():
    d = msg_send_recv(['getRegressionInfo'])
    if request.form['action']=="start" and d['status'] != "Running":
        msg_send(['start'])
    elif request.form['action']=="kill" and d['status'] == "Running":
        msg_send(['kill'])
    return "OK"

@app.route("/putRegressionList", methods=['POST'])
@login_required
def putRegressionList():
    d = msg_send_recv(['getRegressionInfo'])
    receiver_status = msg_send_recv(['getReceiverInfo'])
    if d['status'] != "Running":
        file_list = ','.join(request.form.getlist('filename')) + ','
        try:
            n_loop = int(request.form['n_loop'])
            if n_loop < 0:
                raise ValueError
        except ValueError:
            return "Invalid # of run"
        if 'skip_build' in request.form:
            skip_build = True
        else:
            skip_build = False
        run_rtx = True if 'run_rtx' in request.form else False
        run_both = True if 'run_both' in request.form else False
        skip_post = True if 'skip_post' in request.form else False
        build_branch = request.form['build-branch'] if 'build-branch' in request.form else 'main'
        if 'DoSubset' in request.form and bool(request.form['DoSubset']):
            do_subset = True
            start_time0 = None
            try:
                start_time0 = int(request.form['StartTime0'])
            except ValueError:
                return "Invalid StartTime from scn/xml"
            try:
                start_time = int(request.form['StartTime']) - start_time0
            except ValueError:
                return "Invalid StartTime"
            try:
                stop_time = int(request.form['StopTime']) - start_time0
            except ValueError:
                return "Invalid StopTime"
            if start_time < 0:
                return "Start Time needs > "+str(start_time0)
            if stop_time - start_time <= 5*60:
                return "Time Range needs > 5 mins"
        else:
            do_subset = False
            start_time = -1
            stop_time = -1
        LOG.info(do_subset)
        LOG.info('start_time')
        LOG.info(start_time)
        LOG.info('stop_time')
        LOG.info(stop_time)
        if 'selected_devices' in request.form:
            selected_devices = [int(x) for x in request.form.getlist('selected_devices')]
        else:
            selected_devices = []
        deselected_devices = []
        for rcvr in receiver_status['devs']:
            if rcvr['n'] not in selected_devices:
                deselected_devices.append( rcvr['n'] )
        #LOG.info(file_list,n_loop,skip_build)
        msg_send(['playList',file_list,n_loop,skip_build,deselected_devices,do_subset,start_time,stop_time,run_rtx,run_both,skip_post,build_branch])
    return "OK"

@app.route("/playSingle", methods=['POST'])
@login_required
def playSingle():
    msg_send(['playSingle',request.form['filename']])
    return "OK"

@app.route("/copyAndPlaySingle", methods=['POST'])
@login_required
def copyAndPlaySingle():
    msg_send(['copyAndPlaySingle',request.form['filename']])
    return "OK"

@app.route("/cancelCopy", methods=['POST'])
@login_required
def cancelCopy():
    msg_send(['cancelCopy'])
    return "OK"

@app.route("/copyList", methods=['POST'])
@login_required
def copyList():
    msg_send(['copyList',request.form['filename']])
    return "OK"

@app.route("/disableWeeklyRun", methods=['GET'])
def disableWeeklyRun():
    global run_weekly_regression
    run_weekly_regression = False
    return jsonify({'run_enabled': run_weekly_regression})

@app.route("/enableWeeklyRun", methods=['GET'])
def enableWeeklyRun():
    global run_weekly_regression
    run_weekly_regression = True
    return jsonify({'run_enabled': run_weekly_regression})

@app.route("/weeklyRunEnabled", methods=['GET'])
def weeklyRunEnabled():
    return jsonify({'run_enabled': run_weekly_regression})

@app.route("/getDetailedDiag", methods=['GET'])
def getDetailedDiag():
    diag = ""
    d = msg_send_recv(['getDetailedDiag'])
    if d: # if we have a diag command, run it
        d_split = shlex.split(d, posix=False)
        d_split = [string.replace('"','') for string in d_split]
        diag = subprocess.check_output(d_split)
    return diag

@app.route('/%s'%ServeFilePath, defaults={'req_path': ''})
@app.route('/%s/'%ServeFilePath, defaults={'req_path': ''})
@app.route('/%s/<path:req_path>'%ServeFilePath)
def dir_listing(req_path):
    # Joining the base and the requested path
    abs_path = os.path.join(base_dir, req_path)

    # Return 404 if path doesn't exist
    if not os.path.exists(abs_path):
        return flask.abort(404)

    # Check if path is a file and serve
    if os.path.isfile(abs_path):
        return flask.send_file(abs_path)

    def sizeof_fmt(num, suffix='B'):
        for unit in ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']:
            if abs(num) < 1024.0:
                return "%3.1f%s%s" % (num, unit, suffix)
            num /= 1024.0
        return "%.1f%s%s" % (num, 'Yi', suffix)

    # Show directory contents
    files = []
    for filename in sorted(os.listdir(abs_path)):
        if abs_path.endswith('/'):
            abs_filename = abs_path + filename
        else:
            abs_filename = abs_path + '/' + filename
        curr_file = {}
        if os.path.isfile(abs_filename):
            curr_file['type'] = 'text'
            curr_file['name'] = filename
        else:
            curr_file['type'] = 'folder'
            curr_file['name'] = filename
            if not curr_file['name'].endswith('/'):
                curr_file['name'] += '/'
        curr_file['time'] = time.ctime(os.path.getmtime(abs_filename))
        curr_file['size'] = sizeof_fmt(os.path.getsize(abs_filename))
        files.append(curr_file)
    return render_template('files.html',
                           path=os.path.abspath(abs_path),
                           url_root=request.url_root,
                           base_url=request.base_url,
                           files=files)


def start_full_regression():
    if run_weekly_regression:
        msg_addr = ('localhost',5001)
        mp_con.Client(msg_addr).send(['start'])
        mp_con.Client(msg_addr).send(['logSSDWearLevelingCount'])


def schedule_regression():
    schedule.every().friday.at("16:00").do(start_full_regression)
    while True:
        schedule.run_pending()
        time.sleep(1)


if __name__ == "__main__":
    threading.Thread(target=schedule_regression).start()
    app.run(host='0.0.0.0',port=web_port,debug=False,threaded=True)
