#! /usr/bin/env python3

""" Takes in the level 2 XML file and an output directory and then formats and
    packages the level 2 product. The packaged product is output to the output
    directory.
"""

import sys
import logging
import re
from subprocess import CalledProcessError
from collections import namedtuple
from argparse import ArgumentParser
from datetime import datetime


# Local imports
from espa import Metadata
from espa import System
from format_l2_product import format_product
from create_l2_mtl import create_l2_mtl
from package_l2_product import package_product

# Global variables
logger = logging.getLogger(__name__)


def create_l2_product_id(xml_file, product_date, processing_level):
    """ Creates the level 2 product ID from the level 1 product ID.

        Args:
            xml_file (str): The filename for the XML metadata file to get
                the level 1 product ID from.
            product_date (datetime): The date to use for the processing date
            processing_level: Processing level based on the L2 product type.
                    This is either 'L2SP' or 'L2SR'.

        Returns:
            The level 2 product ID

        Raises:
            ValueError if the level 1 product ID wasn't valid.
    """
    # Parse the XML file
    xml = Metadata(xml_file)
    xml.parse()

    product_regex = re.compile(
        "L[EMTOC]\d{2}_\S{4}_\d{6}_(\d{8}_){2}\d{2}_\S{2}")

    # Get product ID from XML and verify it is valid
    l1_product_id = str(xml.xml_object.global_metadata.product_id)
    logger.debug('Verifying that the level 1 ID is valid: ' + l1_product_id)
    valid_l1_product_id = product_regex.match(l1_product_id)
    if valid_l1_product_id is None:
        raise ValueError('Error: Invalid product ID found: ' + l1_product_id)

    # Generate the level 2 product ID by changing the processing level and date
    # on the level 1 ID.
    logger.debug('Creating level 2 ID from level 1 ID')
    l1_product_id_parts = l1_product_id.split('_')

    l1_product_id_parts[1] = processing_level

    l1_product_id_parts[4] = product_date.strftime('%Y%m%d')
    l2_product_id = '_'.join(l1_product_id_parts)
    logger.debug('Created level 2 ID: ' + l2_product_id)

    return l2_product_id


def main():
    """ Main routine for the format and packaging script.

        Generates the level 2 product ID and current time. These are then
        passed into the scripts that are called to keep the product ID and time
        synced between all the scripts. The called scripts perform all of the
        rest of the packaging and formatting.

        The called scripts are: format_product, create_l2_mtl, and
        package_product.
    """
    # Create command line argument parser
    parser = ArgumentParser()

    parser.add_argument('--xml',
                        action='store', dest='xml_file', required=True,
                        help='The XML metadata file to use')

    parser.add_argument('--output_dir',
                        action='store', dest='output_dir', required=True,
                        help='The directory to be created to store the final '
                             'product.')

    parser.add_argument('--hdf_filename',
                        action='store', dest='hdf_filename',
                        required=False, default=None,
                        help='File to use for formatting into HDF rather than '
                             'GeoTIFF')

    parser.add_argument('--hdf5_filename',
                        action='store', dest='hdf5_filename',
                        required=False, default=None,
                        help='File to use for formatting into HDF5 rather than'
                             ' GeoTIFF')

    parser.add_argument('--debug',
                        action='store_true', dest='debug',
                        required=False, default=False,
                        help='Enable debug logging')

    parser.add_argument('--disable_tar', action='store_false',
                        dest='create_tarball', required=False,
                        help='Disable the the Level 2 product tarball')

    parser.add_argument('--disable_cog', action='store_false',
                        dest='convert_cog', required=False,
                        help='Disable the conversion to COG format')

    parser.add_argument('--l2_product_type',
                        action='store', required=False, default='sr_st',
                        choices=['sr', 'sr_st', 'sr_toa_bt',
                                 'sr_st_toa_bt'], type=str.lower,
                        help='Set the L2 product (sr, sr_st, sr_toa_bt, '
                             'or sr_st_toa_bt)')

    parser.add_argument('--include_angle_bands', action='store_true',
                        dest='include_angle_bands', required=False,
                        help='Include band 4 angle bands')

    args = parser.parse_args()

    # Configure logging
    System.setup_logging(args.debug)

    # Processing level setting
    processing_level = {"sr": "L2SR",
                        "sr_st": "L2SP",
                        "sr_toa_bt": "L2SR",
                        "sr_st_toa_bt": "L2SP"}

    Product = namedtuple('Product', ['type', 'bqa'])
    products = [Product(type=args.l2_product_type, bqa=True)]
    product_date = datetime.utcnow()

    try:
        l2_product_id = create_l2_product_id(
            args.xml_file, product_date, processing_level[args.l2_product_type]
            )
    except ValueError as e:
        logger.exception(str(e))
        sys.exit('Failed to format and package product.')

    for product in products:
        try:
            # Format the product
            format_product(args.xml_file, l2_product_id, args.output_dir,
                           product.type, args.convert_cog,
                           args.include_angle_bands, args.hdf_filename,
                           args.hdf5_filename, product.bqa)

            if args.hdf_filename is None and args.hdf5_filename is None:
                # Create level 2 metadata
                create_l2_mtl(l2_product_id, args.output_dir, product_date,
                              args.l2_product_type,
                              processing_level[args.l2_product_type],
                              args.include_angle_bands)

                # If the flag disabling the Level 2 product tarball is off,
                # create the tarball. The checksum file is created and
                # product_data.txt file are updated regardless
                package_product(args.output_dir, l2_product_id,
                                product_date,
                                processing_level[args.l2_product_type],
                                args.create_tarball)
        except CalledProcessError as e:
                logger.exception(str(e))
                logger.error(e.output)
                sys.exit('Failed to format and package product.')
        except Exception as e:
            logger.exception(str(e))
            sys.exit('Failed to format and package product.')

    logger.info('Completed format and package of the product ' + l2_product_id)
    sys.exit()


if __name__ == '__main__':
    main()
