import pandas as pd
import os
import pathlib
import numpy as np

import sys

sys.path.append("..")


class TabularSpecies:
    """
    Class holding pandas data frame and helper dicts on all MNP species
    """

    def __init__(self):
        filepath = pathlib.Path(__file__)

        # De volledige Species tabel als Pandas dataframe
        self.df = pd.read_csv(
            r"https://git.wur.nl/roelo008/benb_utils/-/raw/master/resources/mnp_species.csv",
            sep=",",
            comment="#",
            encoding="cp1252",
        )

        # TODO: soortenlijst N-gevoelige soorten (179 stuks) toevoegen aan mnp_species.csv en labellen als dusdanig

        # Dictionaries tussen code, lokale naam, wetenschappelijke naam
        self.code2local = dict(zip(self.df.Species_code, self.df.Local_name))
        self.local2code = dict(zip(self.df.Local_name, self.df.Species_code))

        self.code2scientific = dict(zip(self.df.Species_code, self.df.Scientific_name))
        self.scientific2code = dict(zip(self.df.Scientific_name, self.df.Species_code))

        self.local2scientific = dict(zip(self.df.Local_name, self.df.Scientific_name))
        self.scientific2local = dict(zip(self.df.Scientific_name, self.df.Local_name))

        self.code2taxon = dict(zip(self.df.Species_code, self.df.taxon))

        # Dictionaries to self, is usefull sometimes. Trust me...
        self.local2local = dict(zip(self.df.Local_name, self.df.Local_name))
        self.code2code = dict(zip(self.df.Species_code, self.df.Species_code))
        self.scientific2scientific = dict(
            zip(self.df.Scientific_name, self.df.Scientific_name)
        )

        # # Dict between species code and boolean species lists
        self.code2selall = self.df.set_index('Species_code').assign(selall=True).selall.to_dict()

        self.code2sel468 = pd.Series(
            np.where(
                self.df.has_landscape_density
                & self.df.has_gvg_response
                & self.df.has_ndep_response
                & self.df.has_ph_response
                & self.df.has_species_traits,
                True,
                False,
            ),
            index=self.df.Species_code,
        ).to_dict()
        self.code2sel281 = pd.Series(
            np.where(
                self.df.has_landscape_density
                & self.df.has_gvg_response
                & self.df.has_ndep_response
                & self.df.has_ph_response
                & self.df.has_species_traits
                & ~self.df.enp2016_validatie.isin(
                    [
                        "slecht",
                        "slecht model",
                        "Slecht model",
                        "zeer matig model",
                        "Zeer matig model",
                        "nvt",
                    ]
                ),
                True,
                False,
            ),
            index=self.df.Species_code,
        ).to_dict()
        self.code2sel146 = pd.Series(
            np.where(
                self.df.has_landscape_density
                & self.df.has_gvg_response
                & self.df.has_ndep_response
                & self.df.has_ph_response
                & self.df.has_species_traits
                & ~self.df.enp2016_validatie.isin(
                    [
                        "slecht",
                        "slecht model",
                        "Slecht model",
                        "zeer matig model",
                        "Zeer matig model",
                        "nvt",
                    ]
                )
                & self.df.enp2016_typisch,
                True,
                False,
            ),
            index=self.df.Species_code,
        ).to_dict()
        self.code2selNSensitive = pd.Series(
            np.where(self.df.mnp2021_n_sensitive, True, False),
            index=self.df.Species_code,
        ).to_dict()

        # Additional data sources. Note that population factors are not defined for all species
        # TODO: merge deze tabel met self.df ?
        # self.pop_factors_src = r'W:\PROJECTS\QMAR\MNP-SNL-ParameterSet\Parameters_v05_2021_04_08\07_MNP_versie4_par_population_factors.csv'
        # self.pop_factors = pd.read_csv(self.pop_factors_src, sep=',')
        # self.code2local_distance = dict(zip(self.pop_factors.Species_code, self.pop_factors.Local_distance))
        # self.code2key_area = dict(zip(self.pop_factors.Species_code, self.pop_factors.Key_area))


class IndividualSpecies:
    """
    Class for holding properties of a single species instance.
    Hans Roelofsen, WEnR, 12 jan 2022
    """

    def __init__(self, x, brief=False, verbose=False):
        """
        Initiate for a species.
        :param x: species code, local name or scientific name
        """

        species_tab = TabularSpecies()

        species_identifiers_full = ["Species_code", "Local_name", "Scientific_name"]
        species_identifiers_brief = ["code", "local", "scientific"]

        try:
            # Determine if X is a species code, local name, scientific name or not-existing
            isin = [
                x in getattr(species_tab.df, col).values
                for col in species_identifiers_full
            ].index(True)
            typer = species_identifiers_brief[isin]
        except ValueError:
            if verbose:
                print("{} is not a recognized species".format(x))
            raise

        self.code = getattr(species_tab, "{}2code".format(typer))[x]  # Species code
        self.local = getattr(species_tab, "{}2local".format(typer))[
            x
        ]  # Species local name
        self.scientific = getattr(species_tab, "{}2scientific".format(typer))[
            x
        ]  # Species Scientific name
        self.taxondict = {"S02": "V", "S06": "E", "S09": "P"}
        self.taxon = self.taxondict[self.code[:3]]
        # try:
            # self.key_area = species_tab.code2key_area[self.code]
            # self.local_distance = species_tab.code2local_distance[self.code]
        # except KeyError:
        #     self.key_area = None
        #     self.local_distance = None

        # Paths to default files with species response towards abiotic conditions
        self.gvg_response_src = r"w:\PROJECTS\QMAR\MNP-SNL-ParameterSet\Parameters_v05_2021_04_08\04_MNP_versie4_par_response_GVG.csv"
        self.ndep_response_src = r"w:\PROJECTS\QMAR\MNP-SNL-ParameterSet\Parameters_v05_2021_04_08\05_MNP_versie4_par_response_Ndep.csv"
        self.ph_response_src = r"w:\PROJECTS\QMAR\MNP-SNL-ParameterSet\Parameters_v05_2021_04_08\06_MNP_versie4_par_response_PH.csv"

        if not brief:
            # Set all relevant attributes
            self.sel281 = species_tab.code2sel281[
                self.code
            ]  # boolean, species is part of 281 selection?
            self.sel468 = species_tab.code2sel468[self.code]  # idem for 468 selection
            self.sel146 = species_tab.code2sel146[self.code]  # idem foo 146 selection
            self.selNSensitive = species_tab.code2selNSensitive[
                self.code
            ]  # idem for N-Sensitive selection
            self.selall = species_tab.code2selall[self.code]  # catch all
            self.groupinfo = "146:{0} - 281:{1} - 468:{2} - NSensitive: {3}".format(
                self.sel146, self.sel281, self.sel468, self.selNSensitive
            )

    def abiotic_limits(self, abiotic, of="txt"):
        """
        return response limits for an abiotic factor
        :param abiotic: {gvg, ndep, ph}
        :return: dict
        """

        tab = pd.read_csv(
            getattr(self, "{}_response_src".format(abiotic)), index_col="Species_code"
        )
        try:
            response = tab.loc[self.code]

            return {
                "dict": dict(
                    zip([i[1] for i in response.index.str.split("_")], response)
                ),
                "array": response.values,
                "lst": response.to_list(),
                "sep": "\t".join([str(i) for i in response.to_list()]),
            }[of]
        except KeyError:
            print("{0} limits for {1} not found".format(abiotic, self.local))
            return None

    def response_to_abiotic(self, abiotic, x):
        """
        return response to a given Abiotic value
        :param abiotic which abiotic?
        :param x: the abiotic value
        :return: response {0, 0.5, 1}
                    _____
          1|       |     |
           |       |     |
        0.5|  _____|     |_____
           |  |                |
           |  |                |
          0 __|________________|__
             L20   L80   H80   H20
        """

        limits = self.abiotic_limits(abiotic=abiotic, of="array")

        # limits = np.array(getattr(self, '{}_response_src'.format(abiotic)).loc[self.code])
        return [0, 0.5, 1, 0.5, 0][int(np.digitize(x, limits))]

    def response_to_beheertype(self, beheertype):
        """
        return Landtype Quality (aka draagkracht of self towards a beheertype
        :param beheertype:
        :return: land type quality [0-1]
        """
        # TODO (20220323) Vergelijk met ./dkq.Draagkracht.query4species()


if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser()
    parser.add_argument("species", help="Species local name, scientific name or code.")
    parser.add_argument("what", help="any property of the species")
    args = parser.parse_args()

    sp = IndividualSpecies(args.species)
    try:
        print(getattr(sp, args.what))
    except AttributeError:
        print("{0} does not have a {1}".format(sp.local, args.what))
        sys.exit(1)