#! /usr/bin/env python3
import sys
import os
import re
import subprocess
import datetime
from optparse import OptionParser
import logging

ERROR = 1
SUCCESS = 0


#############################################################################
#  Python script to run the Landsat 8-9 surface reflectance code based
# on the inputs specified by the user.  This script will determine the input
# auxiliary file needed for processing, based on the date of the Landsat 8-9
# input file.
#
# Usage: do_lasrc.py --help prints the help message
############################################################################
class SurfaceReflectance():

    def __init__(self):
        pass

    ########################################################################
    # Description: runSr will use the parameters passed for the input and
    # output files.  If input/output files are None (i.e. not specified) then
    # the command-line parameters will be parsed for this information.  The
    # surface reflectance application is then executed to generate the desired
    # outputs on the specified input file.  If a log file was specified, then
    # the output from this application will be logged to that file.
    #
    # Inputs:
    #   xml_infile - name of the input XML file
    #   process_sr - specifies whether the surface reflectance processing,
    #       should be completed.  True or False.  Default is True, otherwise
    #       the processing will halt after the TOA reflectance products are
    #       complete.
    #   num_threads - number of threads used for processing
    #
    # Returns:
    #     ERROR - error running the surface reflectance application
    #     SUCCESS - successful processing
    #
    # Notes:
    #   1. The script obtains the path of the XML file and changes
    #      directory to that path for running the surface reflectance
    #      application.  If the XML file directory is not writable, then this
    #      script exits with an error.
    #   2. If the XML file is not specified and the information is
    #      going to be grabbed from the command line, then it's assumed all
    #      the parameters will be pulled from the command line.
    #######################################################################
    def runSr(self, xml_infile=None, process_sr=None, write_toa=False,
              num_threads=None):
        # if no parameters were passed then get the info from the
        # command line
        if xml_infile is None:
            # Get version number
            cmdstr = ('lasrc --version')
            (exit_code, self.version) = subprocess.getstatusoutput(cmdstr)

            # get the command line argument for the XML file
            parser = OptionParser(version=self.version)
            parser.add_option("-i", "--xml", type="string",
                              dest="xml",
                              help="name of XML file", metavar="FILE")
            parser.add_option("-s", "--process_sr", type="string",
                              dest="process_sr",
                              help="process the surface reflectance products; "
                              "True or False (default is True)  If False, then"
                              " processing will halt after the TOA reflectance"
                              " products are complete.")
            parser.add_option("--write_toa", dest="write_toa", default=False,
                              action="store_true",
                              help="write the intermediate TOA reflectance "
                              "products")
            parser.add_option("--write_bt", type="choice",dest="write_bt",
                              choices=['True','False'], default=True,
                              help="write the intermediate TOA Brightness "
                              "Temperature products")
            parser.add_option("--scale_refl", dest="scale_refl",
                              type="float",
                              help="scaling value for reflective bands")
            parser.add_option("--offset_refl", dest="offset_refl",
                              type="float",
                              help="offset value for reflective bands")
            parser.add_option("--scale_therm", dest="scale_therm",
                              type="float",
                              help="scaling value for thermal bands")
            parser.add_option("--offset_therm", dest="offset_therm",
                              type="float",
                              help="offset value for thermal bands")
            parser.add_option("--num_threads", dest="num_threads",
                              type="int",
                              help="number of threads used for processing")
            parser.add_option("--verbose", dest="verbose", default=False,
                              action="store_true",
                              help="Turn verbose logging on")
            parser.add_option("--use_l1_angle_bands", action="store_true",
                              dest="use_l1_angle_bands", default=False,
                              help=("flag to use level 1 angle bands rather"
                                    " than create them"))

            (options, args) = parser.parse_args()

            # XML input file
            xml_infile = options.xml
            if xml_infile is None:
                parser.error('missing input XML file command-line argument')
                return ERROR

            # surface reflectance options
            process_sr = options.process_sr
            write_toa = options.write_toa
            write_bt = options.write_bt
            scale_refl = options.scale_refl
            offset_refl = options.offset_refl
            scale_therm = options.scale_therm
            offset_therm = options.offset_therm
            num_threads = options.num_threads
            verbose = options.verbose
            use_l1_angle_bands = options.use_l1_angle_bands

        # get the logger
        logger = logging.getLogger(__name__)
        msg = ('Surface reflectance processing of Landsat file: {}'
               .format(xml_infile))
        logger.info(msg)

        # make sure the XML file exists
        if not os.path.isfile(xml_infile):
            msg = ('XML file does not exist or is not accessible: {}'
                   .format(xml_infile))
            logger.error(msg)
            return ERROR

        # use the base XML filename and not the full path.
        base_xmlfile = os.path.basename(xml_infile)
        msg = 'Processing XML file: {}'.format(base_xmlfile)
        logger.info(msg)

        # get the path of the XML file and change directory to that location
        # for running this script.  save the current working directory for
        # return to upon error or when processing is complete.  Note: use
        # abspath to handle the case when the filepath is just the filename
        # and doesn't really include a file path (i.e. the current working
        # directory).
        mydir = os.getcwd()
        xmldir = os.path.dirname(os.path.abspath(xml_infile))
        if not os.access(xmldir, os.W_OK):
            msg = ('Path of XML file is not writable: {}. Script needs '
                   'write access to the XML directory.'.format(xmldir))
            logger.error(msg)
            return ERROR
        msg = ('Changing directories for surface reflectance processing: {}'
               .format(xmldir))
        logger.info(msg)
        os.chdir(xmldir)

        # pull the date from the XML filename to determine which auxiliary
        # file should be used for input.
        # Example: LC09_L1TP_041027_20130630_20140312_01_T1.xml uses the
        # L8ANC2013181.hdf_fused HDF file.

        l1_product_regex = \
            re.compile("L[OC]0[89]_\S{4}_\d{6}_(\d{4})(\d{2})(\d{2})")
        match = re.match(l1_product_regex, base_xmlfile)
        if match:
            # Level 1 product naming convention. Parse the year, month, day
            # from the XML filename.
            aux_year = match.group(1)
            aux_month = match.group(2)
            aux_day = match.group(3)
            myday = datetime.date(int(aux_year), int(aux_month), int(aux_day))
            aux_doy = myday.strftime("%j")
            # The L8ANC files are actually used for both L8 and L9
            aux_file = 'L8ANC{}{}.hdf_fused'.format(aux_year, aux_doy)
        else:
            msg = ('Base XML filename {} is not recognized as a valid Landsat '
                   '8-9 product Id'.format(base_xmlfile))
            logger.error(msg)
            os.chdir(mydir)
            return ERROR

        # Generate per-pixel angle bands for band 4 (representative band)
        # if directed to do so.  Otherwise, it's assumed the angle bands
        # are available with the level 1 data.
        if not use_l1_angle_bands:
            cmdstr = 'create_l8_angle_bands --xml {}'.format(base_xmlfile)
            logger.info('per-pixel angles command: {0}'.format(cmdstr))
            (exit_code, output) = subprocess.getstatusoutput(cmdstr)
            logger.info(output)
            if exit_code != 0:
                logger.error('Error running create_l8_angle_bands. Processing '
                             'will terminate.')
                os.chdir(mydir)
                return ERROR

            # Mask the angle bands to match the band quality band
            cmdstr = ('mask_per_pixel_angles.py --xml {}'
                      .format(base_xmlfile))
            (exit_code, output) = subprocess.getstatusoutput(cmdstr)
            logger.info(output)
            if exit_code != 0:
                logger.error('Error masking angle bands with the band '
                             'quality band. Processing will terminate.')
                return ERROR

        # run surface reflectance algorithm, checking the return status.  exit
        # if any errors occur.
        process_sr_opt_str = '--process_sr=true '
        write_toa_opt_str = ''
        write_bt_opt_str = ''
        verbose_opt_str = ''
        scale_refl_opt_str = ''
        scale_therm_opt_str = ''
        offset_refl_opt_str = ''
        offset_therm_opt_str = ''
        num_threads_opt_str = ''

        if process_sr == 'False':
            process_sr_opt_str = '--process_sr=false '

        if write_toa:
            write_toa_opt_str = '--write_toa '

        if write_bt == 'False':
            write_bt_opt_str = '--write_bt=false '

        if verbose:
            verbose_opt_str = '--verbose '
        if scale_refl is not None:
            scale_refl_opt_str = '--scale_refl={} '.format(scale_refl)
        if scale_therm is not None:
            scale_therm_opt_str = '--scale_therm={} '.format(scale_therm)
        if offset_refl is not None:
            offset_refl_opt_str = '--offset_refl={} '.format(offset_refl)
        if offset_therm is not None:
            offset_therm_opt_str = '--offset_therm={} '.format(offset_therm)
        if num_threads is not None:
            num_threads_opt_str = '--num_threads={} '.format(num_threads)

        cmdstr = ('lasrc --xml={XML} --aux={AUX} '
                  '{SR}{TOA}{BT}{VERB}{SCALE_REF}{SCALE_THERM}{OFFSET_REF}'
                  '{OFFSET_THERM}{NUM_THREADS}'
                  .format(XML=xml_infile, AUX=aux_file, SR=process_sr_opt_str,
                          TOA=write_toa_opt_str, BT=write_bt_opt_str,
                          VERB=verbose_opt_str,
                          SCALE_REF=scale_refl_opt_str,
                          SCALE_THERM=scale_therm_opt_str,
                          OFFSET_REF=offset_refl_opt_str,
                          OFFSET_THERM=offset_therm_opt_str,
                          NUM_THREADS=num_threads_opt_str))
        msg = 'Executing lasrc command: {}'.format(cmdstr)
        logger.info(msg)
        (exit_code, output) = subprocess.getstatusoutput(cmdstr)
        logger.info(output)
        if exit_code != 0:
            msg = 'Error running lasrc.  Processing will terminate.'
            logger.error(msg)
            os.chdir(mydir)
            return ERROR

        # successful completion.  return to the original directory.
        os.chdir(mydir)
        msg = 'Completion of surface reflectance.'
        logger.info(msg)
        return SUCCESS

# #####end of SurfaceReflectance class######

if __name__ == "__main__":
    # setup the default logger format and level. log to STDOUT.
    logging.basicConfig(format=('%(asctime)s.%(msecs)03d %(process)d'
                                ' %(levelname)-8s'
                                ' %(filename)s:%(lineno)d:'
                                '%(funcName)s -- %(message)s'),
                        datefmt='%Y-%m-%d %H:%M:%S',
                        level=logging.INFO)
    sys.exit(SurfaceReflectance().runSr())
