import csv
import os
from itertools import zip_longest

import pandas as pd

from graphysio import writedata
from graphysio.dialogs import DlgPeriodExport, askDirPath, askSaveFilePath
from graphysio.utils import sanitize_filename

file_filters = ";;".join(
    [f"{ext.upper()} files (*.{ext})" for ext in writedata.curve_writers],
)


class TsExporter:
    periodfields = ["patient", "begin", "end", "periodid", "comment"]

    def __init__(self, parent, name) -> None:
        self.parent = parent
        self.name = name
        try:
            self.outdir = parent.properties["dircache"]
        except KeyError:
            self.outdir = os.path.expanduser("~")

    def curves(self) -> None:
        filepath, ext = askSaveFilePath(
            "Export to",
            f"{self.name}.csv",
            self.outdir,
            filter=file_filters,
        )
        if filepath is None:
            return
        self.outdir = os.path.dirname(filepath)
        curves = list(self.parent.curves.values())
        export_func = writedata.curve_writers[ext]
        export_func(curves, filepath)

    def periods(self) -> None:
        xmin, xmax = self.parent.vbrange
        dlg = DlgPeriodExport(
            begin=xmin,
            end=xmax,
            patient=self.name,
            directory=self.outdir,
        )

        def cb(result) -> None:
            patient, comment, periodname, filepath = result
            self.name = patient
            self.outdir = os.path.dirname(filepath)

            with open(filepath, "a", newline="") as csvfile:
                writer = csv.DictWriter(
                    csvfile,
                    fieldnames=self.periodfields,
                    quoting=csv.QUOTE_MINIMAL,
                )
                if not os.path.exists(filepath):
                    writer.writeheader()
                writer.writerow(
                    {
                        "patient": patient,
                        "begin": xmin,
                        "end": xmax,
                        "periodid": periodname,
                        "comment": comment,
                    },
                )

        dlg.dlgdata.connect(cb)
        dlg.exec_()

    def cycles(self) -> None:
        outdir = askDirPath("Export to", self.outdir)
        if outdir is None:
            # Cancel pressed
            return

        # Some non trivial manipulations to get the cycles from all
        # curves, then reorganize to group by the n-th cycle from each
        # curve and put those cycles into a dataframe for export.
        def getCurveCycles(curve):
            cycleIdx = curve.getCycleIndices()
            return (curve.series.loc[b : b + d] for b, d in zip(*cycleIdx))

        curves = self.parent.curves.values()
        allByCurve = (getCurveCycles(curve) for curve in curves)
        allByCycle = zip_longest(*allByCurve)

        for n, cycle in enumerate(allByCycle):
            idxstart = None
            for s in cycle:
                # Make all series start at the same point
                if s is None or len(s) < 1:
                    continue
                if idxstart is None:
                    idxstart = s.index[0]
                idxdelta = s.index[0] - idxstart
                s.index -= idxdelta
            df = pd.concat(cycle, axis=1)
            df["datetime"] = pd.to_datetime(df.index, unit="ns")

            filename = sanitize_filename(f"{self.name}-{n+1}.csv")
            filepath = os.path.join(self.outdir, filename)
            df.to_csv(
                filepath,
                date_format="%Y-%m-%d %H:%M:%S.%f",
                index_label="timens",
            )

    def cyclepoints(self) -> None:
        filepath, _ = askSaveFilePath("Export to", f"{self.name}-feet.csv", self.outdir)
        if filepath is None:
            # Cancel pressed
            return
        self.outdir = os.path.dirname(filepath)

        feetseries = []
        for c in self.parent.curves.values():
            for k, v in c.feetitem.indices.items():
                feetseries.append(pd.Series(v, name=f"{c.name()}-{k}"))
        df = pd.concat(feetseries, axis=1).astype("Int64")
        df.to_csv(filepath, index_label="idx")


class PuExporter:
    def __init__(self, parent, name) -> None:
        self.parent = parent
        self.name = name
        try:
            self.outdir = parent.properties["dircache"]
        except KeyError:
            self.outdir = os.path.expanduser("~")

    def exportloops(self) -> None:
        outdir = askDirPath("Export to", self.outdir)
        if outdir is None:
            # Cancel pressed
            return
        self.outdir = outdir
        self.writetable()
        self.writeloops()

    def writetable(self) -> None:
        data = []
        for loop in self.parent.loops:
            alpha, beta, gala = loop.angles
            delay = loop.offset / 1e6
            tmpdict = {"alpha": alpha, "beta": beta, "gala": gala, "delay": delay}
            data.append(tmpdict)
        df = pd.DataFrame(data)
        df.index += 1
        filename = sanitize_filename(f"{self.name}-loopdata.csv")
        filepath = os.path.join(self.outdir, filename)
        df.to_csv(filepath, index_label="idx")

    def writeloops(self) -> None:
        for n, loop in enumerate(self.parent.loops):
            df = loop.df
            filename = sanitize_filename(f"{self.name}-{n+1}.csv")
            filepath = os.path.join(self.outdir, filename)
            df["datetime"] = pd.to_datetime(df.index, unit="ns")
            df.to_csv(
                filepath,
                date_format="%Y-%m-%d %H:%M:%S.%f",
                index_label="timens",
            )


class POIExporter:
    def __init__(self, parent, name) -> None:
        self.parent = parent
        self.name = name
        try:
            self.outdir = parent.properties["dircache"]
        except KeyError:
            self.outdir = os.path.expanduser("~")

    def poi(self) -> None:
        filepath, _ = askSaveFilePath("Export to", f"{self.name}-poi.csv", self.outdir)
        if filepath is None:
            # Cancel pressed
            return
        self.outdir = os.path.dirname(filepath)

        srcseries = self.parent.curve.series
        poiidx = self.parent.curve.feetitem.indices[self.parent.pointkey]
        pois = srcseries[poiidx.dropna()].rename("poi")
        sers = [srcseries, pois]
        df = pd.concat(sers, axis=1, keys=[s.name for s in sers])
        df["datetime"] = pd.to_datetime(df.index, unit="ns")
        df.to_csv(filepath, date_format="%Y-%m-%d %H:%M:%S.%f", index_label="timens")
