# -*- coding: utf-8 -*-
"""
This is a skeleton file that can serve as a starting point for a Python
console script. To run this script uncomment the following lines in the
[options.entry_points] section in setup.cfg:

    console_scripts =
         fibonacci = satproc.skeleton:run

Then run `python setup.py install` which will install the command `fibonacci`
inside your current environment.
Besides console scripts, the header (i.e. until _logger...) of this file can
also be used as template for Python modules.

Note: This skeleton file can be safely removed if not needed!
"""

import argparse
import logging
import sys

from satproc import __version__
from satproc.chips import extract_chips
from satproc.utils import get_raster_band_count

__author__ = "Damián Silvani"
__copyright__ = "Dymaxion Labs"
__license__ = "Apache-2.0"

_logger = logging.getLogger(__name__)


def parse_args(args):
    """Parse command line parameters

    Args:
      args ([str]): command line parameters as list of strings

    Returns:
      :obj:`argparse.Namespace`: command line parameters namespace
    """
    parser = argparse.ArgumentParser(
        description="Extract chips from a raster file, "
        "and optionally generate mask chips",
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
    )

    parser.add_argument("raster", nargs="+", help="input raster file(s)")
    parser.add_argument(
        "--size", type=int, default=256, help="size of image tiles, in pixels"
    )
    parser.add_argument(
        "--step-size", type=int, default=None, help="step size (i.e. stride), in pixels (if None, same as size i.e. no overlap)"
    )
    parser.add_argument(
        "--sliding-windows-mode",
        choices=["exact", "whole", "whole_overlap"],
        default="whole_overlap",
        help="mode of sliding windows",
    )
    parser.add_argument("--labels", help="inpul label shapefile")
    parser.add_argument(
        "--label-property",
        default="class",
        help="label property to separate in classes",
    )
    parser.add_argument(
        "--classes", nargs="+", type=str, help="specify classes order in result mask."
    )
    parser.add_argument(
        "--masks",
        "-m",
        nargs="+",
        choices=["extent", "boundary", "distance"],
        default={"extent"},
    )
    parser.add_argument(
        "--mask-type", choices=["single", "class", "instance"], default="class"
    )
    parser.add_argument("--aoi", help="Filter by AOI vector file")
    parser.add_argument(
        "--within",
        dest="within",
        action="store_true",
        help="only create chip is it is within AOI (if provided)",
    )
    parser.add_argument(
        "--no-within",
        dest="within",
        default=False,
        action="store_false",
        help="create chip if it intersects with AOI (if provided)",
    )
    parser.add_argument("-o", "--output-dir", help="output dir", default=".")

    parser.add_argument(
        "--rescale",
        dest="rescale",
        default=False,
        action="store_true",
        help="rescale intensity using percentiles (lower/upper cuts)",
    )
    parser.add_argument(
        "--no-rescale",
        dest="rescale",
        action="store_false",
        help="do not rescale intensity",
    )

    parser.add_argument(
        "--rescale-mode",
        default="percentiles",
        choices=["percentiles", "values", "s2_rgb_extra"],
        help="choose mode of intensity rescaling",
    )

    parser.add_argument(
        "--lower-cut",
        type=float,
        default=2,
        help="(for 'percentiles' mode) lower cut of percentiles "
        "for cumulative count in intensity rescaling",
    )
    parser.add_argument(
        "--upper-cut",
        type=float,
        default=98,
        help="(for 'percentiles' mode) upper cut of percentiles "
        "for cumulative count in intensity rescaling",
    )

    parser.add_argument(
        "--min",
        type=float,
        help="(for 'values' mode) minimum value in intensity rescaling",
    )
    parser.add_argument(
        "--max",
        type=float,
        help="(for 'values' mode) maximum value in intensity rescaling",
    )

    parser.add_argument(
        "-b",
        "--bands",
        nargs="+",
        type=int,
        help="specify band indexes. If type is 'jpg', defaults to (1, 2, 3). "
        "If type is 'tif', defaults to the total band count.",
    )
    parser.add_argument(
        "-t", "--type", help="output chip format", choices=["jpg", "tif"], default="tif"
    )

    parser.add_argument(
        "--write-footprints",
        help="write a GeoJSON file of chip footprints",
        dest="write_footprints",
        action="store_true",
        default=False,
    )
    parser.add_argument(
        "--no-write-footprints",
        help="do not write a GeoJSON file of chip footprints",
        dest="write_footprints",
        action="store_false",
    )

    parser.add_argument("--crs", help="force CRS of input files")

    parser.add_argument(
        "--skip-existing",
        dest="skip_existing",
        default=True,
        action="store_true",
        help="skip already existing chips (and masks)",
    )
    parser.add_argument(
        "--no-skip-existing",
        dest="skip_existing",
        action="store_false",
        help="do not skip already existing chips (and masks)",
    )

    parser.add_argument(
        "--skip-low-contrast",
        dest="skip_low_contrast",
        default=False,
        action="store_true",
        help="skip image chips with low contrast",
    )
    parser.add_argument(
        "--no-skip-low-contrast",
        dest="skip_low_contrast",
        action="store_false",
        help="do not skip image chips with low contrast",
    )
    parser.add_argument(
        "--skip-with-empty-mask",
        action="store_true",
        default=True,
        help="skip chips with empty mask",
    )
    parser.add_argument(
        "--no-skip-with-empty-mask",
        action="store_false",
        dest="skip_with_empty_mask",
        help="do not skip chips with empty mask",
    )
    parser.add_argument(
        "--extent-no-border",
        action="store_true",
        help="do not include border in extent mask",
    )

    parser.add_argument(
        "--version", action="version", version="satproc {ver}".format(ver=__version__)
    )
    parser.add_argument(
        "-v",
        "--verbose",
        dest="loglevel",
        help="set loglevel to INFO",
        action="store_const",
        const=logging.INFO,
    )
    parser.add_argument(
        "-vv",
        "--very-verbose",
        dest="loglevel",
        help="set loglevel to DEBUG",
        action="store_const",
        const=logging.DEBUG,
    )
    return parser.parse_args(args), parser


def setup_logging(loglevel):
    """Setup basic logging

    Args:
      loglevel (int): minimum loglevel for emitting messages
    """
    logformat = "[%(asctime)s] %(levelname)s:%(name)s:%(message)s"
    logging.basicConfig(
        level=loglevel, stream=sys.stdout, format=logformat, datefmt="%Y-%m-%d %H:%M:%S"
    )


def main(args):
    """Main entry point allowing external calls

    Args:
      args ([str]): command line parameter list
    """
    args, parser = parse_args(args)
    setup_logging(args.loglevel)

    bands = args.bands
    if not bands:
        if args.type == "jpg":
            bands = [1, 2, 3]
        else:
            some_raster = args.raster[0]
            bands = list(range(1, get_raster_band_count(some_raster) + 1))

    if type == "jpg" and len(bands) != 3:
        parser.error(
            f"--type is jpg, but --bands does not have 3 band indexes: {bands}"
        )

    rescale_mode = args.rescale_mode if args.rescale else None
    if rescale_mode == "percentiles":
        rescale_range = (args.lower_cut, args.upper_cut)
        _logger.info("Rescale intensity with percentiles %s", rescale_range)
    elif rescale_mode == "values":
        rescale_range = (args.min, args.max)
        _logger.info("Rescale intensity with values %s", rescale_range)
    elif rescale_mode == "s2_rgb_extra":
        rescale_range = (args.lower_cut, args.upper_cut)
        _logger.info(
            "Rescale intensity of first 3 bands (RGB) to (0, 0.3) "
            "and the rest of the bands with percentiles %s",
            rescale_range,
        )
    else:
        rescale_range = None
        _logger.info("No rescale intensity")

    _logger.info("Extract chips")

    extract_chips(
        args.raster,
        aoi=args.aoi,
        labels=args.labels,
        label_property=args.label_property,
        masks=args.masks,
        mask_type=args.mask_type,
        extent_no_border=args.extent_no_border,
        rescale_mode=rescale_mode,
        rescale_range=rescale_range,
        bands=bands,
        chip_type=args.type,
        within=args.within,
        write_footprints=args.write_footprints,
        classes=args.classes,
        crs=args.crs,
        skip_existing=args.skip_existing,
        skip_low_contrast=args.skip_low_contrast,
        skip_with_empty_mask=args.skip_with_empty_mask,
        size=args.size,
        step_size=args.step_size,
        windows_mode=args.sliding_windows_mode,
        output_dir=args.output_dir,
    )


def run():
    """Entry point for console_scripts"""
    main(sys.argv[1:])


if __name__ == "__main__":
    run()
