import CTMeth
from CTMeth.neural.training_settings import read_settings
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
import os
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
import numpy as np


def pca2d(params):
    def log_pca(txtpath, pca_explain):

        print(txtpath)
        with open(txtpath, "w") as f:
            f.write(f"\n{pca_explain}")

    project_name = params["project_name"]

    sep = ","
    src = params["pca2d_src"]
    src = "hh"
    if src == "hh":
        input_data_path = params["output_path_only_hyper_hypo"]
    elif src == "full":
        input_data_path = params["output_path_full"]
    elif src == "hhi":
        input_data_path = params["output_path_hyper_hypo_indeter"]
    elif src == "ii":
        input_data_path = params["output_path_indeter_indeter"]
    else:
        input_data_path = src
        sep = params["pca2d_src_sep"]

    df = pd.read_csv(input_data_path, index_col=0)
    group_divide = params["group_divide"]

    input_data = pd.read_csv(input_data_path, index_col=0, sep=sep)
    sortby = input_data.columns[5]

    input_data_sorted = input_data.sort_values(by=sortby, ascending=False)

    confidence_array = params["pca2d_confidence_thresholds"]
    if not isinstance(confidence_array, list):
        quantiles = input_data_sorted.iloc[:, 5].quantile([0, 0.50, 0.75, 0.95])
        confidence_array = quantiles.values

    for n in confidence_array:

        confidence_threshold = n
        input_data_sorted = input_data_sorted[
            input_data_sorted[sortby] >= confidence_threshold
        ]
        cpgs_count = len(input_data_sorted)

        df3 = input_data_sorted.iloc[:, 6:]
        labels = df3.columns.values
        df4 = df3.transpose()
        df4.insert(len(df4.columns), "target", 1)
        df4.target = df4.index
        df4.head()

        features = df4.columns[:-1]

        x = df4.loc[:, features].values

        y = df4.loc[:, ["target"]].values
        targets = df4.target

        try:
            x = StandardScaler().fit_transform(x)

            pca = PCA(n_components=2, svd_solver="full")
            principalComponents = pca.fit_transform(x)
            principalDf = pd.DataFrame(
                data=principalComponents,
                columns=["principal component 1", "principal component 2"],
            )

            df5 = df4["target"]
            df5 = df5.reset_index(drop=True)

            finalDf = pd.concat([principalDf, df5], axis=1)
            pca_explain = np.round(pca.explained_variance_ratio_, 2)

            line1 = f"PCA2D project: {project_name}, \n src: {src}, \nconfidence {round(n,2)}"
            line2 = f"PC1 explanation {pca_explain[0]} \n PC2 explanation {pca_explain[1]} \n PCs sum: {round(sum(pca_explain),2)} , \ncpgs_count {cpgs_count}"
            title = "".join([line1, "\n", line2])
            fig = plt.figure(figsize=(8, 8))
            ax = fig.add_subplot(1, 1, 1)
            ax.set_xlabel("PC 1")
            ax.set_ylabel("PC 2")
            ax.set_title(title)

            colors = []
            for n in range(group_divide):
                colors.append("red")
            for n in range(group_divide, len(labels)):
                colors.append("blue")

            x = finalDf.iloc[:, 0]
            y = finalDf.iloc[:, 1]
            sc = ax.scatter(x, y, c=colors, s=100)

            ax.axvline(0, color="red")
            ax.axhline(0, color="red")

            annot = ax.annotate(
                "",
                xy=(0, 0),
                xytext=(20, 20),
                textcoords="offset points",
                bbox=dict(boxstyle="round", fc="w"),
                arrowprops=dict(arrowstyle="->"),
            )
            annot.set_visible(False)

            def update_annot(ind):

                pos = sc.get_offsets()[ind["ind"][0]]
                annot.xy = pos
                z = ind["ind"]
                text = f"{labels[z][0]}"
                annot.set_text(text)
                annot.get_bbox_patch().set_facecolor(colors[ind["ind"][0]])
                annot.get_bbox_patch().set_alpha(0.4)

            def hover(event):
                vis = annot.get_visible()
                if event.inaxes == ax:
                    cont, ind = sc.contains(event)
                    if cont:
                        update_annot(ind)
                        annot.set_visible(True)
                        fig.canvas.draw_idle()
                    else:
                        if vis:
                            annot.set_visible(False)
                            fig.canvas.draw_idle()

            fig.canvas.mpl_connect("motion_notify_event", hover)
            #
            try:
                pca_output_folder = params["pca2d_output_folder"]
            except KeyError:
                print("pca output folder not specified")
                pca_output_folder = params["project_path"]
            if not isinstance(pca_output_folder, str):
                pca_output_folder = params["project_path"]
            else:
                if len(pca_output_folder) < 1:
                    pca_output_folder = params["project_path"]
            savefigpth = pca_output_folder
            basename = os.path.basename(input_data_path)[:-4]
            imagename = (
                f"pca2d_{basename}-conf{np.round(confidence_threshold,2)}-src-{src}.png"
            )

            savefigpth = os.path.join(savefigpth, imagename)

            txtname = (
                f"pca2d_{basename}-conf{np.round(confidence_threshold,2)}-src-{src}.txt"
            )

            savetxtpth = os.path.join(pca_output_folder, txtname)
            log_pca(savetxtpth, pca_explain)

            if params["pca2d_show"]:
                plt.show()
            if params["pca2d_save"]:
                print(f"saved to {savefigpth}")
                plt.savefig(savefigpth, bbox_inches="tight")
            plt.close()

            ###
        except:
            print("Error")
            import traceback

            print(traceback.format_exc())
