#! /usr/bin/env python

############################################################################
# Copyright (c) 2019 Dr. Peter Bunting, Aberystwyth University
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#
# Purpose:  A command to find projection information from inputs.
#
# Author: Pete Bunting
# Email: pfb@aber.ac.uk
# Date: 09/09/2019
# Version: 1.0
#
# History:
# Version 1.0 - Created.
#
#############################################################################

import argparse
import os

import osgeo.gdal as gdal
import osgeo.osr as osr
import rsgislib.tools.utils

if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        prog="rsgisproj.py",
        description="Find projection information for a given input.",
    )

    parser.add_argument(
        "--image",
        type=str,
        required=False,
        help="The GDAL image file for which the projection will be outputted.",
    )
    parser.add_argument(
        "--vecfile",
        type=str,
        required=False,
        help="The GDAL vector file for which the projection will be outputted.",
    )
    parser.add_argument(
        "--veclyr",
        type=str,
        required=False,
        help="The layer within the --vecfile, if specified, for which the "
             "projection information will be outputted. If not provided then "
             "assume layer name is the same as the file name (e.g., shapefile)",
    )
    parser.add_argument(
        "--epsg",
        type=int,
        required=False,
        help="Provide an inputted EPSG code to define the projection.",
    )
    parser.add_argument(
        "--epsga",
        type=int,
        required=False,
        help="Provide an inputted EPSGA code to define the projection.",
    )
    parser.add_argument(
        "--wktfile",
        type=str,
        required=False,
        help="Provide an inputted WKT string as a text file to define the projection.",
    )
    parser.add_argument(
        "--wkt",
        type=str,
        required=False,
        help="Provide an inputted WKT string to define the projection.",
    )
    parser.add_argument(
        "--proj4file",
        type=str,
        required=False,
        help="Provide an inputted Proj4 string as a text "
             "file to define the projection.",
    )
    parser.add_argument(
        "--proj4",
        type=str,
        required=False,
        help="Provide an inputted Proj4 string to define the projection.",
    )
    parser.add_argument(
        "--esrifile",
        type=str,
        required=False,
        help="Provide an inputted ESRI .prj file to define the projection.",
    )
    parser.add_argument(
        "--utm",
        type=str,
        required=False,
        help="Provide a UTM zone format must be, XXH, where XX are the zone "
             "and H is the hemisphere (e.g., 36N, 01S, 02N, 54S). WGS84 "
             "will be used as the datum.",
    )
    parser.add_argument(
        "-o",
        "--output",
        required=True,
        type=str,
        nargs="+",
        choices=["EPSG", "WKT", "WKTPretty", "PROJ4"],
        help="The output format of the projection. Multiple options can be "
             "specified and they will be printed in turn.",
    )

    # Call the parser to parse the arguments.
    args = parser.parse_args()

    spat_ref_obj = None

    if args.image is not None:
        img_ds = gdal.Open(args.image, gdal.GA_ReadOnly)
        if img_ds == None:
            raise Exception("Could not open raster image: '{}'".format(args.image))
        proj_wkt_str = img_ds.GetProjection()
        img_ds = None
        spat_ref_obj = osr.SpatialReference()
        spat_ref_obj.ImportFromWkt(proj_wkt_str)
    elif args.vecfile is not None:
        vec_ds = gdal.OpenEx(args.vecfile, gdal.OF_VECTOR)
        if vec_ds is None:
            raise Exception("Could not open vector dataset: {}".format(vec_ds))

        if args.veclyr is None:
            vec_lyr_name = os.path.splitext(os.path.basename(args.vecfile))[0]
        else:
            vec_lyr_name = args.veclyr

        vec_lyr_obj = vec_ds.GetLayer(vec_lyr_name)
        if vec_lyr_obj is None:
            raise Exception(
                "Could not open layer '{0}' within file: {1}".format(
                    vec_lyr_name, args.vecfile
                )
            )
        spat_ref_obj = vec_lyr_obj.GetSpatialRef()
        vec_ds = None
    elif args.epsg is not None:
        epsg_code = int(args.epsg)
        spat_ref_obj = osr.SpatialReference()
        spat_ref_obj.ImportFromEPSG(epsg_code)
    elif args.epsga is not None:
        epsga_code = int(args.epsga)
        spat_ref_obj = osr.SpatialReference()
        spat_ref_obj.ImportFromEPSGA(epsga_code)
    elif args.wktfile is not None:
        wkt_str = rsgislib.tools.utils.read_text_file_no_new_lines(args.wktfile)
        spat_ref_obj = osr.SpatialReference()
        spat_ref_obj.ImportFromWkt(wkt_str)
    elif args.wkt is not None:
        spat_ref_obj = osr.SpatialReference()
        spat_ref_obj.ImportFromWkt(args.wkt)
    elif args.proj4file is not None:
        proj4_str = rsgislib.tools.utils.read_text_file_no_new_lines(args.proj4file)
        spat_ref_obj = osr.SpatialReference()
        spat_ref_obj.ImportFromProj4(proj4_str)
    elif args.proj4 is not None:
        spat_ref_obj = osr.SpatialReference()
        spat_ref_obj.ImportFromProj4(args.proj4)
    elif args.esrifile is not None:
        esri_prj_str = rsgislib.tools.utils.read_text_file_no_new_lines(args.esrifile)
        spat_ref_obj = osr.SpatialReference()
        spat_ref_obj.ImportFromESRI(esri_prj_str)
    elif args.utm is not None:
        utm_str = args.utm
        if len(utm_str) == 3:
            try:
                utm_zone = int(utm_str[0:2])
                if (utm_zone < 1) or (utm_zone > 60):
                    raise Exception("UTM zone is out of range.")
                utm_hemi = utm_str[2]
            except Exception as e:
                print(
                    f"Could not retrieve UTM zone from UTM string: '{utm_str}'. "
                    f"Zone = {utm_str[0:1]}, Hemispher = {utm_str[2]}"
                )
                raise e
            hemi_north = True
            if utm_hemi.upper() == "S":
                hemi_north = False

            spat_ref_obj = osr.SpatialReference()
            spat_ref_obj.SetUTM(utm_zone, hemi_north)
        else:
            raise Exception(
                "UTM string should only have three characters "
                "(e.g., 36N, 01S, 02N, 54S): '{utm_str}'."
            )
    else:
        raise Exception("You must provide one of the input options.")

    if spat_ref_obj is not None:
        for out_format in args.output:
            if out_format.upper() == "WKT":
                print("{}".format(spat_ref_obj.ExportToWkt()))
            elif out_format.upper() == "WKTPRETTY":
                print("{}".format(spat_ref_obj.ExportToPrettyWkt()))
            elif out_format.upper() == "EPSG":
                spat_ref_obj.AutoIdentifyEPSG()
                print("EPSG:{}".format(spat_ref_obj.GetAuthorityCode(None)))
            elif out_format.upper() == "PROJ4":
                print("{}".format(spat_ref_obj.ExportToProj4()))
            else:
                raise Exception("The output projection format was not recognised.")
    else:
        raise Exception(
            "The projection object was None and therefore "
            "something has gone wrong when inputting the projection."
        )
