#!/usr/bin/env python
"""
`run_pre_deployment_simulations.py`
===================================

"""
import time
import os
import subprocess

try:
    from ConfigParser import SafeConfigParser # for python 2.x
except:
    from configparser import SafeConfigParser # for python 3+

from datetime import datetime

# code snippet from stackoverflow to filter non-printable characters, but allow unicode http://stackoverflow.com/questions/92438/stripping-non-printable-characters-from-a-string-in-python
#printable = Set('Lu', 'Ll', ...)
#def filter_non_printable(str):
#  return ''.join(c for c in str if unicodata.category(c) in printable)

def mission_end_message(msg):
    """Test a string to see if it is characteristic of a mission end."""
    smsg = msg.replace('\x08','').split(' ',1)[-1].strip().split(' ',1)[-1]
    endings = [ '[MissionManager](IMPORTANT): Started mission Default', ]
    if smsg in endings: return True
    else: return False


def execute_mission(mission_script, set_args = None, verbose = 0, **kwargs):
    """Simulate a LRAUV mission.

    Use set_args variable to pass `set` commands to the LRAUV application.

    """
    lcmd = 'nice ./bin/LRAUV -x"{} run"'
    largs = ['load {0}'.format(mission_script)]
    if set_args is not None:
        set_args = set_args.replace('\n',' ') # handle multi-line args
        set_args = set_args.strip(';') # get rid of leading or trailing ;
        print 'running mission with additional arguments:', set_args
        for a in set_args.split(';'): 
            print 'setting: ', a.strip()
            largs.append('set {0}'.format(a.strip()))
    largstr = ';'.join(largs)
    print largstr
    #TODO any other kind of input args... 
    
    p = subprocess.Popen('nice ./bin/LRAUV -x "{0}; run"'.format(largstr),
        shell=True, #TODO get it to work false
        executable = '/bin/bash', #TODO portable
        cwd = '../..', #TODO portable 
        stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    
    while True:
        stdout = p.stdout.readline().strip()
        if verbose: print stdout
        if mission_end_message(stdout):
            p.stdin.write('quit\n')
            # p.terminate()
            break
        retcode = p.poll()
        # print retcode # 127 means that bash probably couldn't execute it...
        if retcode: break 
        #TODO some kind of timeout, rely on <CTRL-C> for now
    return 0


def unpack_logs(verbose = 0, **kwargs):
    """Unpack Log files from a LRAUV mission.

    """
    #TODO standard lists of data for science and turbulence users
    print subprocess.call('nice ./bin/unserialize -n -k -v SIM ./Logs/latest/slate', 
        #TODO as list
        shell=True, #TODO get it to work false
        executable = '/bin/bash', #TODO portable
        cwd = '../..', #TODO portable 
        )


def publish_logs(publish_host = None, publish_user = None, publish_path = None, mission_path = None, verbose=0, **kwargs):
    """Publish the logs to a folder on a host (maybe web accessible."""
    clstr = 'nice ssh {0}@{1} "mkdir -p {2}/{3}"'
    cl = clstr.format(publish_user, publish_host, publish_path, mission_path)
    mkdirp = subprocess.call(cl, shell=True)

    clstr = 'nice rsync -avPhv --delete --include=syslog --include=*.km* --exclude=* ./ {0}@{1}:{2}/{3}/' 
    cl = clstr.format(publish_user, publish_host, publish_path, mission_path)
    latestlogpath = os.path.join('..','..','Logs','latest')

    return subprocess.call(cl, shell=True, cwd=latestlogpath)


def svn_update(directory = '../..'):
    return subprocess.call('svn up', shell=True, cwd=directory)


def make(directory = '../..'):
    return subprocess.call('make', shell=True, cwd=directory)


def nuke_logs():
    return subprocess.call('rm -r 201*', shell=True, cwd = '../../Logs')


def main(inifile = None, verbose = 0):
    """Run the simulations."""

    # TODO make each of the following steps optional using args
    nuke_logs() # to avoid tons of sbd at startup
    svn_update() # make sure you are using the latest code
    make() # build the app if you haven't already

    parser = SafeConfigParser()
    try: parser.read(inifile.name)
    except: parser.read(inifile) # maybe it's just the filename, not a handle
    if verbose: print parser.sections()
    
    for section in parser.sections():
        if verbose: print 'running simulation section:', section
        kwargs = dict(parser.items(section))
        # TODO: Ensure SimDaemon is running locally...
        erc = execute_mission(**kwargs)
        # could also unpack all args... execute_mission(parser.get(section,'mission_script'))
        unpack_logs(**kwargs)
        mission_path = kwargs['mission_script'][:-4]
        publish_logs(mission_path = mission_path, **kwargs)


if __name__ == "__main__":
    import argparse
    # instantiate parser
    parser = argparse.ArgumentParser(description='run simulations of standard missions from ../Missions, unpack resulting logs, and post kmz files.', 
        prefix_chars='-+')
    # add positional arguments
    # parser.add_argument('echo', help='echo the string you use here')
    # add optional arguments
    parser.add_argument('-V', '--version', action='version', 
        version='%(prog)s 0.0.1',
        help='display version information and exit')
    parser.add_argument('-v', '--verbose', action='count', default=0,
        help='display verbose output')
    parser.add_argument('-i', '--inifile', metavar='filename', 
        type=argparse.FileType('rt'), 
        default='standard_missions.ini',
        help='configuration file (default standard_missions.ini)')
    args = parser.parse_args()
    main(**args.__dict__) #TODO more pythonic?
