"""
Implementation of Sentinel Hub Process API interface
"""
from .constants import MimeType
from .download import SentinelHubDownloadClient
from .sentinelhub_base_api import SentinelHubBaseApiRequest
from .sh_utils import _update_other_args


class SentinelHubRequest(SentinelHubBaseApiRequest):
    """ Sentinel Hub Process API interface

    For more information check
    `Statistical API documentation <https://docs.sentinel-hub.com/api/latest/api/process/>`__.
    """
    _SERVICE_ENDPOINT = 'process'

    def __init__(self, evalscript, input_data, responses, bbox=None, geometry=None, size=None, resolution=None,
                 **kwargs):
        """
        For details of certain parameters check the
        `Process API reference <https://docs.sentinel-hub.com/api/latest/reference/#operation/process>`_.

        :param evalscript: `Evalscript <https://docs.sentinel-hub.com/api/latest/#/Evalscript/>`__.
        :type evalscript: str
        :param input_data: A list of input dictionary objects as described in the API reference. It can be generated
            with the helper method `SentinelHubRequest.input_data`
        :type input_data: List[dict or InputDataDict]
        :param responses: A list of `output.responses` objects as described in the API reference. It can be generated
            with the helper function `SentinelHubRequest.output_response`
        :type responses: List[dict]
        :param bbox: Bounding box describing the area of interest.
        :type bbox: sentinelhub.BBox
        :param geometry: Geometry describing the area of interest.
        :type geometry: sentinelhub.Geometry
        :param size: Size of the image.
        :type size: Tuple[int, int]
        :param resolution: Resolution of the image. It has to be in units compatible with the given CRS.
        :type resolution: Tuple[float, float]
        :param data_folder: location of the directory where the fetched data will be saved.
        :type data_folder: str
        :param config: A custom instance of config class to override parameters from the saved configuration.
        :type config: SHConfig or None
        """
        if not isinstance(evalscript, str):
            raise ValueError("'evalscript' should be a string")

        parsed_mime_type = MimeType.from_string(responses[0]['format']['type'].split('/')[1])
        self.mime_type = MimeType.TAR if len(responses) > 1 else parsed_mime_type

        self.payload = self.body(
            request_bounds=self.bounds(bbox=bbox, geometry=geometry),
            request_data=input_data,
            request_output=self.output(size=size, resolution=resolution, responses=responses),
            evalscript=evalscript
        )

        super().__init__(SentinelHubDownloadClient, **kwargs)

    @staticmethod
    def body(request_bounds, request_data, evalscript, request_output=None, other_args=None):
        """ Generate the Process API request body

        :param request_bounds: A dictionary as generated by `SentinelHubRequest.bounds` helper method.
        :type request_bounds: dict
        :param request_data: A list of dictionaries as generated by `SentinelHubRequest.input_data` helper method.
        :type request_data: List[dict]
        :param evalscript: `Evalscript <https://docs.sentinel-hub.com/api/latest/#/Evalscript/>`__.
        :type evalscript: str
        :param request_output: A dictionary as generated by `SentinelHubRequest.output` helper method.
        :type request_output: dict
        :param other_args: Additional dictionary of arguments. If provided, the resulting dictionary will get updated
            by it.
        :param other_args: dict
        """
        request_body = {
            'input': {
                'bounds': request_bounds,
                'data': request_data
            },
            'evalscript': evalscript
        }

        if request_output is not None:
            request_body['output'] = request_output

        if other_args:
            _update_other_args(request_body, other_args)

        return request_body

    @staticmethod
    def output_response(identifier, response_format, other_args=None):
        """ Generate an element of `output.responses` as described in the Process API reference.

        :param identifier: Identifier of the output response.
        :type identifier: str
        :param response_format: A mime type of one of 'png', 'json', 'jpeg', 'tiff'.
        :type response_format: str or sentinelhub.MimeType
        :param other_args: Additional dictionary of arguments. If provided, the resulting dictionary will get updated
            by it.
        :param other_args: dict
        """
        output_response = {
            'identifier': identifier,
            'format': {
                'type': MimeType(response_format).get_string()
            }
        }

        if other_args:
            _update_other_args(output_response, other_args)

        return output_response

    @staticmethod
    def output(responses, size=None, resolution=None, other_args=None):
        """ Generate an `output` part of the request as described in the Process API reference

        :param responses: A list of objects in `output.responses` as generated by `SentinelHubRequest.output_response`.
        :type responses: List[dict]
        :param size: Size of the image.
        :type size: Tuple[int, int]
        :param resolution: Resolution of the image. It has to be in units compatible with the given CRS.
        :type resolution: Tuple[float, float]
        :param other_args: Additional dictionary of arguments. If provided, the resulting dictionary will get updated
            by it.
        :param other_args: dict
        """
        if size and resolution:
            raise ValueError('Either size or resolution argument should be given, not both.')

        request_output = {
            'responses': responses
        }

        if size:
            request_output['width'], request_output['height'] = size
        if resolution:
            request_output['resx'], request_output['resy'] = resolution

        if other_args:
            _update_other_args(request_output, other_args)

        return request_output
