/*****************************************************************************
FILE: convert_espa_to_cog.c
  
PURPOSE: Contains functions for creating the Cloud-Optimized GeoTIFF (COG)
products for each of the bands in the XML file.

PROJECT:  Land Satellites Data System Science Research and Development (LSRD)
at the USGS EROS

LICENSE TYPE:  NASA Open Source Agreement Version 1.3

NOTES:
  1. The XML metadata format written via this library follows the ESPA internal
     metadata format found in ESPA Raw Binary Format v2.x.doc.  The schema for
     the ESPA internal metadata format is available at
     http://espa.cr.usgs.gov/schema/espa_internal_metadata_v2_x.xsd.
  2. File format follows the Landsat COG DFCB
     https://prd-wret.s3.us-west-2.amazonaws.com/assets/palladium/production/atoms/files/LSDS-1388-Landsat-Cloud-Optimized-GeoTIFF_DFCB-v2.0.pdf
*****************************************************************************/
#include <unistd.h>
#include "convert_espa_to_cog.h"

/******************************************************************************
MODULE:  convert_espa_to_cog

PURPOSE: Converts the internal ESPA raw binary file to Cloud-Optimized GeoTIFF
(COG) file format.

RETURN VALUE:
Type = int
Value           Description
-----           -----------
ERROR           Error converting to COG
SUCCESS         Successfully converted to COG

NOTES:
  1. The GDAL tools will be used for converting the raw binary (ENVI format)
     files to COG.
  2. An associated .tfw (ESRI world file) will be generated for each COG file.
******************************************************************************/
int convert_espa_to_cog
(
    char *espa_xml_file,   /* I: input ESPA XML metadata filename */
    char *cog_file,        /* I: base output COG filename */
    bool del_src           /* I: should the source files be removed after
                                 conversion? */
)
{
    char FUNC_NAME[] = "convert_espa_to_cog";  /* function name */
    char errmsg[STR_SIZE];      /* error message */
    char overview_sampling[STR_SIZE];  /* sampling to be used for overviews;
                                   nearest for QA, average for image bands */
    char gdal_cmd[STR_SIZE];    /* command string for GDAL call */
    char cog_band[STR_SIZE];    /* name of the COG file for this band */
    char hdr_file[STR_SIZE];    /* name of the header file for this band */
    char xml_file[STR_SIZE];    /* new XML file for the COG product */
    char tmpfile[STR_SIZE];     /* filename of file.TIF.aux.xml */
    char *cptr = NULL;          /* pointer to empty space in the band name */
    int i;                      /* looping variable for each band */
    int count;                  /* number of chars copied in snprintf */
    int status = SUCCESS;       /* status for multi-threaded area */
    Espa_internal_meta_t xml_metadata;  /* XML metadata structure to be
                                   populated by reading the XML metadata file */

    /* Validate the input metadata file */
    if (validate_xml_file (espa_xml_file) != SUCCESS)
    {  /* Error messages already written */
        return (ERROR);
    }

    /* Initialize the metadata structure */
    init_metadata_struct (&xml_metadata);

    /* Parse the metadata file into our internal metadata structure; also
       allocates space as needed for various pointers in the global and band
       metadata */
    if (parse_metadata (espa_xml_file, &xml_metadata) != SUCCESS)
    {  /* Error messages already written */
        return (ERROR);
    }

    /* Loop through the bands in the XML file and convert them to COG.  The
       filenames will have the ESPA base name followed by _ and the band
       name of each band in the XML file.  Blank spaces in the band name will
       be replaced with underscores. */
#ifdef _OPENMP
    #pragma omp parallel for private (i, count, cog_band, cptr, overview_sampling, gdal_cmd, errmsg, status, tmpfile, hdr_file)
#endif
    for (i = 0; i < xml_metadata.nbands; i++)
    {
        /* Determine the output COG band name */
        count = snprintf (cog_band, sizeof (cog_band), "%s",
            xml_metadata.band[i].file_name);
        if (count < 0 || count >= sizeof (cog_band))
        {
            sprintf (errmsg, "Overflow of cog_file string");
            error_handler (true, FUNC_NAME, errmsg);
            status = ERROR;
        }

        /* Loop through this filename and replace any occurances of blank
           spaces with underscores */
        while ((cptr = strchr (cog_band, ' ')) != NULL)
            *cptr = '_';

        /* Replace the file extension. The standard is to use .TIF for the
           COG files. */
        cptr = strrchr (cog_band, '.');
        if (cptr == NULL)
        {
            sprintf (errmsg, "No file extension found in the band filename: "
                "%s\n", cog_band);
            error_handler (true, FUNC_NAME, errmsg);
            status = ERROR;
        }
        strcpy (cptr, ".TIF");

        /* Convert the files */
        printf ("Converting %s to %s\n", xml_metadata.band[i].file_name,
            cog_band);

        /* Specify the COG options. Check if the fill value is defined and
           write it to the tiff file. */
        if ((int) xml_metadata.band[i].fill_value == (int) ESPA_INT_META_FILL)
        {
            /* Fill value is not defined so don't write the nodata tag */
            count = snprintf (gdal_cmd, sizeof (gdal_cmd),
                "gdal_translate -of Gtiff -co TILED=YES -co BLOCKXSIZE=256 "
                "-co BLOCKYSIZE=256 -co INTERLEAVE=PIXEL -co COMPRESS=DEFLATE "
                "-q %s %s", xml_metadata.band[i].file_name, cog_band);
        }
        else
        {
            /* Fill value is defined so use the nodata tag */
            count = snprintf (gdal_cmd, sizeof (gdal_cmd),
                "gdal_translate -of Gtiff -a_nodata %ld -co TILED=YES "
                "-co BLOCKXSIZE=256 -co BLOCKYSIZE=256 -co INTERLEAVE=PIXEL "
                "-co COMPRESS=DEFLATE -q %s %s",
                xml_metadata.band[i].fill_value,
                xml_metadata.band[i].file_name, cog_band);
        }
        if (count < 0 || count >= sizeof (gdal_cmd))
        {
            sprintf (errmsg, "Overflow of gdal_cmd string");
            error_handler (true, FUNC_NAME, errmsg);
            status = ERROR;
        }

        if (system (gdal_cmd) == -1)
        {
            sprintf (errmsg, "Running gdal_translate: %s", gdal_cmd);
            error_handler (true, FUNC_NAME, errmsg);
            status = ERROR;
        }

        /* Generate the COG overviews. Use nearest neighbor sampling for QA
           and average sampling for image data. */
        if (!strcmp (xml_metadata.band[i].category, "qa"))
            strcpy (overview_sampling, "nearest");
        else
            strcpy (overview_sampling, "average");

        count = snprintf (gdal_cmd, sizeof (gdal_cmd),
            "gdaladdo -q -r %s %s 2 4 8 16 32 64", overview_sampling, cog_band);
        if (count < 0 || count >= sizeof (gdal_cmd))
        {
            sprintf (errmsg, "Overflow of gdal_cmd string");
            error_handler (true, FUNC_NAME, errmsg);
            status = ERROR;
        }

        if (system (gdal_cmd) == -1)
        {
            sprintf (errmsg, "Running gdaladdo: %s", gdal_cmd);
            error_handler (true, FUNC_NAME, errmsg);
            status = ERROR;
        }

        /* Remove the {cog_name}.TIF.aux.xml file since it's not needed and
           clutters the results.  Don't worry about testing the unlink
           results.  If it doesn't unlink it's not fatal. */
        count = snprintf (tmpfile, sizeof (tmpfile), "%s.aux.xml", cog_band);
        if (count < 0 || count >= sizeof (tmpfile))
        {
            sprintf (errmsg, "Overflow of tmpfile string");
            error_handler (true, FUNC_NAME, errmsg);
            status = ERROR;
        }
        unlink (tmpfile);

        /* Remove the source file if specified */
        if (del_src && status != ERROR)
        {
            /* .img file */
            printf ("  Removing %s\n", xml_metadata.band[i].file_name);
            if (unlink (xml_metadata.band[i].file_name) != 0)
            {
                sprintf (errmsg, "Deleting source file: %s",
                    xml_metadata.band[i].file_name);
                error_handler (true, FUNC_NAME, errmsg);
                status = ERROR;
            }

            /* .hdr file */
            count = snprintf (hdr_file, sizeof (hdr_file), "%s",
                xml_metadata.band[i].file_name);
            if (count < 0 || count >= sizeof (hdr_file))
            {
                sprintf (errmsg, "Overflow of hdr_file string");
                error_handler (true, FUNC_NAME, errmsg);
                status = ERROR;
            }

            cptr = strrchr (hdr_file, '.');
            strcpy (cptr, ".hdr");
            printf ("  Removing %s\n", hdr_file);
            if (unlink (hdr_file) != 0)
            {
                sprintf (errmsg, "Deleting source file: %s", hdr_file);
                error_handler (true, FUNC_NAME, errmsg);
                status = ERROR;
            }
        }

        /* Update the XML file to use the new COG band name */
        strcpy (xml_metadata.band[i].file_name, cog_band);
    }

    if (status == ERROR)
        return (ERROR);

    /* Remove the source XML if specified */
    if (del_src)
    {
        printf ("  Removing %s\n", espa_xml_file);
        if (unlink (espa_xml_file) != 0)
        {
            sprintf (errmsg, "Deleting source file: %s", espa_xml_file);
            error_handler (true, FUNC_NAME, errmsg);
            return (ERROR);
        }
    }

    /* Create the XML file for the COG product */
    count = snprintf (xml_file, sizeof (xml_file), "%s_cog.xml", cog_file);
    if (count < 0 || count >= sizeof (xml_file))
    {
        sprintf (errmsg, "Overflow of xml_file string");
        error_handler (true, FUNC_NAME, errmsg);
        return (ERROR);
    }

    /* Write the new XML file containing the COG band names */
    if (write_metadata (&xml_metadata, xml_file) != SUCCESS)
    {
        sprintf (errmsg, "Error writing updated XML for the COG product: %s",
            xml_file);
        error_handler (true, FUNC_NAME, errmsg);
        return (ERROR);
    }

    /* Free the metadata structure */
    free_metadata (&xml_metadata);

    /* Successful conversion */
    return (SUCCESS);
}

