# -*- coding: utf-8 -*-
# Copyright 2021 IRT Saint Exupéry, https://www.irt-saintexupery.com
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License version 3 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

# Contributors:
#    INITIAL AUTHORS - API and implementation and/or documentation
#        :author: Francois Gallard
#    OTHER AUTHORS   - MACROSCOPIC CHANGES
"""
Base class for all optimization post processings
************************************************
"""
from __future__ import absolute_import, division, unicode_literals

import inspect
from os.path import abspath, dirname, exists, join, splitext

import matplotlib
import pylab
from future import standard_library

from gemseo.core.grammar import InvalidDataException
from gemseo.core.json_grammar import JSONGrammar
from gemseo.utils.source_parsing import SourceParsing

standard_library.install_aliases()


class OptPostProcessor(object):
    """Abstract class for optimization post processing methods"""

    def __init__(self, opt_problem):
        """
        Constructor

        :param opt_problem: the optimization problem to run
        """
        self.opt_problem = opt_problem
        self.database = opt_problem.database
        cls_name = self.__class__.__name__
        name = cls_name + "_options"
        f_class = inspect.getfile(self.__class__)
        comp_dir = abspath(dirname(f_class))
        schema_file = join(comp_dir, name + ".json")
        if not exists(schema_file):
            schema_file = join(comp_dir, "options", name + ".json")
        if not exists(schema_file):
            raise ValueError(
                "Options grammar for optimization "
                + " post processor does not exists"
                + ", expected: "
                + str(schema_file)
                + "or: "
                + str(join(comp_dir, name + ".json"))
            )
        self.opt_grammar = JSONGrammar(name, schema_file=schema_file)
        if hasattr(self.__class__, "_plot"):
            descr_opts = SourceParsing.get_options_doc(
                self.__class__._plot
            )  # pylint: disable=E1101
            self.opt_grammar.add_description(descr_opts)
        if hasattr(self.__class__, "_run"):
            descr_opts = SourceParsing.get_options_doc(self.__class__._run)
            self.opt_grammar.add_description(descr_opts)
        # the data dict to eventually rebuild the plot in another framework
        self.out_data_dict = {}
        # Store output files generated by the post processing
        self.output_files = []

    def execute(self, **options):
        """
        Executes the post processing

        :param options: options dict, see associated JSON file
        """
        self.check_options(**options)
        if not self.opt_problem.database:
            raise ValueError(
                "Optimization problem was not solved,"
                + " cannot run post processing "
                + str(self.__class__.__name__)
            )
        self._run(**options)

    def check_options(self, **options):
        """
        Checks the options of the post processor

        :param options: options dict, see associated JSON file
        """
        try:
            self.opt_grammar.load_data(options)
        except InvalidDataException:
            name = self.__class__.__name__
            raise InvalidDataException(
                "Invalid options for post processing " + name + " got : " + str(options)
            )

    def _run(self, **options):
        """Visualizes the optimization history

        :param options: options of the plot,
            see associated JSON file
        """
        self._plot(**options)

    def _plot(self, **options):
        """Visualizers

        :param options: options dict, see associated JSON file

        """
        raise NotImplementedError()

    def _save_and_show(self, fig, file_path, save=True, show=False, extension="pdf"):
        """
        Saves figures and or shows it depending on options

        :param fig: matplotlib figure
        :param file_path: file path to save the plot. If it does not end with
            the specified extension, the extension is replaced
        :param save: save the figure (default: True)
        :param show: show the figure (default: False)
        :param extension: file extension (default: 'pdf')
        """
        matplotlib.rcParams.update({"font.size": 10})
        if save:
            if not file_path.endswith(extension):
                basen = splitext(file_path)[0]
                file_path = basen + "." + extension
            fig.savefig(file_path, bbox_inches="tight")
            self.output_files.append(file_path)
        if show:
            try:
                pylab.plt.show(fig)
            except TypeError:
                pylab.plt.show()
