#!/usr/bin/env python
"""
----------------------------------------------------------------------
Purpose:        Collect relevant input and output files
                for filing a support request.

Usage:          mreport [-c -h -? -ver] <name directory>

  -c <int|str>  Copy only the first and last <int>
                lines of any files. Default: 700
                if '-c all' is given, all lines are
                copied.
  -h -?         Print this help text.
  -ver          Print version info

Mreport will create a temporary directory in the current
directury named similar to "mreport_original_name_dir_<PID>"
and copy to it all relevant information from the name directory.
Large files will be shortened in this process (see -c option).

Mreport will compile a .tgz file of the temporary directory.
Please include this .tgz in your support request.

Note that mreport will also collect some system information,
therefore please run mreport on the same host on which
the failed job was running.
----------------------------------------------------------------------
"""


# standard modules
import getopt
import sys
import os
import re
import platform
import subprocess

# put MCTDH in the search path for modules
modulepath = os.path.expandvars("$MCTDH_DIR")
sys.path.insert(1,modulepath+'/bin/python/clusters')


# import some stuff from MCTDH python lib
from mversion import __version__, __status__, __author__
from mversion import __copyright__, __maintainer__, __email__
from mversion import versionInfo

# number of lines to copy if files are very large
CPLINES = 700

# list of files we are interested in
filelist = ("log", "op.log", "op.log.pes", "log.pes", "output", "input",
            "timing", "steps", "auto","cross","ptiming",
            "mpitiming","rlx_info","stop","update","speed",
            "eigval", "iteration", "overlaps", "jfunc",
            "fieldf.dat", "fieldb.dat","targetpop.dat",
            "dipmtxf.dat","overlapf.dat","rawfieldf.dat",
            "optcnrl.log", "backward_init.inp","backward_init.op",
            "backward.op","backward_targ.inp","fieldb_init.dat",
            "fieldb_targ.dat","fieldf_init.dat","fieldf_targ.dat",
            "forward1.inp","forwardi.inp","forward_init.inp",
            "forward_init.op","forward.op","forward_targ.inp",
            "forward_targ.op", "genfld_envlpe.log","genfld_guessb.log",
            "genfld_guess.log","initialstate.inp","pnltyfnc.dat",
            "targetstate.inp","sysinfo")

# subdirectories that could exist
subdirs = ("forward","backward", "initialstate", "targetstate",
           "forward_init","backward_init","forward_targ","backward_targ")


def chkNameDir(name_dir):
    """
    chkNameDir(name_dir): compile a list of files to copy.
    """

    files = []
    dirs = []

    for fname in filelist:
        fpath = name_dir + "/" + fname
        if os.path.isfile(fpath):
            files.append(fname)

    # optcntrl
    for sdir in subdirs:
        if os.path.exists(name_dir + "/" + sdir) and os.path.isdir(name_dir + "/" + sdir):
            dirs.append(sdir)
            for fname in filelist:
                fpath = name_dir + "/" + sdir + "/" + fname
                if os.path.isfile(fpath):
                    files.append(sdir + "/" + fname)

    return files, dirs


def getSysInfo():
    """
    getSysInfo(name_dir): collect some data about the computer.
    Mostly relies on MCTDH compile script.
    """

    pattern = "[\'\(\)\,]"

    sysinfo = "PLATFORM INFORMATION\n\n"

    sysinfo += "Platform         " + platform.platform() + "\n"
    sysinfo += "System           " + platform.system() + "\n"
    sysinfo += "System           " + platform.machine() + "\n"

    try:
        wininfo = str(platform.win32_ver())
        wininfo = re.sub(pattern,"", wininfo)
        wininfo = re.sub(" +"," ", wininfo)

        isempty = (wininfo == " ")
        if not isempty:
            sysinfo += "win32_ver        " + wininfo
    except:
        pass

    try:
        macinfo = str(platform.mac_ver())
        macinfo = re.sub(pattern,"", macinfo)
        macinfo = re.sub(" +"," ", macinfo)

        isempty = (macinfo == " ")
        if not isempty:
            sysinfo += "mac_ver          " + macinfo
    except:
        pass

    try:
        distinfo = str(platform.dist())
        distinfo = re.sub(pattern,"", distinfo)
        distinfo = re.sub(" +"," ", distinfo)

        isempty = (distinfo == " ")
        if not isempty:
            sysinfo += "dist             " + distinfo
    except:
        pass

    sysinfo += "\n\n\n"

    process = subprocess.Popen(["compile", "config",], stdout=subprocess.PIPE)
    (output, err) = process.communicate()
    exit_code = process.wait()
    sysinfo += str(output) + "\n\n" + str(err)

    return sysinfo


def cpfile(src,dest,chop=None):
    """
    cpfile(src,dest,chop=None)

    Copy file src to file dest.
    If chop is None, copy complete.
    If chop is integer then copy
    the first chop lines and the last
    chop lines (if file has more lines
    then 3*chop)
    """

    if chop is None:
        commandline = "cp " + src + " " + dest
        print commandline
        os.system(commandline)
        return

    chop = int(chop)

    # check number of lines in file
    process = subprocess.Popen(["wc", "-l", src], stdout=subprocess.PIPE)
    (output, err) = process.communicate()
    exit_code = process.wait()
    nlines = int(str(output).split()[0])


    # if files are very long just copy first and last part
    if nlines > 3*chop:

        message = ">> chopfile " + src + " > " + dest
        print message

        commandline = "head -n " + str(chop) + " " + src + " > " +  dest
        os.system(commandline)

        nskip = nlines - 2*chop

        # put a message that lines have been skipped
        fdest = file(dest,mode="a")
        message = "\n# ++++++++++++++++++++++++++++++++++++++++++\n"
        message += "# mreport: skipping " + str(nskip) + " lines"
        message += "\n# ++++++++++++++++++++++++++++++++++++++++++\n\n"
        fdest.write(message)
        fdest.close()

        commandline = "tail -n " + str(chop)+ " " + src + " >> " +  dest
        os.system(commandline)
        return

    # otherwise copy complete
    else:
        commandline = "cp " + src + " " + dest
        print commandline

        exitstatus = os.system(commandline)
        if exitstatus <> 0:
            print "Problem while copying file " + src
            print "cp returned exit code " + str(exitstatus)
            sys.exit("Aborting...")

        return




if __name__ == "__main__":


    # first get the command line options
    caller_name = sys.argv.pop(0)
    caller_name = caller_name.split("/")[-1]

    if "-h" in sys.argv or "-?" in sys.argv:
        sys.exit(__doc__)

    if "-ver" in sys.argv:

        versionInfo(progname = caller_name,
                    version = __version__,
                    status = __status__,
                    author = __author__,
                    copyright = __copyright__,
                    maintainer = __maintainer__,
                    support = __email__)
        sys.exit()

    if len(sys.argv) >= 1:
        name_dir = sys.argv.pop(-1)

    if '-c' in sys.argv:
        pos = sys.argv.index("-c")
        sys.argv.pop(pos)
        try:
            CPLINES = sys.argv.pop(pos)
        except:
            message = " ERROR: -c option must be "
            message += "followed by an integer or 'all'"
            sys.exit(message)
        if CPLINES == "all":
            CPLINES = None
        else:
            try:
                CPLINES = int(CPLINES)
            except:
                message = " ERROR: -c option must be "
                message += "followed by an integer or 'all'"
                sys.exit(message)


    elif len(sys.argv) <> 0:
        message = " ERROR: unknown options: " + " ".join(sys.argv)
        sys.exit(message)



    if name_dir.endswith("/."):
        name_dir = name_dir[0:len(name_dir)-1]

    if name_dir[-1:] == "/":
        name_dir = name_dir[0:len(name_dir)-1]


    # check if name_dir is a directory
    if not os.path.isdir(name_dir):
        message = " ERROR: '" + name_dir + "' is not a directory."
        sys.exit(message)


    # check for log and input files.
    # Abort if there is no log or input - these are essential
    logname = name_dir+"/log"
    octlogname = name_dir+"/optcntrl.log"
    peslogname = name_dir+"/log.pes"
    inpname = name_dir+"/input"

    logs = "', '".join([logname,peslogname,octlogname])

    if not os.path.isfile(logname) \
           and not os.path.isfile(octlogname) \
           and not os.path.isfile(peslogname):

        message = " ERROR: None of '" +logs + "' does exist."
        message += " Is " + name_dir + " a name directory?"
        sys.exit(message)

    if not os.path.isfile(inpname) and not os.path.isfile(octlogname):
        message = " ERROR: '" +inpname + "' does not exist."
        message += " Is " + name_dir + " a name directory?"
        sys.exit(message)


    # make the temporary name dir
    pid = os.getpid()
    name_dir1 = name_dir.split("/")[-1]
    if name_dir == ".":
        name_dir1 = os.getcwd().split("/")[-1]
    tmp_name_dir =  caller_name + "_" + name_dir1 + "_" + str(pid)

    commandline = "mkdir -p " + tmp_name_dir
    print commandline
    exitstatus = os.system(commandline)
    if exitstatus <> 0:
        print "Problem while creating " + tmp_name_dir
        print "mkdir returned exit code " + str(exitstatus)
        sys.exit("Aborting...")

    # collect system info and write to temporary name dir
    sysinfo = getSysInfo()
    fname = tmp_name_dir + "/sysinfo"
    if not os.path.exists(fname) or loverwrite:
        f = file(fname,mode="w")
        f.write(sysinfo)
        f.close()
    else:
        message = " ERROR: could not write file " + fname + ". Use -w option."
        sys.exit(message)


    # collect list of things to copy...
    flist, dirs = chkNameDir(name_dir)
    for d in dirs:
        commandline = "mkdir -p " + tmp_name_dir + "/" + d
        print commandline

        exitstatus = os.system(commandline)
        if exitstatus <> 0:
            print "Problem while creating " + tmp_name_dir + "/" + d
            print "mkdir returned exit code " + str(exitstatus)
            sys.exit("Aborting...")

    # ...and copy
    for f in flist:
        src = os.path.join(name_dir,f)
        dest = os.path.join(tmp_name_dir,f)
        cpfile(src,dest,chop=CPLINES)


    # finally make a tar and zip it
    tarfilename = tmp_name_dir + ".tgz"
    commandline = "tar -cvzf " + tarfilename + " " + tmp_name_dir
    print commandline

    exitstatus = os.system(commandline)
    if exitstatus == 0:
        print "\nCreated file " + tarfilename
    else:
        print "Problem while creating " + tarfilename
        print "tar returned exit code " + str(exitstatus)
