import unittest
import datetime
import numpy as np

from shapely.geometry import MultiPolygon

from sentinelhub import WmsRequest, WcsRequest, CRS, MimeType, CustomUrlParam, ServiceType, DataSource, BBox,\
    WebFeatureService, DownloadFailedException, TestSentinelHub, TestCaseContainer
from sentinelhub.data_request import OgcRequest
from sentinelhub.ogc import OgcImageService


class TestOgc(TestSentinelHub):

    class OgcTestCase(TestCaseContainer):
        """
        Container for each test case of sentinelhub OGC functionalities
        """
        def __init__(self, name, request, result_len, save_data=False, **kwargs):
            super().__init__(name, request, **kwargs)

            self.result_len = result_len
            self.save_data = save_data

            self.data = None

        def collect_data(self):
            if self.save_data:
                self.request.save_data(redownload=True, data_filter=self.data_filter)
                self.data = self.request.get_data(save_data=True, data_filter=self.data_filter)
            else:
                self.data = self.request.get_data(redownload=True, data_filter=self.data_filter)

    @classmethod
    def setUpClass(cls):
        super().setUpClass()

        wgs84_bbox = BBox(bbox=(-5.23, 48.0, -5.03, 48.17), crs=CRS.WGS84)
        wgs84_bbox_2 = BBox(bbox=(21.3, 64.0, 22.0, 64.5), crs=CRS.WGS84)
        wgs84_bbox_3 = BBox(bbox=(-72.0, -70.4, -71.8, -70.2), crs=CRS.WGS84)
        wgs84_bbox_4 = BBox(bbox=(-72.0, -66.4, -71.8, -66.2), crs=CRS.WGS84)
        wgs84_bbox_byoc = BBox(bbox=(13.82387, 45.85221, 13.83313, 45.85901), crs=CRS.WGS84)
        pop_web_bbox = BBox(bbox=(1292344.0, 5195920.0, 1310615.0, 5214191.0), crs=CRS.POP_WEB)
        geometry_wkt_pop_web = 'POLYGON((1292344.0 5205055.5, 1301479.5 5195920.0, 1310615.0 5205055.5, ' \
                               '1301479.5 5214191.0, 1292344.0 5205055.5))'
        geometry_wkt_wgs84 = 'POLYGON((-5.13 48, -5.23 48.09, -5.13 48.17, -5.03 48.08, -5.13 48))'
        img_width = 100
        img_height = 100
        resx = '53m'
        resy = '78m'
        expected_date = datetime.datetime.strptime('2017-10-07T11:20:58', '%Y-%m-%dT%H:%M:%S')

        cls.test_cases = [
            cls.OgcTestCase('generalWmsTest',
                            OgcRequest(data_folder=cls.OUTPUT_FOLDER, image_format=MimeType.TIFF_d32f, bbox=wgs84_bbox,
                                       layer='BANDS-S2-L1C', maxcc=0.5, size_x=img_width, size_y=img_height,
                                       time=(datetime.date(year=2017, month=1, day=5),
                                             datetime.date(year=2017, month=12, day=16)),
                                       service_type=ServiceType.WMS, time_difference=datetime.timedelta(days=10)),
                            result_len=14, img_min=0.0, img_max=1.5964, img_mean=0.1810, img_median=0.1140, tile_num=29,
                            save_data=True, data_filter=[0, -2, 0]),
            cls.OgcTestCase('generalWcsTest',
                            OgcRequest(data_folder=cls.OUTPUT_FOLDER, image_format=MimeType.TIFF_d32f, bbox=wgs84_bbox,
                                       layer='BANDS-S2-L1C', maxcc=0.6, size_x=resx, size_y=resy,
                                       time=(datetime.datetime(year=2017, month=10, day=7, hour=1),
                                             datetime.datetime(year=2017, month=12, day=11)),
                                       service_type=ServiceType.WCS, time_difference=datetime.timedelta(hours=1)),
                            result_len=4, img_min=0.0002, img_max=0.5266, img_mean=0.1038, img_median=0.0948,
                            tile_num=6, date_check=expected_date, save_data=True, data_filter=[0, -1]),
            # CustomUrlParam tests:
            cls.OgcTestCase('customUrlLogoQualitySampling',
                            WmsRequest(data_folder=cls.OUTPUT_FOLDER, image_format=MimeType.PNG,
                                       layer='TRUE-COLOR-S2-L1C', width=img_width, bbox=wgs84_bbox,
                                       time=('2017-10-01', '2017-10-02'),
                                       custom_url_params={CustomUrlParam.SHOWLOGO: True,
                                                          CustomUrlParam.QUALITY: 100,
                                                          CustomUrlParam.DOWNSAMPLING: 'BICUBIC',
                                                          CustomUrlParam.UPSAMPLING: 'BICUBIC'}),
                            result_len=1, img_min=29, img_max=255, img_mean=179.833917, img_median=181, tile_num=2,
                            data_filter=[0, -1]),
            cls.OgcTestCase('customUrlPreview',
                            WmsRequest(data_folder=cls.OUTPUT_FOLDER, image_format=MimeType.PNG,
                                       layer='TRUE-COLOR-S2-L1C', height=img_height, bbox=wgs84_bbox,
                                       time=('2017-10-01', '2017-10-02'),
                                       custom_url_params={CustomUrlParam.PREVIEW: 2}),
                            result_len=1, img_min=27, img_max=244, img_mean=175.51357, img_median=177, tile_num=2),
            cls.OgcTestCase('customUrlEvalscripturl',
                            WcsRequest(data_folder=cls.OUTPUT_FOLDER, image_format=MimeType.PNG,
                                       layer='TRUE-COLOR-S2-L1C', resx=resx, resy=resy, bbox=pop_web_bbox,
                                       time=('2017-10-01', '2017-10-02'),
                                       custom_url_params={CustomUrlParam.EVALSCRIPTURL:
                                                              'https://raw.githubusercontent.com/sentinel-hub/'
                                                              'customScripts/master/sentinel-2/false_color_infrared/'
                                                              'script.js'}),
                            result_len=1, img_min=46, img_max=255, img_mean=231.051154, img_median=255, tile_num=3),
            cls.OgcTestCase('customUrlEvalscript,Transparent,Geometry',
                            WcsRequest(data_folder=cls.OUTPUT_FOLDER, image_format=MimeType.PNG,
                                       layer='TRUE-COLOR-S2-L1C', resx=resx, resy=resy, bbox=wgs84_bbox,
                                       time=('2017-10-01', '2017-10-02'),
                                       custom_url_params={CustomUrlParam.TRANSPARENT: True,
                                                          CustomUrlParam.EVALSCRIPT: 'return [B10,B8A, B03 ]',
                                                          CustomUrlParam.GEOMETRY: geometry_wkt_wgs84}),
                            result_len=1, img_min=0, img_max=255, img_mean=50.726361, img_median=1.0, tile_num=2),
            cls.OgcTestCase('FalseLogo,BgColor,Geometry',
                            WmsRequest(data_folder=cls.OUTPUT_FOLDER, image_format=MimeType.PNG,
                                       layer='TRUE-COLOR-S2-L1C', width=img_width, height=img_height, bbox=pop_web_bbox,
                                       time=('2017-10-01', '2017-10-02'),
                                       custom_url_params={CustomUrlParam.SHOWLOGO: False,
                                                          CustomUrlParam.BGCOLOR: "F4F86A",
                                                          CustomUrlParam.GEOMETRY: geometry_wkt_pop_web}),
                            result_len=1, img_min=64, img_max=MimeType.PNG.get_expected_max_value(), img_mean=213.6124,
                            img_median=242.0, tile_num=3),
            # DataSource tests:
            cls.OgcTestCase('S2 L1C Test',
                            WmsRequest(data_source=DataSource.SENTINEL2_L1C, data_folder=cls.OUTPUT_FOLDER,
                                       image_format=MimeType.TIFF_d8, layer='BANDS-S2-L1C',
                                       width=img_width, height=img_height, bbox=wgs84_bbox,
                                       time=('2017-10-01', '2017-10-02')),
                            result_len=1, img_min=0, img_max=160, img_mean=60.50514, img_median=63.0,
                            tile_num=2),
            cls.OgcTestCase('S2 L2A Test',
                            WmsRequest(data_source=DataSource.SENTINEL2_L2A, data_folder=cls.OUTPUT_FOLDER,
                                       image_format=MimeType.TIFF, layer='BANDS-S2-L2A',
                                       width=img_width, height=img_height, bbox=wgs84_bbox,
                                       time=('2017-10-01', '2017-10-02')),
                            result_len=1, img_min=0.0, img_max=MimeType.TIFF.get_expected_max_value(),
                            img_mean=22744.01498, img_median=21391.0, tile_num=2),
            cls.OgcTestCase('L8 Test',
                            WmsRequest(data_source=DataSource.LANDSAT8, data_folder=cls.OUTPUT_FOLDER,
                                       image_format=MimeType.TIFF_d32f, layer='BANDS-L8',
                                       width=img_width, height=img_height, bbox=wgs84_bbox,
                                       time=('2017-10-05', '2017-10-10'), time_difference=datetime.timedelta(hours=1)),
                            result_len=1, img_min=0.0012, img_max=285.78003, img_mean=52.06088, img_median=0.5192,
                            tile_num=2),
            cls.OgcTestCase('DEM Test',
                            WmsRequest(data_source=DataSource.DEM, data_folder=cls.OUTPUT_FOLDER,
                                       image_format=MimeType.TIFF_d32f, layer='DEM',
                                       width=img_width, height=img_height, bbox=wgs84_bbox),
                            result_len=1, img_min=-108.0, img_max=-18.0, img_mean=-72.2097, img_median=-72.0),
            cls.OgcTestCase('MODIS Test',
                            WmsRequest(data_source=DataSource.MODIS, data_folder=cls.OUTPUT_FOLDER,
                                       image_format=MimeType.TIFF_d32f, layer='BANDS-MODIS',
                                       width=img_width, height=img_height, bbox=wgs84_bbox, time='2017-10-01'),
                            result_len=1, img_min=0.0, img_max=3.2767, img_mean=0.136548, img_median=0.00240,
                            tile_num=1),
            cls.OgcTestCase('S1 IW Test',
                            WmsRequest(data_source=DataSource.SENTINEL1_IW, data_folder=cls.OUTPUT_FOLDER,
                                       image_format=MimeType.TIFF_d32f, layer='BANDS-S1-IW',
                                       width=img_width, height=img_height, bbox=wgs84_bbox,
                                       time=('2017-10-01', '2017-10-02'),
                                       time_difference=datetime.timedelta(hours=1)),
                            result_len=1, img_min=0.00150, img_max=MimeType.TIFF_d32f.get_expected_max_value(),
                            img_mean=0.1135815, img_median=0.06210, tile_num=2),
            cls.OgcTestCase('S1 EW Test',
                            WmsRequest(data_source=DataSource.SENTINEL1_EW, data_folder=cls.OUTPUT_FOLDER,
                                       image_format=MimeType.TIFF_d32f, layer='BANDS-S1-EW',
                                       width=img_width, height=img_height, bbox=wgs84_bbox_2,
                                       time=('2018-2-7', '2018-2-8'),
                                       time_difference=datetime.timedelta(hours=1)),
                            result_len=2, img_min=0.0003, img_max=1.0, img_mean=0.53118, img_median=1.0, tile_num=3),
            cls.OgcTestCase('S1 EW SH Test',
                            WmsRequest(data_source=DataSource.SENTINEL1_EW_SH,
                                       data_folder=cls.OUTPUT_FOLDER,
                                       image_format=MimeType.TIFF_d16, layer='BANDS-S1-EW-SH',
                                       width=img_width, height=img_height, bbox=wgs84_bbox_3,
                                       time=('2018-2-6', '2018-2-8'),
                                       time_difference=datetime.timedelta(hours=1)),
                            result_len=1, img_min=472, img_max=3539, img_mean=926.8818, img_median=891.0,
                            tile_num=1),
            cls.OgcTestCase('S1 IW ASC Test',
                            WmsRequest(data_source=DataSource.SENTINEL1_IW_ASC, data_folder=cls.OUTPUT_FOLDER,
                                       image_format=MimeType.TIFF_d32f, layer='BANDS-S1-IW',
                                       width=img_width, height=img_height, bbox=wgs84_bbox,
                                       time=('2017-10-01', '2017-10-03'),
                                       time_difference=datetime.timedelta(hours=1)),
                            result_len=1, img_min=0.00150, img_max=MimeType.TIFF_d32f.get_expected_max_value(),
                            img_mean=0.1135815, img_median=0.0621000, tile_num=2),
            cls.OgcTestCase('S1 EW ASC Test',
                            WmsRequest(data_source=DataSource.SENTINEL1_EW_ASC, data_folder=cls.OUTPUT_FOLDER,
                                       image_format=MimeType.TIFF_d32f, layer='BANDS-S1-EW',
                                       width=img_width, height=img_height, bbox=wgs84_bbox_2,
                                       time=('2018-2-7', '2018-2-8'),
                                       time_difference=datetime.timedelta(hours=1)),
                            result_len=1, img_min=0.0003, img_max=0.2322, img_mean=0.02199, img_median=0.0102,
                            tile_num=2),
            cls.OgcTestCase('S1 EW SH ASC Test',
                            WmsRequest(data_source=DataSource.SENTINEL1_EW_SH_ASC,
                                       data_folder=cls.OUTPUT_FOLDER,
                                       image_format=MimeType.TIFF_d16, layer='BANDS-S1-EW-SH',
                                       width=img_width, height=img_height, bbox=wgs84_bbox_3,
                                       time=('2018-2-6', '2018-2-8'),
                                       time_difference=datetime.timedelta(hours=1)),
                            result_len=1, img_min=472, img_max=3539, img_mean=926.8818, img_median=891.0,
                            tile_num=1),
            cls.OgcTestCase('S1 IW DES Test',
                            WmsRequest(data_source=DataSource.SENTINEL1_IW_DES, data_folder=cls.OUTPUT_FOLDER,
                                       image_format=MimeType.TIFF_d32f, layer='BANDS-S1-IW',
                                       width=img_width, height=img_height, bbox=wgs84_bbox,
                                       time=('2017-10-01', '2017-10-05'),
                                       time_difference=datetime.timedelta(hours=1)),
                            result_len=1, img_min=0.0, img_max=0.07700,
                            img_mean=0.0210801, img_median=0.0132, tile_num=1),
            cls.OgcTestCase('S1 EW DES Test',
                            WmsRequest(data_source=DataSource.SENTINEL1_EW_DES, data_folder=cls.OUTPUT_FOLDER,
                                       image_format=MimeType.TIFF_d32f, layer='BANDS-S1-EW',
                                       width=img_width, height=img_height, bbox=wgs84_bbox_2,
                                       time=('2018-2-7', '2018-2-8'),
                                       time_difference=datetime.timedelta(hours=1)),
                            result_len=1, img_min=0.0003, img_max=1.0, img_mean=0.53118, img_median=1.0, tile_num=1),
            cls.OgcTestCase('S1 EW SH DES Test',
                            WmsRequest(data_source=DataSource.SENTINEL1_EW_SH_DES,
                                       data_folder=cls.OUTPUT_FOLDER,
                                       image_format=MimeType.TIFF_d16, layer='BANDS-S1-EW-SH',
                                       width=img_width, height=img_height, bbox=wgs84_bbox_4,
                                       time=('2018-2-5', '2018-2-6'),
                                       time_difference=datetime.timedelta(hours=1)),
                            result_len=1, img_min=3978, img_max=37971, img_mean=15061.2035, img_median=14772.0,
                            tile_num=1),
            # cls.OgcTestCase('BYOC Test',
            #                 WmsRequest(data_source=DataSource('31df1de4-8bd4-43e0-8c3f-b04262d111b6'),
            #                            data_folder=cls.OUTPUT_FOLDER,
            #                            image_format=MimeType.TIFF_d16, layer='DEMO_BYOC_LAYER',
            #                            width=img_width, height=img_height, bbox=wgs84_bbox_byoc,
            #                            custom_url_params={CustomUrlParam.SHOWLOGO: False}),
            #                 result_len=1, img_min=0, img_max=65535, img_mean=17158.8938, img_median=15728.0,
            #                 tile_num=1)
        ]
        """
        # Test case for eocloud data source
        cls.test_cases.extend([
            cls.OgcTestCase('EOCloud S1 IW Test',
                            WmsRequest(data_source=DataSource.SENTINEL1_IW, data_folder=cls.OUTPUT_FOLDER,
                                       image_format=MimeType.TIFF_d32f, layer='BANDS_S1_IW',
                                       width=img_width, height=img_height, bbox=wgs84_bbox,
                                       time=('2017-10-01', '2017-10-02'),
                                       time_difference=datetime.timedelta(hours=1)),
                            result_len=1, img_min=0.0, img_max=0.49706, img_mean=0.04082, img_median=0.00607,
                            tile_num=2),
        ])
        """

        for test_case in cls.test_cases:
            test_case.collect_data()

    def test_return_type(self):
        for test_case in self.test_cases:
            with self.subTest(msg='Test case {}'.format(test_case.name)):
                self.assertTrue(isinstance(test_case.data, list), "Expected a list")
                result_len = test_case.result_len if test_case.data_filter is None else len(test_case.data_filter)
                self.assertEqual(len(test_case.data), result_len,
                                 "Expected a list of length {}, got length {}".format(result_len, len(test_case.data)))

    def test_wfs(self):
        for test_case in self.test_cases:
            with self.subTest(msg='Test case {}'.format(test_case.name)):
                tile_iter = test_case.request.get_tiles()
                tile_n = len(list(tile_iter)) if tile_iter else None

                self.assertEqual(tile_n, test_case.tile_num,
                                 "Expected {} tiles, got {}".format(test_case.tile_num, tile_n))

    def test_download_url(self):
        for test_case in self.test_cases:
            if test_case.url_check is not None:
                with self.subTest(msg='Test case {}'.format(test_case.name)):
                    download_url = self.request.get_url_list()[0]
                    self.assertTrue(test_case.url_check in download_url,
                                    "Parameter '{}' not in download url {}.".format(test_case.url_check, download_url))

    def test_get_dates(self):
        for test_case in self.test_cases:
            if test_case.date_check is not None:
                with self.subTest(msg='Test case {}'.format(test_case.name)):
                    dates = OgcImageService().get_dates(test_case.request)
                    self.assertEqual(len(dates), test_case.result_len,
                                     msg="Expected {} dates, got {}".format(test_case.result_len, len(dates)))
                    self.assertEqual(test_case.date_check, dates[0],
                                     msg="Expected date {}, got {}".format(test_case.date_check, dates[0]))

    def test_filter(self):
        for test_case in self.test_cases:
            if test_case.data_filter is not None:
                with self.subTest(msg='Test case {}'.format(test_case.name)):
                    if (test_case.data_filter[0] - test_case.data_filter[-1]) % test_case.result_len == 0:
                        self.assertTrue(np.array_equal(test_case.data[0], test_case.data[-1]),
                                        msg="Expected first and last output to be equal, got different")
                    else:
                        self.assertFalse(np.array_equal(test_case.data[0], test_case.data[-1]),
                                         msg="Expected first and last output to be different, got the same")

    def test_stats(self):
        for test_case in self.test_cases:
            self.test_numpy_data(test_case.data[0], exp_min=test_case.img_min, exp_max=test_case.img_max,
                                 exp_mean=test_case.img_mean, exp_median=test_case.img_median,
                                 test_name=test_case.name)

    def test_to_large_request(self):
        bbox = BBox((-5.23, 48.0, -5.03, 48.17), CRS.WGS84)
        request = WmsRequest(layer='TRUE-COLOR-S2-L1C', height=6000, width=6000, bbox=bbox,
                             time=('2017-10-01', '2017-10-02'))

        with self.assertRaises(DownloadFailedException):
            request.get_data()


class TestWebFeatureService(TestSentinelHub):

    @classmethod
    def setUpClass(cls):
        super().setUpClass()

        wgs84_bbox = BBox(bbox=(-5.23, 48.0, -5.03, 48.17), crs=CRS.WGS84)
        time_interval = (datetime.date(year=2017, month=1, day=5), datetime.date(year=2017, month=12, day=16))

        cls.test_cases = [
            TestCaseContainer('General test',
                              WebFeatureService(wgs84_bbox, time_interval, data_source=DataSource.SENTINEL2_L1C,
                                                maxcc=0.1),
                              result_len=13),
            TestCaseContainer('Latest date only',
                              WebFeatureService(wgs84_bbox, 'latest', data_source=DataSource.SENTINEL2_L2A),
                              result_len=1),
        ]

    def test_results(self):
        for test_case in self.test_cases:
            with self.subTest(msg='Test case {}'.format(test_case.name)):
                iterator = test_case.request

                features = list(iterator)
                dates = iterator.get_dates()
                geometries = iterator.get_geometries()
                tiles = iterator.get_tiles()

                for result_list, expected_type in [(features, dict), (dates, datetime.datetime),
                                                   (geometries, MultiPolygon), (tiles, tuple)]:
                    self.assertEqual(len(result_list), test_case.result_len)
                    for result in result_list:
                        self.assertTrue(isinstance(result, expected_type),
                                        msg='Expected type {}, got type {}'.format(expected_type, type(result)))


if __name__ == '__main__':
    unittest.main()
