# -*- coding: utf-8 -*-

"""
***************************************************************************
    OtbAlgorithmsTest.py
    ---------------------
    Date                 : January 2019
    Copyright            : (C) 2019 by CNES
    Author               : otb att cnes dot fr
***************************************************************************
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
*                                                                         *
***************************************************************************
"""

__author__ = 'Rashad Kanavath'
__date__ = 'Janauary 2019'
__copyright__ = '(C) 2019, CNES'

import os
import sys
import unittest
import hashlib
import shutil
import nose2
import tempfile
from qgis.core import (QgsProcessingParameterNumber,
                       QgsApplication,
                       QgsRasterLayer,
                       QgsMapLayer,
                       QgsProject,
                       QgsProcessingContext,
                       QgsProcessingUtils,
                       QgsProcessingFeedback,
                       QgsProcessingParameterDefinition)
from qgis.testing import start_app, unittest
from processing.core.ProcessingConfig import ProcessingConfig, Setting
from processing.gui.AlgorithmDialog import AlgorithmDialog
from processing.gui.BatchAlgorithmDialog import BatchAlgorithmDialog
from processing.gui.wrappers import *
from processing.modeler.ModelerParametersDialog import ModelerParametersDialog
from processing.algs.otb.OtbAlgorithm import OtbAlgorithm
from processing.algs.otb.OtbAlgorithmProvider import OtbAlgorithmProvider
from processing.algs.otb.OtbUtils import OtbUtils
from processing.algs.otb.OtbChoiceWidget import OtbParameterChoice, OtbChoiceWidgetWrapper
import AlgorithmsTestBase

import processing
OTB_INSTALL_DIR = os.environ.get('OTB_INSTALL_DIR')


class TestOtbAlgorithms(unittest.TestCase, AlgorithmsTestBase.AlgorithmsTest):

    @staticmethod
    def __input_raster_layer():
        options = QgsRasterLayer.LayerOptions()
        options.loadDefaultStyle = False
        return QgsRasterLayer(os.path.join(AlgorithmsTestBase.processingTestDataPath(), 'raster.tif'),
                              "raster_input",
                              'gdal',
                              options)

    def test_bug21373_mode_vector(self):
        """
        This issue is reported on qgis bug tracker: #21373
        This issue is reported on qgis-otb-plugin tracker: #30
        """
        context = QgsProcessingContext()
        context.setProject(QgsProject.instance())
        feedback = QgsProcessingFeedback()
        parameters = {
            'in': TestOtbAlgorithms.__input_raster_layer(),
            'filter': 'meanshift',
            'mode.vector.out': 'vector.shp'
        }
        alg = OtbAlgorithm('Segmentation', 'Segmentation', os.path.join(self.descrFolder, 'Segmentation.txt'))
        results = alg.processAlgorithm(parameters, context, feedback)
        self.assertDictEqual(results, {'mode.vector.out': 'vector.shp'})

    def test_bug21373_mode_raster(self):
        """
        This issue is reported on qgis bug tracker: #21373
        """
        context = QgsProcessingContext()
        context.setProject(QgsProject.instance())
        feedback = QgsProcessingFeedback()
        parameters = {
            'in': TestOtbAlgorithms.__input_raster_layer(),
            'filter': 'meanshift',
            'mode': 'raster',
            'mode.raster.out': 'raster.tif'
        }
        alg = OtbAlgorithm('Segmentation', 'Segmentation', os.path.join(self.descrFolder, 'Segmentation.txt'))
        results = alg.processAlgorithm(parameters, context, feedback)
        self.assertDictEqual(results, {'mode.raster.out': 'raster.tif'})

    def test_bug21374_Fail(self):
        """
        This issue is reported on qgis bug tracker: #21374
        """
        outdir = tempfile.mkdtemp()
        self.cleanup_paths.append(outdir)
        context = QgsProcessingContext()
        context.setProject(QgsProject.instance())
        feedback = QgsProcessingFeedback()
        parameters = {
            'in': TestOtbAlgorithms.__input_raster_layer(),
            'filter': 'cc',
            'mode.vector.out': os.path.join(outdir, 'vector.shp')
        }

        alg = OtbAlgorithm('Segmentation', 'Segmentation', os.path.join(self.descrFolder, 'Segmentation.txt'))
        ok, msg = alg.checkParameterValues(parameters, context)
        self.assertFalse(ok, 'Algorithm failed checkParameterValues with result {}'.format(msg))

    def test_init_algorithms(self):
        """
        This test will read each otb algorithm in 'algs.txt'
        and creates an instance of OtbAlgorithm and check if it can be executed
        This is done in :class: `OtbAlgorithmProvider` load() method
        """
        algs_txt = os.path.join(self.descrFolder, 'algs.txt')
        with open(algs_txt) as lines:
            line = lines.readline().strip('\n').strip()
            if line != '' and line.startswith('#'):
                version = line[1:]
                print('version =', version)
                line = lines.readline().strip('\n').strip()
            while line != '' and not line.startswith('#'):
                data = line.split('|')
                descriptionFile = os.path.join(self.descrFolder, str(data[1]) + '.txt')
                alg = OtbAlgorithm(data[0], data[1], descriptionFile)
                self.assertIsInstance(alg, OtbAlgorithm)
                ret, msg = alg.canExecute()
                print("canExecute '{}' - {}".format(alg.id(), ret))
                self.assertEqual(ret, True)
                line = lines.readline().strip('\n').strip()

    def test_parameterAs_ScriptMode(self):
        """
        This test will pass an instance of QgsCoordinateReferenceSystem for 'epsg' parameter
        of otb::Rasterization. There is same test in otb_algorithm_tests.yaml which passes
        an instance of str for epsg parameter.
        """
        outdir = tempfile.mkdtemp()
        self.cleanup_paths.append(outdir)

        context = QgsProcessingContext()
        context.setProject(QgsProject.instance())
        feedback = QgsProcessingFeedback()

        vectorFile = os.path.join(AlgorithmsTestBase.processingTestDataPath(), 'polys.gml')
        vectorLayer = QgsProcessingUtils.mapLayerFromString(vectorFile, context)
        parameters = {
            'in': vectorLayer,
            'epsg': QgsCoordinateReferenceSystem('EPSG:4326'),
            'spx': 1.0,
            'spy': 1.0,
            'outputpixeltype': 1,
            'out': os.path.join(outdir, 'raster.tif')
        }
        results = processing.run('otb:Rasterization', parameters, None, feedback)
        result_lyr = QgsProcessingUtils.mapLayerFromString(results['out'], context)
        self.assertTrue(result_lyr.isValid())

    def test_OTBParameterChoiceExists(self):
        """
        This test is here to know if we have change `type()` method of :class: `OtbParameterChoice`
        That value is used by Otb when it creates descriptor files. So changes to this string must be test
        in a unit-test.
        """
        alg_smoothing = OtbAlgorithm('Image Filtering', 'Smoothing', os.path.join(self.descrFolder, 'Smoothing.txt'))
        found = False
        for param in alg_smoothing.parameterDefinitions():
            ## print (param.name(), param.type())
            if param.type() == 'OTBParameterChoice':
                found = True
                break
        self.assertEqual(found, True)

    def test_OTBParameterChoice_Gui(self):
        """
        This test is similar to GuiTests in processing that is done on other parameter widget in processing
        Main difference is this test uses create_wrapper_from_metadata() rather than create_wrapper_from_class()
        like rest of processing widgets.
        """
        param = OtbParameterChoice('test')

        alg = QgsApplication.processingRegistry().createAlgorithmById('otb:Smoothing')
        # algorithm dialog
        dlg = AlgorithmDialog(alg)
        wrapper = WidgetWrapperFactory.create_wrapper_from_metadata(param, dlg)
        self.assertIsNotNone(wrapper)
        self.assertIsInstance(wrapper, OtbChoiceWidgetWrapper)
        self.assertEqual(wrapper.dialog, dlg)
        self.assertIsNotNone(wrapper.widget)

        alg = QgsApplication.processingRegistry().createAlgorithmById('otb:Smoothing')
        # batch dialog
        dlg = BatchAlgorithmDialog(alg)
        wrapper = WidgetWrapperFactory.create_wrapper_from_metadata(param, dlg)
        self.assertIsNotNone(wrapper)
        self.assertIsInstance(wrapper, OtbChoiceWidgetWrapper)
        self.assertEqual(wrapper.dialog, dlg)
        self.assertIsNotNone(wrapper.widget)

        alg = QgsApplication.processingRegistry().createAlgorithmById('otb:Smoothing')
        # modeler dialog
        model = QgsProcessingModelAlgorithm()
        dlg = ModelerParametersDialog(alg, model)
        wrapper = WidgetWrapperFactory.create_wrapper_from_metadata(param, dlg)
        self.assertIsNotNone(wrapper)
        self.assertIsInstance(wrapper, OtbChoiceWidgetWrapper)
        self.assertEqual(wrapper.dialog, dlg)
        self.assertIsNotNone(wrapper.widget)

    @classmethod
    def setUpClass(cls):
        start_app()
        from processing.core.Processing import Processing
        Processing.initialize()
        ProcessingConfig.setSettingValue("OTB_ACTIVATE", True)
        ProcessingConfig.setSettingValue(OtbUtils.FOLDER, OTB_INSTALL_DIR)
        ProcessingConfig.setSettingValue(OtbUtils.APP_FOLDER, os.path.join(OTB_INSTALL_DIR, 'lib', 'otb', 'applications'))
        ProcessingConfig.readSettings()
        # Refresh OTB Algorithms after settings are changed.
        for p in QgsApplication.processingRegistry().providers():
            if p.id() == "otb":
                p.refreshAlgorithms()
        cls.descrFolder = os.path.join(OTB_INSTALL_DIR, 'share', 'otb', 'description')
        cls.cleanup_paths = []

    @classmethod
    def tearDownClass(cls):
        from processing.core.Processing import Processing
        Processing.deinitialize()
        for path in cls.cleanup_paths:
            shutil.rmtree(path)

    def test_definition_file(self):
        """
        return name of yaml file containing test definitions
        """
        print("OTB_INSTALL_DIR = '{}'".format(OTB_INSTALL_DIR))
        return 'otb_algorithm_tests.yaml'


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