# ===============================================================================
# Copyright 2014 Jake Ross
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ===============================================================================
from __future__ import absolute_import
from __future__ import print_function

import os

import yaml
from pyface.confirmation_dialog import ConfirmationDialog
from pyface.constant import OK, CANCEL, YES
from pyface.file_dialog import FileDialog
from traits.api import (

    HasTraits,
    Str,
    List,
    Int,
    Any,
    Button,
    Bool,
    on_trait_change,
    Instance,
)
from traitsui.
api import (
    View,
    Item,
    UItem,
    HGroup,
    InstanceEditor,
    HSplit,
    VGroup,
    EnumEditor,
)
from traitsui.handler import Controller
from traitsui.menu import Action
from traitsui.table_column import ObjectColumn

from pychron.core.helpers.filetools import fileiter, add_extension
from pychron.core.ui.table_editor import myTableEditor
from pychron.core.ui.text_editor import myTextEditor
from pychron.core.yaml import yload
from pychron.envisage.icon_button_editor import icon_button_editor
from pychron.envisage.resources import icon
from pychron.experiment.automated_run.hop_util import split_hopstr
from pychron.loggable import Loggable
from pychron.paths import paths


# class NullInt(Int):
#     default_value = None


class Position(HasTraits):
    detector = Str
    isotope = Str
    deflection = Int
    name = Str

    # available_isotopes=Property(depends_on='isotope')
    #
    # def _get_available_isotopes(self):
    #     from pychron.pychron_constants import ISOTOPES
    #     isos=list(ISOTOPES)
    #     isos.remove(self.isotope)
    #     return isos
    def to_yaml(self):
        return {
            "detector": self.detector,
            "isotope": self.isotope,
            "active": True,
            "deflection": self.deflection,
            "protect": False,
            "is_baseline": False,
        }

    def to_string(self):
        s = "{}:{}".format(self.isotope, self.detector)
        if self.deflection:
            s = "{}:{}".format(s, self.deflection)
        return s


class Hop(HasTraits):
    positions = List
    counts = Int
    settle = Int
    isotope_label = Str
    name = Str
    detectors = List(["A", "B"])
    add_position_button = Button
    remove_position_button = Button
    selected = Any
    error_message = Str

    def to_string(self):
        vs = [str(self.counts), str(self.settle)]
        hs = "'{}'".format(
            ", ".join(
                [p.to_string() for p in self.positions if p.isotope and p.detector]
            )
        )

        return "({}, {})".format(hs, ", ".join(vs))

    def to_yaml(self):
        obj = {"counts": self.counts, "settle": self.settle}
        poss = [p for p in self.positions if p.isotope and p.detector]
        if poss:
            obj["cup_configuration"] = [p.to_yaml() for p in poss]
            pp = poss[0]
            obj["positioning"] = {"detector": pp.detector, "isotope": pp.isotope}

        return obj

    def parse_hopstr(self, hs):
        for is_baseline, iso, det, defl in split_hopstr(hs):
            p = Position(isotope=iso, detector=det, deflection=int(defl) if defl else 0)
            self.positions.append(p)

        self._handle_position_change()

    def validate_hop(self):
        """
        return true if no duplicates
        """
        self.error_message = ""
        n = len(self.positions)
        ps = {p.isotope for p in self.positions}
        dup_iso = len(set(ps)) < n
        if dup_iso:
            self.error_message = self._make_error_message("isotope")

        ds = {p.detector for p in self.positions}
        dup_det = len(ds) < n
        if dup_det:
            em = self._make_error_message("detector")
            if self.error_message:
                self.error_message = "{}; {}".format(self.error_message, em)
            else:
                self.error_message = em

        return not (dup_iso or dup_det)

    def _make_error_message(self, attr):
        dets = []
        ps = []
        for p in self.positions:
            det = getattr(p, attr)
            if det in dets:
                ps.append(det)
            dets.append(det)
        return "Multiple {}s: {}".format(attr.capitalize(), ", ".join(ps))

    def _add_position_button_fired(self):
        self.positions.append(Position())

    def _remove_position_button_fired(self):
        idx = self.positions.index(self.selected)

        self.positions.remove(self.selected)

        if len(self.positions) > 0:
            self.selected = self.positions[idx - 1]
        else:
            self.selected = None

    @on_trait_change("positions:isotope, positions[]")
    def _handle_position_change(self):
        self.isotopes_label = ",".join([i.isotope for i in self.positions])

    def traits_view(self):
        from pychron.pychron_constants import ISOTOPES

        cols = [
            ObjectColumn(name="name", label="", width=20, editable=False),
            ObjectColumn(name="isotope", editor=EnumEditor(values=ISOTOPES)),
            ObjectColumn(name="detector", editor=EnumEditor(values=self.detectors)),
            ObjectColumn(
                name="deflection",
            ),
        ]

        v = View(
            VGroup(
                HGroup(
                    Item("counts", tooltip="Number of measurements at this position"),
                    Item(
                        "settle",
                        label="Settle (s)",
                        tooltip="Delay in seconds after magnet move and before measurement",
                    ),
                ),
                UItem(
                    "positions",
                    editor=myTableEditor(
                        columns=cols,
                        sortable=False,
                        clear_selection_on_dclicked=True,
                        selected="selected",
                    ),
                ),
                HGroup(
                    icon_button_editor(
                        "add_position_button",
                        "add",
                        tooltip="Add isotope/detector to measure",
                    ),
                    icon_button_editor(
                        "remove_position_button",
                        "delete",
                        tooltip="Remove selected isotope/detector",
                        enabled_when="selected",
                    ),
                ),
            )
        )
        return v


class HopSequence(HasTraits):
    hops = List

    def to_string(self):
        return "\n".join([hi.to_string() for hi in self.hops])

    def to_yaml(self):
        return [hi.to_yaml() for hi in self.hops]

    def add_hop(self, idx):
        if idx is not None:
            h = self.hops[idx]
            hh = h.clone_traits()
            self.hops.insert(idx, hh)
        else:
            h = Hop()
            self.hops.append(h)

        self._label_hops()

    def _label_hops(self):
        for i, hi in enumerate(self.hops):
            hi.name = str(i + 1)
            print(hi.name)
            for j, pi in enumerate(hi.positions):
                pi.name = str(j + 1)

    def remove_hop(self, idx):
        self.hops.pop(idx)
        self._label_hops()

    def label_hops(self):
        self._label_hops()


class HopEditorModel(Loggable):
    hop_sequence = Instance(HopSequence)
    selected = Any
    path = Str
    detectors = List
    add_hop_button = Button
    remove_hop_button = Button
    # saveable = Bool
    # saveasable = Bool
    text = Str
    dirty = Bool
    use_yaml = T