# -*- coding: UTF-8 -*-
'''
================================================================================
Project: SiriL-ic ( SiriL Image Companion )

This script structures the SiriL work folder into a subfolder, copies the
astronomical images into the subfolders, and builds the associated SiriL
script. It can also group scripts.
================================================================================
   Author:  M27trognondepomme <pebe92 (at) gmail.com>

This program is provided without any guarantee.

The license is  LGPL-v3
For details, see GNU General Public License, version 3 or later.
                       "https://www.gnu.org/licenses/gpl.html"
================================================================================
'''
import sys
import os.path
import wx
import wx.stc as stc   # StyledTextCtrl

from sirilic.lib.tools import init_language

init_language()


# ------------------------------------------------------------------------------
# This is how you pre-establish a file filter so that the
# dialog only shows the extension(s) you want it to.
wildcard = "Siril Script File (*.ssf)|*.ssf|" \
           "Text (*.txt)|*.txt|"              \
           "Config (*.cfg)|*.cfg|"            \
           "Project (*.prj)|*.prj|"           \
           "rc (*.rc)|*.rc|"                  \
           "log (*.log)|*.log|"               \
           "All (*.*)|*.*"


# ------------------------------------------------------------------------------
class MyScriptEditorFrame(wx.Frame):
    def __init__(self, filename="noname.txt"):
        super(MyScriptEditorFrame, self).__init__(None, size=(800, 600))

        self.findtext = ""
        self.gotoline = 0

        # ------------
        # Return icons folder.
        self.icons_dir = os.path.join(os.path.split(os.path.abspath(__file__))[0], "..", "icon")

        # ------------
        self.filename = os.path.basename(filename)
        dirname = os.path.dirname(os.path.abspath(filename))
        self.dirname = dirname
        self.flagDirty = False

        # ------------
        # Simplified init method.
        self.SetProperties()
        self.CreateMenuBar()
        self.CreateStyledTextControl()
        self.CreateStatusBar()
        self.BindEvents()

        # ------------
        # Load the file if exist
        if not os.path.exists(filename):
            return
        file = open(os.path.join(self.dirname, self.filename), 'r', encoding='utf-8')
        self.edit.SetValue(file.read())
        file.close()
        self.flagDirty = False
        self.SetTitle()

    # --------------------------------------------------------------------------
    def SetProperties(self):
        self.SetTitle()
        frameIcon = wx.Icon(os.path.join(self.icons_dir, "cp-nb.ico"),
                            type=wx.BITMAP_TYPE_ICO)
        self.SetIcon(frameIcon)

    def SetTitle(self):
        """ overrides wx.Frame.SetTitle, so we have to call it using super
        """
        flag = ""
        if self.flagDirty:
            flag = "*" + _("modified") + "*"
        super(MyScriptEditorFrame, self).SetTitle(_("Editor") + " %s %s" %
                                                  (self.filename, flag))

    def CreateMenuBar(self):
        """ Create menu and menu bar. """
        # menubar
        mb = wx.MenuBar()

        # ------------
        mf = wx.Menu()  # menu file
        me = wx.Menu()  # menu edit
        ma = wx.Menu()  # menu about

        # ------------
        self.mfOpen = mf.Append(wx.ID_OPEN,
                                _("&Open") + "\t" + "Ctrl+O",
                                _("Open a new file."))
        # ------------
        mf.AppendSeparator()
        # ------------
        self.mfSave = mf.Append(wx.ID_SAVE,
                                _("&Save") + "\t" + "Ctrl+S",
                                _("Save the current file."))
        self.mfSaveAs = mf.Append(wx.ID_SAVEAS,
                                  _("&Save as") + "\t" + "Ctrl+Shift+S",
                                  _("Save the file under a different name."))
        # ------------
        mf.AppendSeparator()
        # ------------
        self.mfClose = mf.Append(wx.ID_EXIT,
                                 _("&Quit") + "\t" + "Ctrl+Q", _("Exit the editor."))
        # ------------
        self.meCut = me.Append(wx.ID_CUT,
                               _("&Cut") + "\t" + "Ctrl+X", _("Cut"))
        self.meCopy = me.Append(wx.ID_COPY,
                                _("&Copy") + "\t" + "Ctrl+C", _("Copy"))
        self.mePaste = me.Append(wx.ID_PASTE,
                                 _("&Paste") + "\t" + "Ctrl+V", _("Paste"))
        # ------------
        me.AppendSeparator()

        # ------------
        self.meUndo = me.Append(wx.ID_UNDO,
                                _("&Undo") + "\t" + "Ctrl+Z", _("Undo"))
        self.meRedo = me.Append(wx.ID_REDO,
                                _("&Redo") + "\t" + "Ctrl+Shift+Z", _("Redo"))

        # ------------
        me.AppendSeparator()
        self.meFind = me.Append(wx.ID_FIND,
                                _("&Find") + "\t" + "Ctrl+F", _("Find text"))

        self.meNextFind = me.Append(wx.ID_ANY,
                                    _("find &Next") + "\t" + "F3",
                                    _("Next ocurrence"))

        self.meGoto = me.Append(wx.ID_INDEX,
                                _("&Goto") + "\t" + "Ctrl+G", _("Goto line"))

        # ------------
        self.maInfo = ma.Append(wx.ID_ABOUT,
                                _("&About") + "\t" + "Ctrl+I",
                                _("Information about this program."))

        # ------------
        mb.Append(mf, _("File"))
        mb.Append(me, _("Edit"))
        mb.Append(ma, _("About"))

        # ------------
        self.SetMenuBar(mb)

    def CreateStyledTextControl(self):
        self.edit = stc.StyledTextCtrl(self)
        ed = self.edit
        ed.StyleSetSpec(stc.STC_STYLE_DEFAULT, "size:10,face:Courier New,fore:black")
        ed.StyleClearAll()

        ed.SetLexer(stc.STC_LEX_CONTAINER)
        # ed.SetWrapMode(stc.STC_WRAP_WORD)
        ed.SetIndent(4)

        ed.StyleSetSpec(2, 'fore:red, back:#FFFFFF,face:Courier New,size:10')
        ed.StyleSetSpec(3, 'fore:blue, back:#FFFFFF,face:Courier New,size:10')
        ed.StyleSetSpec(4, 'fore:magenta, back:#FFFFFF,face:Courier New,size:10')
        ed.StyleSetSpec(5, 'fore:salmon, back:#FFFFFF,face:Courier New,size:10')

        motcles = "requires set32bits setext setcompress setmem setcpu setfindstar "
        motcles += "cd convert stack calibrate mirrorx_single pm load save register "
        motcles += "close savetif seqsubsky merge seqextract_Ha seqextract_Green "
        motcles += "seqextract_HaOIII seqapplyreg rgbcomp set16bits"
        self.motcles = motcles.split()

        layout = wx.BoxSizer(wx.HORIZONTAL)
        layout.Add(ed, proportion=1, border=0, flag=wx.ALL | wx.EXPAND)
        self.SetSizer(layout)

        ed.Bind(stc.EVT_STC_CHANGE, self.OnChangeTxtCtrl)

        try:
            sys_appearance = wx.SystemSettings.GetAppearance()
            dark = sys_appearance.IsDark()
            if dark:
                ed.StyleSetSpec(stc.STC_STYLE_LINENUMBER,
                                'fore:#FFFF00')
        except Exception:   # pylint: disable=broad-except
            ed.StyleSetSpec(stc.STC_STYLE_LINENUMBER,
                            "size:10,face:Courier New,fore:black")

    def BindEvents(self):
        self.Bind(wx.EVT_MENU, self.OnOpen, self.mfOpen)
        self.Bind(wx.EVT_MENU, self.OnSave, self.mfSave)
        self.Bind(wx.EVT_MENU, self.OnSaveAs, self.mfSaveAs)
        self.Bind(wx.EVT_MENU, self.OnCloseMe, self.mfClose)
        self.Bind(wx.EVT_MENU, self.OnCut, self.meCut)
        self.Bind(wx.EVT_MENU, self.OnCopy, self.meCopy)
        self.Bind(wx.EVT_MENU, self.OnPaste, self.mePaste)
        self.Bind(wx.EVT_MENU, self.OnUndo, self.meUndo)
        self.Bind(wx.EVT_MENU, self.OnRedo, self.meRedo)
        self.Bind(wx.EVT_MENU, self.OnFind, self.meFind)
        self.Bind(wx.EVT_MENU, self.OnNextFind, self.meNextFind)
        self.Bind(wx.EVT_MENU, self.OnGoto, self.meGoto)
        self.Bind(wx.EVT_MENU, self.OnAbout, self.maInfo)

        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)

    def OnChangeTxtCtrl(self, _event):
        lines = self.edit.GetLineCount()
        width = self.edit.TextWidth(stc.STC_STYLE_LINENUMBER, str(lines) + " ")
        self.edit.SetMarginWidth(0, width)
        self.flagDirty = True
        self.SetTitle()
        self.OnColor()

    def GetRawText(self):
        # recuperer un tableau brute car la position gere mal l'utf-8
        fulltext = list(self.edit.GetTextRaw())
        if len(fulltext) == 0:
            return None
        for ii in range(0, len(fulltext)):
            if fulltext[ii] > 127:
                fulltext[ii] = 32
        fulltext = ''.join([chr(elt) for elt in fulltext])
        if len(fulltext) == 0:
            return None
        return fulltext

    def OnColor(self):
        fulltext = self.GetRawText()
        if fulltext is None:
            return
        self.edit.ClearDocumentStyle()

        for item in self.motcles:
            item += " "
            pos = fulltext.find(item)
            while pos in range(0, len(fulltext)):
                self.edit.StartStyling(pos)
                self.edit.SetStyling(len(item), 2)
                pos = fulltext.find(item, pos + len(item))

        self.highligth_eol(fulltext, '"', 5)
        self.highligth_eol(fulltext, "#", 3)
        self.highligth_eol(fulltext, "#TAG#", 4)
        self.highligth_eol(fulltext, "##", 3)

    def highligth_eol(self, fulltext, tag, style, endchar1='\r', endchar2='\n'):
        pos = fulltext.find(tag)
        while pos in range(0, len(fulltext)):
            self.edit.StartStyling(pos)
            pos2 = pos
            while pos2 < len(fulltext) and fulltext[pos2] != endchar1 and fulltext[pos2] != endchar2:
                pos2 += 1
            self.edit.SetStyling(pos2 - pos + 1, style)
            pos = fulltext.find(tag, pos2 + 1)

    def DefaultFileDialogOptions(self):
        """
        Return a dictionary with file dialog options that can be
        used in both the save file dialog as well as in the open
        file dialog.
        """
        return dict(message="Choose a file :", defaultDir=self.dirname, wildcard=wildcard)

    def AskUserForFilename(self, **dialogOptions):
        dialog = wx.FileDialog(self, **dialogOptions)

        if dialog.ShowModal() == wx.ID_OK:
            userProvidedFilename = True
            self.filename = dialog.GetFilename()
            self.dirname = dialog.GetDirectory()
            # Update the window title with the new filename.
            self.SetTitle()
        else:
            userProvidedFilename = False

        dialog.Destroy()

        return userProvidedFilename

    def OnOpen(self, _event):
        if self.AskUserForFilename(style=wx.FD_OPEN,
                                   **self.DefaultFileDialogOptions()):
            file = open(os.path.join(self.dirname, self.filename), 'r', encoding='utf-8')
            self.edit.SetValue(file.read())
            file.close()
            self.flagDirty = False
            self.SetTitle()

    def OnSave(self, _event):
        with open(os.path.join(self.dirname, self.filename), 'w', encoding='utf-8') as file:
            file.write(self.edit.GetValue())
            self.flagDirty = False
            self.SetTitle()

    def OnSaveAs(self, event):
        if self.AskUserForFilename(defaultFile=self.filename, style=wx.FD_SAVE,
                                   **self.DefaultFileDialogOptions()):
            self.OnSave(event)

    def OnCut(self, _event):
        self.edit.Cut()

    def OnCopy(self, _event):
        self.edit.Copy()

    def OnPaste(self, _event):
        self.edit.Paste()

    def OnUndo(self, _event):
        self.edit.Undo()

    def OnRedo(self, _event):
        self.edit.Redo()

    def OnFind(self, _event):
        with wx.TextEntryDialog(self, '', _("Find text"), self.findtext, wx.OK | wx.CANCEL) as dlg:
            if dlg.ShowModal() == wx.ID_OK:
                self.findtext = dlg.GetValue()
                self.OnNextFind(_event)
            dlg.Destroy()

    def OnNextFind(self, _event):
        if len(self.findtext) == 0:
            return
        cur_pos = self.edit.GetCurrentPos()
        fulltext = self.GetRawText()
        if fulltext is None:
            return
        pos = fulltext.find(self.findtext, cur_pos)
        if pos < 0:
            pos = fulltext.find(self.findtext, 0)
        if pos >= 0:
            self.edit.GotoLine(self.edit.LineFromPosition(pos))
            self.edit.SetSelectionStart(pos)
            self.edit.SetSelectionEnd(pos + len(self.findtext))

    def OnGoto(self, _event):
        lastline = max(1, self.edit.GetLineCount())
        with wx.NumberEntryDialog(self, '', _('line') + ':',
                                  _('Goto'), self.gotoline,
                                  1, lastline) as dlg:
            if dlg.ShowModal() == wx.ID_OK:
                self.gotoline = max(0, dlg.GetValue() - 1)
                self.edit.GotoLine(self.gotoline)
            dlg.Destroy()

    def OnAbout(self, _event):
        dialog = wx.MessageDialog(self,
                                  _("A simple Script editor in Sirilic."),
                                  _("About  Script Editor"),
                                  wx.OK)
        dialog.ShowModal()
        dialog.Destroy()

    def OnCloseMe(self, _event):
        self.Close(True)

    def OnCloseWindow(self, _event):
        self.Destroy()


# ------------------------------------------------------------------------------
class MyScriptEditorApp(wx.App):
    def __init__(self, filename="noname.txt"):
        self.filename = filename
        wx.App.__init__(self)

    def OnInit(self):
        frame = MyScriptEditorFrame(self.filename)
        self.SetTopWindow(frame)
        frame.Show(True)
        return True


# ------------------------------------------------------------------------------
def main(filename):
    app = MyScriptEditorApp(filename)
    app.MainLoop()


# ------------------------------------------------------------------------------
if __name__ == "__main__":
    if sys.platform.startswith('win32'):
        main("D:/_TraitAstro/20-SiriL/work/script/sirilic.ssf")
    else:
        main("/home/barch/siril/work/script/sirilic.ssf")
