# noinspection PyPackageRequirements
import wx
from gui.bitmap_loader import BitmapLoader


class BaseValidator(wx.Validator):
    def __init__(self):
        wx.Validator.__init__(self)

    def Validate(self, win):
        raise NotImplementedError()

    def TransferToWindow(self):
        return True

    def TransferFromWindow(self):
        return True


class TextEntryValidatedDialog(wx.TextEntryDialog):
    def __init__(self, parent, validator=None, *args, **kargs):
        wx.TextEntryDialog.__init__(self, parent, *args, **kargs)
        self.parent = parent

        # See https://github.com/wxWidgets/Phoenix/issues/611
        self.txtctrl = self.FindWindowById(3000, self)

        if validator:
            self.txtctrl.SetValidator(validator())


class EntityEditor(wx.Panel):
    """
    Entity Editor is a panel that takes some sort of list as a source and populates a drop down with options to add/
    rename/clone/delete an entity. Comes with dialogs that take user input. Classes that derive this class must override
    functions that get the list from the source, what to do when user does an action, and how to validate the input.
    """

    def __init__(self, parent, entityName, selected=None):
        wx.Panel.__init__(self, parent, id=wx.ID_ANY, style=wx.TAB_TRAVERSAL)
        self.entityName = entityName
        self.validator = None
        self.navSizer = wx.BoxSizer(wx.HORIZONTAL)

        self.choices = []
        self.choices.sort(key=lambda p: p.name)
        self.entityChoices = wx.Choice(self, choices=[p.name for p in self.choices])
        self.navSizer.Add(self.entityChoices, 1, wx.ALL, 5)

        buttons = (("new", wx.ART_NEW, self.OnNew),
                   ("rename", BitmapLoader.getBitmap("rename", "gui"), self.OnRename),
                   ("copy", wx.ART_COPY, self.OnCopy),
                   ("delete", wx.ART_DELETE, self.OnDelete))

        size = None
        for name, art, func in buttons:
            bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) if name != "rename" else art
            btn = wx.BitmapButton(self, wx.ID_ANY, bitmap)
            if size is None:
                size = btn.GetSize()

            btn.SetMinSize(size)
            btn.SetMaxSize(size)

            btn.SetToolTip("{} {}".format(name.capitalize(), self.entityName))
            btn.Bind(wx.EVT_BUTTON, func)
            setattr(self, "btn%s" % name.capitalize(), btn)
            self.navSizer.Add(btn, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 2)

        self.SetSizer(self.navSizer)
        self.Layout()

        self.refreshEntityList(selected=selected)

    def SetEditorValidator(self, validator=None):
        """ Sets validator class (not an instance of the class) """
        self.validator = validator

    def getEntitiesFromContext(self):
        """ Gets list of entities from current context """
        raise NotImplementedError()

    def DoNew(self, name):
        """Override method to do new entity logic. Must return the new entity"""
        raise NotImplementedError()

    def DoCopy(self, entity, name):
        """Override method to copy entity. Must return the copy"""
        raise NotImplementedError()

    def DoRename(self, entity, name):
        """Override method to rename an entity"""
        raise NotImplementedError()

    def DoDelete(self, entity):
        """Override method to delete entity"""
        raise NotImplementedError()

    def OnNew(self, event):
        with TextEntryValidatedDialog(
            self, self.validator, "Enter a name for your new {}:".format(self.entityName),
            "New {}".format(self.entityName)
        ) as dlg:
            dlg.CenterOnParent()

            if dlg.ShowModal() == wx.ID_OK:
                # using dlg.textctrl.GetValue instead of simply dlg.GetValue because the proper way does not work in wxPython 2.8
                new = self.DoNew(dlg.txtctrl.GetValue().strip())
                self.refreshEntityList(new)
                wx.PostEvent(self.entityChoices, wx.CommandEvent(wx.wxEVT_COMMAND_CHOICE_SELECTED))
            else:
                return False

    def OnCopy(self, event):
        with TextEntryValidatedDialog(
            self, self.validator, "Enter a name for your {} copy:".format(self.entityName),
            "Copy {}".format(self.entityName)
        ) as dlg:
            active = self.getActiveEntity()
            dlg.SetValue("{} Copy".format(active.name))
            dlg.txtctrl.SetInsertionPointEnd()
            dlg.CenterOnParent()

            if dlg.ShowModal() == wx.ID_OK:
                copy = self.DoCopy(active, dlg.txtctrl.GetValue().strip())
                self.refreshEntityList(copy)
                wx.PostEvent(self.entityChoices, wx.CommandEvent(wx.wxEVT_COMMAND_CHOICE_SELECTED))

    def OnRename(self, event):
        with TextEntryValidatedDialog(
            self, self.validator, "Enter a new name for your {}:".format(self.entityName),
            "Rename {}".format(self.entityName)
        ) as dlg:
            active = self.getActiveEntity()
            dlg.SetValue(active.name)
            dlg.txtctrl.SetInsertionPointEnd()
            dlg.CenterOnParent()

            if dlg.ShowModal() == wx.ID_OK:
                self.DoRename(active, dlg.txtctrl.GetValue().strip())
                self.refreshEntityList(active)
                wx.PostEvent(self.entityChoices, wx.CommandEvent(wx.wxEVT_COMMAND_CHOICE_SELECTED))

    def OnDelete(self, event):
        with wx.MessageDialog(
            self, "Do you really want to delete the {} {}?".format(self.getActiveEntity().name, self.entityName),
            "Confirm Delete", wx.YES | wx.NO | wx.ICON_QUESTION
        ) as dlg:
            dlg.CenterOnParent()
            if dlg.ShowModal() == wx.ID_YES:
                self.DoDelete(self.getActiveEntity())
                self.refreshEntityList()
                wx.PostEvent(self.entityChoices, wx.CommandEvent(wx.wxEVT_COMMAND_CHOICE_SELECTED))

    def refreshEntityList(self, selected=None):
        self.choices = self.getEntitiesFromContext()
        self.entityChoices.Clear()

        self.entityChoices.AppendItems([p.name for p in self.choices])
        if selected:
            idx = self.choices.index(selected)
            self.entityChoices.SetSelection(idx)
        else:
            self.entityChoices.SetSelection(0)

    def getActiveEntity(self):
        if len(self.choices) == 0:
            return None

        return self.choices[self.entityChoices.GetSelection()]

    def setActiveEntity(self, entity):
        try:
            idx = self.choices.index(entity)
        except IndexError:
            return False
        self.entityChoices.SetSelection(idx)
        return True

    def checkEntitiesExist(self):
        if len(self.choices) > 0:
            return True
        else:
            return self.enterNewEntity()

    def enterNewEntity(self):
        self.Parent.Hide()
        if self.OnNew(None) is False:
            return False
        self.Parent.Show()

        return True


