#!/bin/python
"""
usage: nemo-test-suite [options] base-runid

Setup and optionally compile/run a test-suite of the seven
CCC NEMO configs with five experiments each.

The runids for each config will start with the three 
letter ``base-runid``. Separate directories for each config 
will be created using the full config name. The runids for each
individual config will be the base-runid 
appended with abbreviations of the config name 
(e.g. o025, o1, o1p,  o2c), where the number refers to the 
resolution, "p" is pisces, and "c" is cmoc. 

Use nemo-test-suite -h to see a full list of options.

Examples:
-------
1. A basic test of the origin repo configs, including compile
   and run for runid nti:

    cd ~/PRODUCTION/nti
    nemo-test-suite nti

2. Setup tests from a custom repo, without compiling or running, for nti.

    cd nti
    nemo-test-suite --repo=/users/tor/acrn/ncs/nemo_repo_2 --ver=develop
    --save=off --compile=off --run=off nti

HISTORY:
--------

Nov 2015  Neil Swart  Update to handle multiple experiments and more
                      CCC configurations

Nov 2014  Neil Swart  Original code
"""
import os, glob, re, subprocess, time, sys
from optparse import OptionParser

def nemo_test_suite():
# Get the runid argument from the command line.
    usage="usage: %prog [options] runid"
    usage, description, epilog = il = __doc__.split('\n\n')[:3]

    parser = OptionParser(usage=usage, description=description,
    epilog=epilog)

#    parser.add_option("--base-runid", dest="runid", type=str,
#                      help="The three letter runid")
    parser.add_option("--repo", dest="repo", type=str,
                      help="path/to/your/nemo-CCCma-repo.git",
                      default='$CCRNSRC/ocean_dir/nemo-CCCma.git')
    parser.add_option("--ver", dest="ver", type=str,
                      help="branch or tag to use in the repo",
                      default='master')
    parser.add_option("--save", dest="save", type=str,
                      help="Save namelists to runpath if --save=on",
                      default='on')
    parser.add_option("--machine", dest="machine", type=str,
                      help="The machine to run on, either spica or hadar",
                      default='spica')
    parser.add_option("--compile", dest="compile", type=str,
                      help="If compile=on, the code is compiled.",
                      default='on')
    parser.add_option("--run", dest="run", type=str,
                      help="If run=on, the job is created and \
                      submitted to machine.",
                      default='on')
    parser.add_option("--configs", dest="ccc_configs", type=str,
                      help="Space separated list of CCC configs to test\
                      e.g. --configs='CCC_ORCA1_LIM \
                      CCC_ORCA2_LIM-PISCES'",
                      default="CCC_ORCA025_LIM CCC_ORCA1_LIM \
                      CCC_ORCA1_LIM_PISCES CCC_ORCA2_LIM_PISCES \
                      CCC_ORCA2_LIM_CMOC")
    parser.add_option("--exps", dest="exps", type=str,
                      help="Space separated list of experiments to test\
                      e.g. --exps='EXP_BULK_CF_COREv2\
                       EXP_BULK_IAF_CanESM2_HIST'",
                      default="EXP_BULK_CF_COREv2 EXP_BULK_IAF_COREv2 \
                               EXP_BULK_IAF_CanESM2_PICNTRL \
                               EXP_BULK_IAF_CanESM2_HIST \
                               EXP_FLUX_IAF_CanESM2_PICNTRL")

# Make sure that we are getting a sensible base-runid as the positional
# arg
    try: 
        (options, args) = parser.parse_args()
        if args:
            runid = args[0]
            if ( not runid.isalpha() ) | (len( runid) > 8)  :
                parser.error("base-runid must be a string")
            options.runid=runid
        else:
            parser.error("Three letter base-runid argument is required.")
    except ValueError:
        parser.error("Three letter base-runid argument is required.")

# Define the CCC configuration names and their abbreviations
    ccc_configs = list( options.ccc_configs.split() )

    def config_abb(name):
        """Get an abbreviation from the full config name"""
        return {
            'CCC_ORCA025_LIM' : 'o025', 
            'CCC_ORCA1_LIM' : 'o1', 
            'CCC_ORCA1_LIM_PISCES' : 'o1p', 
            'CCC_ORCA1_LIM_CMOC' : 'o1c', 
            'CCC_ORCA2_LIM' : 'o2',
            'CCC_ORCA2_LIM_PISCES' : 'o2p', 
            'CCC_ORCA2_LIM_CMOC' : 'o2c'
        }[name]

# Define the experiment names and their abbreviations
    exps = list( options.exps.split() )
    def exp_abb(name):
        """Get an abbreviation from the full experiment name"""
        return {
            'EXP00' : 'cv2', 
            'EXP_BULK_CF_COREv2' : 'cv2', 
            'EXP_BULK_IAF_COREv2' : 'cv2iaf', 
            'EXP_BULK_IAF_CanESM2_PICNTRL' : 'canbpi', 
            'EXP_BULK_IAF_CanESM2_HIST' : 'canbhist', 
            'EXP_FLUX_IAF_CanESM2_PICNTRL' : 'canfpi', 
        }[name]


    cwd = os.getcwd()

    for name in  ccc_configs:
        print name
        
        abb = config_abb(name)
        if not os.path.isdir(cwd + '/' + name):
            subprocess.Popen( ['mkdir', cwd + '/' + name]).wait()
        
        os.chdir( cwd + '/' + name)
        
        for ex in  exps:
            print ex
            abb_ex = exp_abb(ex)
            if not os.path.isdir(cwd + '/' + name + '/' + ex):
                subprocess.Popen( ['mkdir', cwd + '/' + name + '/' + ex]).wait()
        
            os.chdir( cwd + '/' + name + '/' + ex)
           
            # run setup-nemo
            subprocess.Popen(['setup-nemo', 
                              'repo=' + options.repo, 
                              'ver=' + options.ver, 
                              'runid=' + options.runid + '-' + abb + '-' + abb_ex,
                              'config=' + name,
                              'exp=' + ex,
                              'save=' + options.save
                             ]).wait()
            
            # read the compile script and exectute it if compile=on
            if options.compile=='on':
                print 'compiling job...'
                infile = (cwd + '/' + name + '/' + ex + "/compile_" 
                          + options.runid + "-" + abb + '-' + abb_ex)
                with open(infile, 'r') as f:
                    es = f.read()
              
                es = list( es.split() )
                subprocess.Popen(es).wait()

            if (options.run=='on'): #& (options.compile=='on'):
                print 'editing makefile, building job string and running job...'
                makefilename = (cwd + '/' + name + '/' + ex + "/make_orca_job_" 
                                + options.runid + "-" + abb + '-' + abb_ex)
                
               #Edit the make file to adjust revelevant parameters:
                makefile_adjustments = {#r'stop=0100' : 'stop=010', 
                                        r'nemo_dump_hist=on' : 'nemo_dump_hist=off',
                                        r'nemo_dump_rs=on' : 'nemo_dump_rs=off',
                                       }
                #makefile_adjustments = {}


                # Read the makefile into memory, adjusting lines as we go 
                lines = []
                with open(makefilename, "r") as infile:
                        for line in infile:
                            for src, target in makefile_adjustments.iteritems(): 
                                line = re.sub(src, target, line)
                            lines.append(line)

                # Overwrite the file from memory                
                with  open(makefilename, "w") as outfile:
                    for line in lines:
                        outfile.write(line)

                # run the make script
                #process = subprocess.Popen([es], stdout=subprocess.PIPE, 
                #                            stderr=subprocess.PIPE)
                os.chmod(makefilename, 0750)
                process = subprocess.Popen([makefilename]).wait()
                job_file = glob.glob((cwd + '/' + name + '/' + ex + '/' 
                                      + options.runid + "-" + abb + '-' 
                                      + abb_ex + '*_job'))

                # submit the job
                if job_file:
                    job_name = job_file[0]
                    subprocess.Popen(['rsub', options.machine,
                                      job_name])
                else:
                    raise RuntimeError('Job string file not found')

            #elif (options.run=='on') & (options.compile!='on'):
            #    print "Cannot run if compile is not on"
    
    os.chdir(cwd)
    optstring = 'nemo-test-suite '
    optstring = optstring + ' '.join(sys.argv[1:]) 
    p = subprocess.Popen(['git', '--git-dir=' + options.repo + '/.git', 'rev-parse', options.ver], stdout=subprocess.PIPE)
    sha1,err = p.communicate()

    with open('.nemo-test-suite.log', 'a') as f:
        f.write('---\n')
        f.write('user: ' + os.environ['USER'])
        f.write('\n')
        f.write('date: ' + time.strftime("%a %b %d %H:%M:%S %Z %Y"))
        f.write('\n')
        f.write('args: ' + optstring)
        f.write('\n')
        f.write('commit: ' + sha1)
        f.write('---\n')

if __name__ =="__main__":
    nemo_test_suite()


