from os import F_OK, access, getcwd, sep
import platform
from re import search
import sys
from time import sleep
from warnings import warn
import webbrowser
import wx

# relax module imports.
from data_store import Relax_data_store; ds = Relax_data_store()
from data_store.gui import Gui
import dep_check
from graphics import IMAGE_PATH, fetch_icon
from gui.about import About_relax
from gui.analyses import Analysis_controller
from gui.spin_viewer.frame import Spin_view_window
from gui.controller import Controller
from gui.export_bmrb import Export_bmrb_window
from gui.filedialog import RelaxDirDialog, RelaxFileDialog
from gui.fonts import font
from gui.icons import Relax_icons
from gui.interpreter import Interpreter
from gui.menu import Menu
from gui.message import error_message, Question
from gui.misc import bitmap_setup, gui_raise, open_file, protected_exec
from gui.pipe_editor import Pipe_editor
from gui.references import References
from gui.relax_prompt import Prompt
from gui.results_viewer import Results_viewer
from gui.components.free_file_format import Free_file_format_window
from gui.string_conv import gui_to_str
from gui.uf_objects import Uf_storage; uf_store = Uf_storage()
from info import Info_box
from lib.errors import RelaxNoPipeError
from lib.warnings import RelaxWarning
from lib.io import io_streams_restore
from pipe_control import state
from pipe_control.pipes import cdp_name
from pipe_control.reset import reset
from pipe_control.system import pwd
from status import Status; status = Status()
from version import repo_head, version

# wx IDs for the toolbar.
TB_FILE_NEW = wx.NewId()
TB_FILE_CLOSE = wx.NewId()
TB_FILE_CLOSE_ALL = wx.NewId()
TB_FILE_CWD = wx.NewId()
TB_FILE_OPEN = wx.NewId()
TB_FILE_SAVE = wx.NewId()
TB_FILE_SAVE_AS = wx.NewId()
TB_VIEW_CONTROLLER = wx.NewId()
TB_VIEW_SPIN_VIEW = wx.NewId()
TB_VIEW_RESULTS = wx.NewId()
TB_VIEW_PIPE_EDIT = wx.NewId()
TB_VIEW_PROMPT = wx.NewId()

class Main(wx.Frame):
    """The main GUI class."""

    # Hard coded variables.
    min_width = 1000
    min_height = 600

    def __init__(self, parent=None, id=-1, title=""):
        """Initialise the main relax GUI frame."""

        # Store the wxPython info for os/machine/version specific hacks.
        status.wx_info = {}
        status.wx_info["version"] = wx.__version__.split('.')
        status.wx_info["minor"] = "%s.%s" % (status.wx_info["version"][0], status.wx_info["version"][1])
        status.wx_info["os"] = sys.platform
        status.wx_info["build"] = None
        if search('gtk2', wx.version()):
            status.wx_info["build"] = 'gtk'
        elif search('cocoa', wx.version()):
            status.wx_info["build"] = 'cocoa'
        elif search('mac-unicode', wx.version()):
            status.wx_info["build"] = 'carbon'
        status.wx_info["full"] = None
        if status.wx_info["build"]:
            status.wx_info["full"] = "%s-%s" % (status.wx_info["os"], status.wx_info["build"])

        # Some internal variables.
        self.test_suite_flag = False

        # The main window style.
        style = wx.DEFAULT_FRAME_STYLE
        if not status.debug and status.wx_info["os"] != 'darwin':
            style = style | wx.MAXIMIZE

        # Execute the base class __init__ method.
        super(Main, self).__init__(parent=parent, id=id, title=title, style=style)

        # Force the main window to start maximised (needed for MS Windows).
        if not status.debug and status.wx_info["os"] != 'darwin':
            self.Maximize()

        # Set up some standard interface-wide fonts.
        font.setup()

        # Set up the relax icons.
        relax_icons = Relax_icons()
        relax_icons.setup()
        self.SetIcons(relax_icons)

        # Initialise some variables for the GUI.
        self.launch_dir = getcwd()

        # Set up the frame.
        self.Layout()
        self.SetSize((self.min_width, self.min_height))
        self.SetMinSize((self.min_width, self.min_height))
        self.Centre()

        # The analysis window object storage.
        self.analysis = Analysis_controller(self)

        # The calculation threads list.
        self.calc_threads = []

        # Initialise the GUI data.
        self.init_data()

        # Build the menu bar.
        self.menu = Menu(self)

        # Build the toolbar.
        self.build_toolbar()

        # Build the controller, but don't show it.
        self.controller = Controller(self)

        # Set the window title.
        if version == "repository commit":
            win_title = "relax %s %s" % (version, repo_head)
        else:
            win_title = "relax %s" % version
        self.SetTitle(win_title)

        # Set up the status bar.
        self.status_bar = self.CreateStatusBar(4, 0)
        self.status_bar.SetStatusWidths([-4, -4, -1, -2])
        self.update_status_bar()

        # Add the start screen.
        self.add_start_screen()

        # Close Box event.
        self.Bind(wx.EVT_CLOSE, self.exit_gui)

        # Initialise the special interpreter thread object.
        self.interpreter = Interpreter()

        # Register functions with the observer objects.
        status.observers.pipe_alteration.register('status bar', self.update_status_bar, method_name='update_status_bar')
        status.observers.system_cwd_path.register('status bar', self.update_status_bar, method_name='update_status_bar')
        status.observers.result_file.register('gui', self.show_results_viewer_no_warn, method_name='show_results_viewer_no_warn')
        status.observers.exec_lock.register('gui', self.enable, method_name='enab')

        # Assume a script has been run and there is data in the store.
        self.analysis.load_from_store()

    def about_relax(self, event=None):
        """The about message for relax.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Build the dialog.
        dialog = About_relax(None, -1)

        # The dialog.
        if status.show_gui:
            dialog.Show()

    def action_export_bmrb(self, event=None):
        """Export the contents of the current data pipe for BMRB deposition.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # No current data pipe.
        if not cdp_name():
            gui_raise(RelaxNoPipeError())
            return

        # Open the export window.
        Export_bmrb_window(self)

    def action_state_save(self, event=None):
        """Save the program state.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Not saved yet, therefore pass execution to state_save_as().
        if not self.save_file:
            self.action_state_save_as(event)
            return

        # Save.
        self.state_save()

    def action_state_save_as(self, event=None):
        """Save the program state with file name selection.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # The dialog.
        dialog = RelaxFileDialog(parent=self, message='Select the relax save state file', defaultFile='state.bz2', wildcard='relax save file (*.bz2)|*.bz2', style=wx.FD_SAVE)

        # Show the dialog and catch if no file has been selected.
        if status.show_gui and dialog.ShowModal() != wx.ID_OK:
            # Don't do anything.
            return

        # The file.
        file_name = dialog.get_file()

        # Set the file name.
        self.save_file = file_name

        # Save.
        self.state_save()

    def add_start_screen(self):
        """Create a start screen for the main window when no analyses exist."""

        # The sizer for the main GUI window.
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(sizer)

        # The relax icon.
        image = wx.StaticBitmap(self, -1, bitmap_setup(IMAGE_PATH+'ulysses_shadowless_400x168.png'))
        sizer.AddStretchSpacer()
        sizer.Add(image, 0, wx.ALIGN_CENTER_HORIZONTAL, 0)
        sizer.AddStretchSpacer()

        # wxPython-Phoenix instability warning text.
        if not dep_check.wx_stable:
            text = [
                "wxPython-Phoenix version %s.%s.%s detected." % (wx.VERSION[0], wx.VERSION[1], wx.VERSION[2]),
                "This version of Phoenix is not stable and relax support is experimental.",
                "Not all features of the GUI may be available or functional.",
                "Please use wxPython \"Classic\" instead - otherwise use at your own risk."
            ]
            for i in range(len(text)):
                element = wx.StaticText(self, -1, text[i])
                element.SetFont(font.roman_font_18)
                element.SetForegroundColour("red")
                sizer.Add(element, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ADJUST_MINSIZE, 0)
            sizer.AddStretchSpacer()
            warn(RelaxWarning("  ".join(text)))

        # Re-perform the layout of the GUI elements, and refresh.
        self.Layout()
        self.Refresh()

    def build_toolbar(self):
        """Create the toolbar."""

        # Init.
        self.toolbar = self.CreateToolBar(wx.TB_HORIZONTAL|wx.TB_FLAT)

        # The new analysis button.
        if dep_check.wx_classic:
            self.toolbar.AddLabelTool(TB_FILE_NEW, "New analysis", wx.Bitmap(fetch_icon('oxygen.actions.document-new', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="New analysis")
        else:
            self.toolbar.AddTool(TB_FILE_NEW, "New analysis", wx.Bitmap(fetch_icon('oxygen.actions.document-new', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="New analysis")
        self.Bind(wx.EVT_TOOL, self.analysis.menu_new, id=TB_FILE_NEW)

        # The close analysis button.
        if dep_check.wx_classic:
            self.toolbar.AddLabelTool(TB_FILE_CLOSE, "Close analysis", wx.Bitmap(fetch_icon('oxygen.actions.document-close', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Close analysis")
        else:
            self.toolbar.AddTool(TB_FILE_CLOSE, "Close analysis", wx.Bitmap(fetch_icon('oxygen.actions.document-close', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Close analysis")
        self.Bind(wx.EVT_TOOL, self.analysis.menu_close, id=TB_FILE_CLOSE)

        # The close all analyses button.
        if dep_check.wx_classic:
            self.toolbar.AddLabelTool(TB_FILE_CLOSE_ALL, "Close all analyses", wx.Bitmap(fetch_icon('oxygen.actions.dialog-close', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Close all analyses")
        else:
            self.toolbar.AddTool(TB_FILE_CLOSE_ALL, "Close all analyses", wx.Bitmap(fetch_icon('oxygen.actions.dialog-close', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Close all analyses")
        self.Bind(wx.EVT_TOOL, self.analysis.menu_close_all, id=TB_FILE_CLOSE_ALL)

        # A separator.
        self.toolbar.AddSeparator()

        # The change working directory button.
        if dep_check.wx_classic:
            self.toolbar.AddLabelTool(TB_FILE_CWD, "Change working directory", wx.Bitmap(fetch_icon('oxygen.places.folder-favorites', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Change working directory")
        else:
            self.toolbar.AddTool(TB_FILE_CWD, "Change working directory", wx.Bitmap(fetch_icon('oxygen.places.folder-favorites', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Change working directory")
        self.Bind(wx.EVT_TOOL, self.system_cwd, id=TB_FILE_CWD)

        # The open state button.
        if dep_check.wx_classic:
            self.toolbar.AddLabelTool(TB_FILE_OPEN, "Open relax state", wx.Bitmap(fetch_icon('oxygen.actions.document-open', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Open relax state")
        else:
            self.toolbar.AddTool(TB_FILE_OPEN, "Open relax state", wx.Bitmap(fetch_icon('oxygen.actions.document-open', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Open relax state")
        self.Bind(wx.EVT_TOOL, self.state_load, id=TB_FILE_OPEN)

        # The save state button.
        if dep_check.wx_classic:
            self.toolbar.AddLabelTool(TB_FILE_SAVE, "Save relax state", wx.Bitmap(fetch_icon('oxygen.actions.document-save', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Save relax state")
        else:
            self.toolbar.AddTool(TB_FILE_SAVE, "Save relax state", wx.Bitmap(fetch_icon('oxygen.actions.document-save', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Save relax state")
        self.Bind(wx.EVT_TOOL, self.action_state_save, id=TB_FILE_SAVE)

        # The save as button.
        if dep_check.wx_classic:
            self.toolbar.AddLabelTool(TB_FILE_SAVE_AS, "Save as", wx.Bitmap(fetch_icon('oxygen.actions.document-save-as', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Save as")
        else:
            self.toolbar.AddTool(TB_FILE_SAVE_AS, "Save as", wx.Bitmap(fetch_icon('oxygen.actions.document-save-as', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Save as")
        self.Bind(wx.EVT_TOOL, self.action_state_save_as, id=TB_FILE_SAVE_AS)

        # A separator.
        self.toolbar.AddSeparator()

        # The relax controller button.
        if dep_check.wx_classic:
            self.toolbar.AddLabelTool(TB_VIEW_CONTROLLER, "Controller", wx.Bitmap(fetch_icon('oxygen.apps.preferences-system-performance', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="relax controller")
        else:
            self.toolbar.AddTool(TB_VIEW_CONTROLLER, "Controller", wx.Bitmap(fetch_icon('oxygen.apps.preferences-system-performance', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="relax controller")
        self.Bind(wx.EVT_TOOL, self.show_controller, id=TB_VIEW_CONTROLLER)

        # The spin viewer button.
        if dep_check.wx_classic:
            self.toolbar.AddLabelTool(TB_VIEW_SPIN_VIEW, "Spin viewer", wx.Bitmap(fetch_icon('relax.spin', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Spin viewer window")
        else:
            self.toolbar.AddTool(TB_VIEW_SPIN_VIEW, "Spin viewer", wx.Bitmap(fetch_icon('relax.spin', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Spin viewer window")
        self.Bind(wx.EVT_TOOL, self.show_tree, id=TB_VIEW_SPIN_VIEW)

        # The results viewer button.
        if dep_check.wx_classic:
            self.toolbar.AddLabelTool(TB_VIEW_RESULTS, "Results viewer", wx.Bitmap(fetch_icon('oxygen.actions.view-statistics', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Results viewer window")
        else:
            self.toolbar.AddTool(TB_VIEW_RESULTS, "Results viewer", wx.Bitmap(fetch_icon('oxygen.actions.view-statistics', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Results viewer window")
        self.Bind(wx.EVT_TOOL, self.show_results_viewer, id=TB_VIEW_RESULTS)

        # The data pipe editor button.
        if dep_check.wx_classic:
            self.toolbar.AddLabelTool(TB_VIEW_PIPE_EDIT, "Data pipe editor", wx.Bitmap(fetch_icon('relax.pipe', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Data pipe editor")
        else:
            self.toolbar.AddTool(TB_VIEW_PIPE_EDIT, "Data pipe editor", wx.Bitmap(fetch_icon('relax.pipe', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="Data pipe editor")
        self.Bind(wx.EVT_TOOL, self.show_pipe_editor, id=TB_VIEW_PIPE_EDIT)

        # The relax prompt button.
        if dep_check.wx_classic:
            self.toolbar.AddLabelTool(TB_VIEW_PROMPT, "relax prompt", wx.Bitmap(fetch_icon('oxygen.mimetypes.application-x-executable-script', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="The relax prompt GUI window")
        else:
            self.toolbar.AddTool(TB_VIEW_PROMPT, "relax prompt", wx.Bitmap(fetch_icon('oxygen.mimetypes.application-x-executable-script', "22x22"), wx.BITMAP_TYPE_ANY), shortHelp="The relax prompt GUI window")
        self.Bind(wx.EVT_TOOL, self.show_prompt, id=TB_VIEW_PROMPT)

        # Build the toolbar.
        self.toolbar.Realize()

    def close_windows(self):
        """Throw a warning to close all of the non-essential windows when execution is locked.

        This is to speed up the calculations by avoiding window updates.
        """

        # Init the window list.
        win_list = []

        # Is the spin viewer window open?
        if hasattr(self, 'spin_viewer') and self.spin_viewer.IsShown():
            win_list.append('The spin viewer window')

        # Is the pipe editor window open?
        if hasattr(self, 'pipe_editor') and self.pipe_editor.IsShown():
            win_list.append('The data pipe editor window')

        # Is the results viewer window open?
        if hasattr(self, 'results_viewer') and self.results_viewer.IsShown():
            win_list.append('The results viewer window')

        # The windows are not open, so quit.
        if not len(win_list):
            return

        # The text.
        text = "The following windows are currently open:\n\n"
        for win in win_list:
            text = "%s\t%s.\n" % (text, win)
        text = text + "\nClosing these will significantly speed up the calculations."

        # Display the error message dialog.
        dlg = wx.MessageDialog(self, text, caption="Close windows", style=wx.OK|wx.ICON_EXCLAMATION|wx.STAY_ON_TOP)
        if status.show_gui:
            dlg.ShowModal()

        # Otherwise output to stderr.
        else:
            sys.stderr.write(text)
            sys.stderr.flush()

    def contact_relax(self, event=None):
        """Write an email to the relax mailing-list using the standard mailing program.

        @keyword event: The wx event.
        @type event:    wx event
        """

        webbrowser.open_new('mailto:relax-users@gna.org')

    def enable(self):
        """Enable and disable certain parts of the main window with the execution lock."""

        # Flag for enabling or disabling the elements.
        enable = False
        if not status.exec_lock.locked():
            enable = True

        # The toolbar.
        wx.CallAfter(self.toolbar.EnableTool, TB_FILE_NEW, enable)
        wx.CallAfter(self.toolbar.EnableTool, TB_FILE_CLOSE, enable)
        wx.CallAfter(self.toolbar.EnableTool, TB_FILE_CLOSE_ALL, enable)
        wx.CallAfter(self.toolbar.EnableTool, TB_FILE_CWD, enable)
        wx.CallAfter(self.toolbar.EnableTool, TB_FILE_OPEN, enable)
        wx.CallAfter(self.toolbar.EnableTool, TB_FILE_SAVE, enable)
        wx.CallAfter(self.toolbar.EnableTool, TB_FILE_SAVE_AS, enable)

    def exit_gui(self, event=None):
        """Catch the main window closure and perform the exit procedure.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Ask if the user is sure they would like to exit.
        doexit = wx.ID_YES
        if status.show_gui and not ds.is_empty():
            doexit = Question('Are you sure you would like to quit relax?  All unsaved data will be lost.', title='Exit relax', default=True).ShowModal()

        # Exit.
        if doexit == wx.ID_YES:
            # Restore the IO streams.
            io_streams_restore(verbosity=0)

            # The relax information box.
            info = Info_box()

            # The width of the printout.
            if platform.uname()[0] in ['Windows', 'Microsoft']:
                width = 80
            else:
                width = 100

            # Remove the Mac OS X task bar icon.
            if hasattr(self, 'taskbar_icon'):
                self.taskbar_icon.Destroy()

            # Terminate the interpreter thread to allow for a cleaner exit.
            self.interpreter.exit()

            # End the GUI main loop.
            app = wx.GetApp()
            app.ExitMainLoop()

    def init_data(self):
        """Initialise the data used by the GUI interface."""

        # Temporary data:  the save file.
        self.save_file = None
        self.system_cwd_path = pwd(verbose=False)

        # Add the GUI object to the data store, if not present.
        if not hasattr(ds, 'relax_gui'):
            ds.relax_gui = Gui()

    def free_file_format_settings(self, event=None):
        """Open the free file format settings window.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Build the window.
        win = Free_file_format_window()

        # Show the window.
        if status.show_gui:
            win.Show()

    def references(self, event=None):
        """Display the references relevant for relax.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Build and show the references window.
        self.references = References(self)
        if status.show_gui:
            self.references.Show()

    def relax_manual(self, event=None):
        """Display the relax manual.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # The PDF manual.
        file = status.install_path + sep+"docs"+sep+"relax.pdf"

        # Test if it exists.
        if not access(file, F_OK):
            error_message("The relax manual '%s' cannot be found.  Please compile using the scons program." % file)
            return

        # Open the relax PDF manual using the native PDF reader.
        open_file(file)

    def reset(self):
        """Reset the GUI."""

        # Close some GUI windows, if open.
        windows = ['pipe_editor', 'relax_prompt', 'results_viewer', 'spin_viewer']
        for window in windows:
            if hasattr(self, window):
                # Get the object.
                win_obj = getattr(self, window)

                # Close the window.
                win_obj.Close()

        # Flush all wx events to make sure the GUI is ready for the next test.
        wx.Yield()

        # Reset the relax controller.
        self.controller.reset()

    def run_test_suite(self, event=None, categories=['system', 'unit', 'gui', 'verification']):
        """Execute the full test suite.

        @keyword event:         The wx event.
        @type event:            wx event
        @keyword categories:    The list of test categories to run, for example ['system', 'unit', 'gui', 'verification'] for all tests.
        @type categories:       list of str
        """

        # Ask if this should be done.
        msg = "In running the test suite, relax will be reset and all data lost.  Are you sure you would like to run the test suite?"
        if Question(msg, parent=self, size=(400, 150), default=False).ShowModal() == wx.ID_NO:
            return

        # Set the test suite flag.
        self.test_suite_flag = True

        # Change the cursor to waiting.
        wx.BeginBusyCursor()

        # Set a new style to stay on top, refreshing to update the style (needed for Mac OS X and MS Windows).
        orig_style = self.controller.GetWindowStyle()
        self.controller.SetWindowStyle(orig_style | wx.STAY_ON_TOP)
        self.controller.Refresh()

        # Make the relax controller modal so that all other windows are deactivated (to stop users from clicking on things).
        self.controller.MakeModal(True)

        # Close all open windows.
        if hasattr(self, 'spin_viewer'):
            self.spin_viewer.Close()
        if hasattr(self, 'pipe_editor'):
            self.pipe_editor.Close()
        if hasattr(self, 'results_viewer'):
            self.results_viewer.Close()
        if hasattr(self, 'relax_prompt'):
            self.relax_prompt.Close()

        # Reset relax.
        reset()

        # Show the relax controller.
        self.show_controller(event)

        # Yield
        wx.GetApp().Yield(True)

        # Prevent all new GUI elements from being shown.
        status.show_gui = False

        # Run the tests (with the import here to break a nasty circular import).
        import test_suite.test_suite_runner
        runner = test_suite.test_suite_runner.Test_suite_runner([], from_gui=True, categories=categories)
        runner.run_all_tests()

        # Reactive the GUI.
        status.show_gui = True

        # Turn off the busy cursor.
        if wx.IsBusy():
            wx.EndBusyCursor()

        # Restore the controller.
        self.controller.SetWindowStyle(orig_style)
        self.controller.MakeModal(False)
        self.controller.Refresh()

        # Unset the test suite flag.
        self.test_suite_flag = False

        # Set the controller main gauge to 100%.
        wx.CallAfter(self.controller.main_gauge.SetValue, 100)

    def run_test_suite_gui(self, event=None):
        """Execute the GUI tests.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Forward the call.
        self.run_test_suite(event, categories=['gui'])

    def run_test_suite_sys(self, event=None):
        """Execute the system tests.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Forward the call.
        self.run_test_suite(event, categories=['system'])

    def run_test_suite_unit(self, event=None):
        """Execute the unit tests.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Forward the call.
        self.run_test_suite(event, categories=['unit'])

    def run_test_suite_verification(self, event=None):
        """Execute the verification tests.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Forward the call.
        self.run_test_suite(event, categories=['verification'])

    def show_controller(self, event=None):
        """Display the relax controller window.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Bring the window to the front.
        if self.controller.IsShown():
            self.controller.Raise()
            return

        # Open the window.
        if status.show_gui:
            self.controller.Show()

    def show_pipe_editor(self, event=None):
        """Display the data pipe editor window.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Throw a warning if the execution lock is on.
        if status.exec_lock.locked():
            dlg = wx.MessageDialog(self, "Leaving the pipe editor window open will slow down the calculations.", caption="Warning", style=wx.OK|wx.ICON_EXCLAMATION|wx.STAY_ON_TOP)
            if status.show_gui:
                dlg.ShowModal()

        # Build the pipe editor if needed.
        if not hasattr(self, 'pipe_editor'):
            self.pipe_editor = Pipe_editor(gui=self)

        # Bring the window to the front.
        if self.pipe_editor.IsShown():
            self.pipe_editor.Raise()
            return

        # Open the window.
        if status.show_gui and not self.pipe_editor.IsShown():
            self.pipe_editor.Show()

        # Update the grid.
        self.pipe_editor.update_grid()
        self.pipe_editor.activate()

        # Register the grid for updating when a user function completes or when the GUI analysis tabs change (needed here for the window hiding and associated unregistering).
        self.pipe_editor.observer_setup(register=True)

    def show_prompt(self, event=None):
        """Display the relax prompt window.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Build the relax prompt if needed.
        if not hasattr(self, 'relax_prompt'):
            self.relax_prompt = Prompt(None, -1, "", parent=self)

        # Bring the window to the front.
        if self.relax_prompt.IsShown():
            self.relax_prompt.Raise()
            return

        # Open the window.
        if status.show_gui:
            self.relax_prompt.Show()

    def show_results_viewer(self, event=None):
        """Display the analysis results.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Show the results viewer in a thread safe way.
        wx.CallAfter(self.show_results_viewer_safe, warn=True)

    def show_results_viewer_safe(self, warn=False):
        """Display the analysis results in a thread safe wx.CallAfter call.

        @keyword warn:  A flag which if True will cause a message dialog to appear warning about keeping the window open with the execution lock.
        @type warn:     bool
        """

        # Throw a warning if the execution lock is on.
        if warn and status.exec_lock.locked():
            dlg = wx.MessageDialog(self, "Leaving the results viewer window open will slow down the calculations.", caption="Warning", style=wx.OK|wx.ICON_EXCLAMATION|wx.STAY_ON_TOP)
            if status.show_gui:
                wx.CallAfter(dlg.ShowModal)

        # Create the results viewer window if needed.
        if not hasattr(self, 'results_viewer'):
            self.results_viewer = Results_viewer(self)

        # Bring the window to the front.
        if self.results_viewer.IsShown():
            self.results_viewer.Raise()
            return

        # Open the window.
        if status.show_gui and not self.results_viewer.IsShown():
            self.results_viewer.Show()

    def show_results_viewer_no_warn(self):
        """Display the analysis results."""

        # Show the results viewer in a thread safe way with no warning dialog.
        wx.CallAfter(self.show_results_viewer_safe, warn=False)

    def show_tree(self, event=None):
        """Display the molecule, residue, and spin tree window.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # Throw a warning if the execution lock is on.
        if status.exec_lock.locked():
            dlg = wx.MessageDialog(self, "Leaving the spin viewer window open will slow down the calculations.", caption="Warning", style=wx.OK|wx.ICON_EXCLAMATION|wx.STAY_ON_TOP)
            if status.show_gui:
                dlg.ShowModal()

        # Build the spin view window.
        if not hasattr(self, 'spin_viewer'):
            self.spin_viewer = Spin_view_window(None, -1, "", parent=self)

        # Bring the window to the front.
        if self.spin_viewer.IsShown():
            self.spin_viewer.Raise()
            return

        # Open the window (the GUI flag check is inside the Show method).
        if not self.spin_viewer.IsShown():
            self.spin_viewer.Show(show=status.show_gui)

    def state_load(self, event=None, file_name=None):
        """Load the program state.

        @keyword event:     The wx event.
        @type event:        wx event
        @keyword file_name: The name of the file to load (for dialogless operation).
        @type file_name:    str
        """

        # Execution lock.
        if status.exec_lock.locked():
            return

        # Warning.
        if not self.analysis.init_state or not ds.is_empty():
            # The message.
            msg = "Loading a saved relax state file will cause all unsaved data to be lost.  Are you sure you would to open a save file?"

            # The dialog.
            if status.show_gui and Question(msg, default=True, size=(400, 150)).ShowModal() == wx.ID_NO:
                return

        # Open the dialog.
        if not file_name:
            dialog = RelaxFileDialog(parent=self, message='Select the relax save state file', defaultFile='state.bz2', wildcard='relax save files (*.bz2;*.gz)|*.bz2;*.gz|All files (*)|*', style=wx.FD_OPEN)

            # Show the dialog and catch if no file has been selected.
            if status.show_gui and dialog.ShowModal() != wx.ID_OK:
                # Don't do anything.
                return

            # The file.
            file_name = gui_to_str(dialog.get_file())

        # Yield to allow the cursor to be changed.
        wx.Yield()

        # Change the cursor to waiting, and freeze the GUI.
        wx.BeginBusyCursor()
        self.Freeze()

        # Make sure the GUI returns to normal if a failure occurs.
        try:
            # Delete the current tabs.
            self.analysis.delete_all()

            # Reset the relax data store.
            reset()

            # The new save file name.
            self.save_file = file_name

            # Load the relax state.
            if protected_exec(state.load_state, file_name, verbosity=0):
                # Update the core of the GUI to match the new data store.
                self.sync_ds(upload=False)

            # File loading failure.
            else:
                # Reset relax to clear any partially loaded data.
                reset()

                # Reinitialise the GUI data store structure.
                self.init_data()

        # Reset the cursor, and thaw the GUI.
        finally:
            self.Thaw()

            # Turn off the busy cursor.
            if wx.IsBusy():
                wx.EndBusyCursor()

    def state_save(self):
        """Save the program state."""

        # Update the data store to match the GUI.
        self.sync_ds(upload=True)

        # Save the relax state (with save user feedback).
        try:
            wx.BeginBusyCursor()
            state.save_state(self.save_file, verbosity=0, force=True)

            # Sleep a little so the user sees the busy cursor and knows that a save has occurred!
            sleep(1)

        # Turn off the user feedback.
        finally:
            if wx.IsBusy():
                wx.EndBusyCursor()

    def sync_ds(self, upload=False):
        """Synchronise the GUI and the relax data store, both ways.

        This method allows the GUI information to be uploaded into the relax data store, or for the information in the relax data store to be downloaded by the GUI.

        @keyword upload:    A flag which if True will cause the GUI to send data to the relax data store.  If False, data will be downloaded from the relax data store to update the GUI.
        @type upload:       bool
        """

        # Loop over each analysis.
        for page in self.analysis.analysis_loop():
            # Execute the analysis page specific update methods.
            if hasattr(page, 'sync_ds'):
                page.sync_ds(upload)

    def system_cwd(self, event=None):
        """Change the system current working directory.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # The dialog.
        dialog = RelaxDirDialog(parent=self, message="Select working directory", defaultPath=wx.EmptyString, style=wx.DD_CHANGE_DIR)

        # Show the dialog and catch if no directory has been selected.
        if status.show_gui and dialog.ShowModal() != wx.ID_OK:
            # Don't do anything.
            return

        # Call the get_path function to get the directory name and change path.
        self.system_cwd_path = dialog.get_path()

        # Update the status bar.
        self.update_status_bar()

        # Change the directory
        try:
            wx.BeginBusyCursor()

            # Sleep a little so the user sees the busy cursor and knows that the directory changes has occurred.
            sleep(1)

        # Turn off the user feedback.
        finally:
            if wx.IsBusy():
                wx.EndBusyCursor()

    def uf_call(self, event=None):
        """Catch the user function call to properly specify the parent window.

        @keyword event: The wx event.
        @type event:    wx event
        """

        # The user function ID.
        uf_id = event.GetId()

        # Get the user function name.
        name = uf_store.get_uf(uf_id)

        # Call the user function GUI object.
        uf_store[name](event=event, wx_parent=self)

    def update_status_bar(self):
        """Update the status bar info."""

        # Set the current data pipe info.
        pipe = cdp_name()

        # No data pipe.
        if pipe == None:
            pipe = ''

        # Get the current working directory
        self.system_cwd_path = pwd(verbose=False)

        # The relax information box.
        info = Info_box()

        # Set the status.
        wx.CallAfter(self.status_bar.SetStatusText, info.copyright_short, 0)
        wx.CallAfter(self.status_bar.SetStatusText, self.system_cwd_path, 1)
        wx.CallAfter(self.status_bar.SetStatusText, "Data pipe:", 2)
        wx.CallAfter(self.status_bar.SetStatusText, pipe, 3)


from cchess_alphazero.environment.light_env.common import *
from cchess_alphazero.environment.lookup_tables import Winner

class L_Chessboard:

    def __init__(self, init=None):
        self.height = 10
        self.width = 9
        self.board = [['.' for col in range(self.width)] for row in range(self.height)]
        self.steps = 0
        self._legal_moves = None
        self.turn = RED
        self.winner = None
        if init is None or init == '':
            self.assign_fen(None)
        else:
            self.parse_init(init)

    def _update(self):
        self._fen = None
        self._legal_moves = None
        self.steps += 1
        if self.steps % 2 == 0:
            self.turn = RED
        else:
            self.turn = BLACK

    def parse_init(self, init):
        pieces = 'rnbakabnrccpppppRNBAKABNRCCPPPPP'
        position = [init[i:i+2] for i in range(len(init)) if i % 2 == 0]
        for pos, piece in zip(position, pieces):
            if pos != '99':
                x, y = int(pos[0]), 9 - int(pos[1])
                self.board[y][x] = piece

    def assign_fen(self, fen):
        if fen is None:
            fen = init_fen
        x = 0
        y = 0
        for k in range(0, len(fen)):
            ch = fen[k]
            if ch == ' ':
                if (fen[k+1] == 'b'):
                    self.turn = BLACK
                break
            if ch == '/':
                x = 0
                y += 1
            elif ch >= '1' and ch <= '9':
                for i in range(int(ch)):
                    self.board[y][x] = '.'
                    x = x + 1
            else:
                self.board[y][x] = ch
                x = x + 1

    def FENboard(self):
        def swapcase(a):
            if a.isalpha():
                a = replace_dict[a]
                return a.lower() if a.isupper() else a.upper()
            return a

        c = 0
        fen = ''
        for i in range(self.height - 1, -1, -1):
            c = 0
            for j in range(self.width):
                if self.board[i][j] == '.':
                    c = c + 1
                else:
                    if c > 0:
                        fen = fen + str(c)
                    fen = fen + swapcase(self.board[i][j])
                    c = 0
            if c > 0:
                fen = fen + str(c)
            if i > 0:
                fen = fen + '/'
        if self.turn is RED:
            fen += ' r'
        else:
            fen += ' b'
        fen += ' - - 0 1'
        return fen

    def fliped_FENboard(self):
        fen = self.FENboard()
        foo = fen.split(' ')
        rows = foo[0].split('/')
        def swapcase(a):
            if a.isalpha():
                return a.lower() if a.isupper() else a.upper()
            return a
        def swapall(aa):
            return "".join([swapcase(a) for a in aa])

        return "/".join([swapall(reversed(row)) for row in reversed(rows)]) \
            + " " + foo[1] \
            + " " + foo[2] \
            + " " + foo[3] + " " + foo[4] + " " + foo[5]

    @property
    def is_red_turn(self):
        return self.turn == RED

    @property
    def screen(self):
        return self.board

    def legal_moves(self):
        if self._legal_moves is not None:
            return self._legal_moves

        _legal_moves = []
        for y in range(self.height):
            for x in range(self.width):
                ch = self.board[y][x]
                if (self.turn == RED and ch.isupper()):
                    continue
                if (self.turn == BLACK and ch.islower()):
                    continue
                if ch in mov_dir:
                    if (x == 0 and y == 3):
                        aa = 3
                    for d in mov_dir[ch]:
                        x_ = x + d[0]
                        y_ = y + d[1]
                        if not self._can_move(x_, y_):
                            continue
                        elif ch == 'p' and y < 5 and x_ != x:  # for red pawn
                            continue
                        elif ch == 'P' and y > 4 and x_ != x:  # for black pawn
                            continue
                        elif ch == 'n' or ch == 'N' or ch == 'b' or ch == 'B': # for knight and bishop
                            if self.board[y+int(d[1]/2)][x+int(d[0]/2)] != '.':
                                continue
                            elif ch == 'b' and y_ > 4:
                                continue
                            elif ch == 'B' and y_ < 5:
                                continue
                        elif ch != 'p' and ch != 'P': # for king and advisor
                            if x_ < 3 or x_ > 5:
                                continue
                            if (ch == 'k' or ch == 'a') and y_ > 2:
                                continue
                            if (ch == 'K' or ch == 'A') and y_ < 7:
                                continue
                        _legal_moves.append(move_to_str(x, y, x_, y_))
                        if (ch == 'k' and self.turn == RED): #for King to King check
                            d, u = self._y_board_from(x, y)
                            if (u < self.height and self.board[u][x] == 'K'):
                                _legal_moves.append(move_to_str(x, y, x, u))
                        elif (ch == 'K' and self.turn == BLACK):
                            d, u = self._y_board_from(x, y)
                            if (d > -1 and self.board[d][x] == 'k'):
                                _legal_moves.append(move_to_str(x, y, x, d))
                elif ch != '.': # for connon and root
                    l,r = self._x_board_from(x,y)
                    d,u = self._y_board_from(x,y)
                    for x_ in range(l+1,x):
                        _legal_moves.append(move_to_str(x, y, x_, y))
                    for x_ in range(x+1,r):
                        _legal_moves.append(move_to_str(x, y, x_, y))
                    for y_ in range(d+1,y):
                        _legal_moves.append(move_to_str(x, y, x, y_))
                    for y_ in range(y+1,u):
                        _legal_moves.append(move_to_str(x, y, x, y_))
                    if ch == 'r' or ch == 'R': # for root
                        if self._can_move(l, y):
                            _legal_moves.append(move_to_str(x, y, l, y))
                        if self._can_move(r, y):
                            _legal_moves.append(move_to_str(x, y, r, y))
                        if self._can_move(x, d):
                            _legal_moves.append(move_to_str(x, y, x, d))
                        if self._can_move(x, u):
                            _legal_moves.append(move_to_str(x, y, x, u))
                    else: # for connon
                        l_, _ = self._x_board_from(l,y)
                        _, r_ = self._x_board_from(r,y)
                        d_, _ = self._y_board_from(x,d)
                        _, u_ = self._y_board_from(x,u)
                        if self._can_move(l_, y):
                            _legal_moves.append(move_to_str(x, y, l_, y))
                        if self._can_move(r_, y):
                            _legal_moves.append(move_to_str(x, y, r_, y))
                        if self._can_move(x, d_):
                            _legal_moves.append(move_to_str(x, y, x, d_))
                        if self._can_move(x, u_):
                            _legal_moves.append(move_to_str(x, y, x, u_))

        self._legal_moves = _legal_moves
        return _legal_moves

    def is_legal(self, mov):
        return mov.uci in self.legal_moves

    def is_end(self):
        red_k, black_k = [0, 0], [0, 0]
        for i in range(self.height):
            for j in range(self.width):
                if self.board[i][j] == 'k':
                    red_k[0] = i
                    red_k[1] = j
                if self.board[i][j] == 'K':
                    black_k[0] = i
                    black_k[1] = j
        if red_k[0] == 0 and red_k[1] == 0:
            self.winner = Winner.black
        elif black_k[0] == 0 and black_k[1] == 0:
            self.winner = Winner.red
        elif red_k[1] == black_k[1]:
            has_block = False
            i = red_k[0] + 1
            while i < black_k[0]:
                if self.board[i][red_k[1]] != '.':
                    has_block = True
                    break
                i += 1
            if not has_block:
                if self.turn == RED:
                    self.winner = Winner.red
                else:
                    self.winner = Winner.black
        return self.winner is not None

    def print_to_cl(self):
        for i in range(9, -1, -1):
            print(self.board[i])

    def move_action_str(self, uci):
        mov = Move(uci)
        self.push(mov)
        return True

    def push(self, mov):
        self.board[mov.n[1]][mov.n[0]] = self.board[mov.p[1]][mov.p[0]]
        self.board[mov.p[1]][mov.p[0]] = '.'
        self._update()

    def _is_same_side(self,x,y):
        if self.turn == RED and self.board[y][x].islower():
            return True
        if self.turn == BLACK and self.board[y][x].isupper():
            return True

    def _can_move(self,x,y): # basically check the move
        if x < 0 or x > self.width-1:
            return False
        if y < 0 or y > self.height-1:
            return False
        if self._is_same_side(x,y):
            return False
        return True

    def _x_board_from(self,x,y):
        l = x-1
        r = x+1
        while l > -1 and self.board[y][l] == '.':
            l = l-1
        while r < self.width and self.board[y][r] == '.':
            r = r+1
        return l,r

    def _y_board_from(self,x,y):
        d = y-1
        u = y+1
        while d > -1 and self.board[d][x] == '.':
            d = d-1
        while u < self.height and self.board[u][x] == '.':
            u = u+1
        return d,u

    def result(self, claim_draw=True) -> str:
        rst = '*'
        if ('k' not in self.board[0]) and ('k' not in self.board[1]) and ('k' not in self.board[2]):
            rst = '0-1'
        if ('K' not in self.board[9]) and ('K' not in self.board[8]) and ('K' not in self.board[7]):
            rst = '1-0'
        return rst

    def clear_chessmans_moving_list(self):
        return

    def calc_chessmans_moving_list(self):
        return

    def save_record(self, filename):
        return

    def parse_WXF_move(self, wxf):
        '''
        red is upper, black is lower alphabet
        '''
        p = self.swapcase(wxf[0])
        col = wxf[1]
        mov = wxf[2]
        dest_col = wxf[3]
        src_row, src_col = self.find_row(p, col)
        if mov == '.' or mov == '=':
            # move horizontally
            dest_row = src_row
            if p.islower():
                dest_col = int(dest_col) - 1
            else:
                dest_col = self.width - int(dest_col)
        else:
            if p == 'h' or p == 'H' or p == 'e' or p == 'E' or p == 'a' or p == 'A':
                if p.islower():
                    dest_col = int(dest_col) - 1
                else:
                    dest_col = self.width - int(dest_col)

                if p == 'h' or p == 'H':
                    # for house/knight
                    step = 1 if abs(dest_col - src_col) == 2 else 2
                elif p == 'e' or p == 'E':
                    # for elephant/bishop
                    step = 2
                else:
                    # for advisor
                    step = 1
                if mov == '+' and p.islower() or mov == '-' and p.isupper():
                    dest_row = src_row + step
                else:
                    dest_row = src_row - step
            else:
                # move vertically
                step = int(dest_col)
                if mov == '+' and p.islower() or mov == '-' and p.isupper():
                    dest_row = src_row + step
                else:
                    dest_row = src_row - step
                dest_col = src_col
        return move_to_str(src_col, src_row, dest_col, dest_row)

    def find_row(self, piece, col):
        if piece == 'h' or piece == 'H':
            piece = 'n' if piece == 'h' else 'N'
        if piece == 'e' or piece == 'E':
            piece = 'b' if piece == 'e' else 'B'
        column = 0
        row = -1
        if col.isdigit():
            if piece.isupper():
                column = self.width - int(col)
            else:
                column = int(col) - 1
            for i in range(self.height):
                if self.board[i][int(column)] == piece:
                    row = i
                    break
        else:
            first_row = -1
            second_row = -1
            column = -1
            for j in range(self.width):
                column = -1
                for i in range(self.height):
                    if self.board[i][j] == piece:
                        if column == -1:
                            column = j
                            first_row = i
                        else:
                            if column == j:
                                second_row = i
                                break
                            else:
                                column = j
                                first_row = second_row = -1
                if first_row != -1 and second_row != -1:
                    break
            if (piece.islower() and col == '+') or (piece.isupper() and col == '-'):
                row = second_row
            else:
                row = first_row
        return row, column

    def swapcase(self, a):
        if a.isalpha():
            return a.lower() if a.isupper() else a.upper()
        return a

if __name__ == '__main__': # test
    board = Chessboard()
    print(board.legal_moves)


"""

6.Rock Strength & Elastic Properties

"""

# function for Young's modulus computation

def yme_cal(**kwargs):
    """
    input:  dataframe = well logging data in dataframe,
            las = well logging data in las file (.las)
    ...
    output: Young's modulus
    """

    dataframe = kwargs.get('dataframe')
    las = kwargs.get('las')

    from mem.rsepeq import syme_sand, syme_shale

    # calculate dynamic Young's modulus

    dataframe['DYME'] = dyme_eq(RHOB=dataframe.RHOB_MRG, DTC=dataframe.DTC_MRG, DTS=dataframe.DTS_MRG)

    # calculate static Young's modulus

    for litho in ['SAND', 'SHALE']:

        # prepare input parameters

        Rhob = dataframe.loc[dataframe.LITHO == litho, 'RHOB_MRG'].dropna()
        dt = dataframe.loc[dataframe.LITHO == litho, 'DTC_MRG'].dropna()
        Vp = (1/dt)
        Phie = dataframe.loc[dataframe.LITHO == litho, 'PHIE'].dropna()
        Dyme = dataframe.loc[dataframe.LITHO == litho, 'DYME'].dropna()

        # unit conversion for empirical equations

        Dyme *= 6.8948e-6 # 6.8948e-6 for psi unit to GPa unit
        Vp *= 304.800 # 304.800 for ft/us to km/s

        # calculation

        if litho == 'SAND':
            Syme = syme_sand(Rhob=Rhob, dt=dt, Vp=Vp, Phie=Phie, Dyme=Dyme)
            dataframe['YME'] = Syme
        else:
            Syme = syme_shale(Rhob=Rhob, dt=dt, Vp=Vp, Phie=Phie, Dyme=Dyme)
            dataframe['YME'].fillna(Syme, inplace = True)

    # unit conversion

    dataframe['YME'] *= 145038 # 145038 for GPa unit to psi unit

    # update LAS file

    las.append_curve('DYME', dataframe['DYME'], unit = 'psi', descr = 'Dynamic Young\'s modulus', value = '')
    las.append_curve('YME', dataframe['YME'], unit = 'psi', descr = 'Static Young\'s modulus', value = '')

    return dataframe, las

# dynamic Young's modulus equation

def dyme_eq(**kwargs):
    """
    input:  RHOB = density in grams per cubic centimetre unit [g/c3]
            DTC = slowness in microseconds per foot unit [us/ft],
            DTS = slowness in microseconds per foot unit [us/ft]
    ...
    output: dynamic Young's modulus in pound per square inch unit [psi]
    """

    RHOB = kwargs.get('RHOB')
    DTC = kwargs.get('DTC')
    DTS = kwargs.get('DTS')

    # convert slowness (us/ft) to velosity (m/s)

    Vp = (1/DTC) * 304800 # 304800 for ft/us to m/s
    Vs = (1/DTS) * 304800

    # compute dynamic Young's modulus (psi)

    term1 = (3*(Vp**2)) - (4*(Vs**2))
    term2 = (Vp**2) - (Vs**2)

    DYME = RHOB * (Vs**2) * (term1/term2) * 0.145038 # 0.145038 for conversion factor

    return DYME

# function for Poisson's ratio computation

def pr_cal(**kwargs):
    """
    input:  dataframe = well logging data in dataframe,
            las = well logging data in las file (.las)
    ...
    output: Poisson's ratio
    """

    dataframe = kwargs.get('dataframe')
    las = kwargs.get('las')

    # calculate dynamic Poisson's ratio

    dataframe['DPR'] = dpr_eq(DTC=dataframe.DTC_MRG, DTS=dataframe.DTS_MRG)

    # calculate static Poisson's ratio

    factor = 1.0

    dataframe['PR'] = factor * dataframe.DPR

    # update LAS file

    las.append_curve('DPR', dataframe['DPR'], unit = 'unitless', descr = 'Dynamic Poisson\'s ratio', value = '')
    las.append_curve('PR', dataframe['PR'], unit = 'unitless', descr = 'Static Poisson\'s ratio', value = '')

    return dataframe, las

# dynamic Possion's ratio equation

def dpr_eq(**kwargs):
    """
    input:  DTC = slowness in microseconds per foot unit [us/ft],
            DTS = slowness in microseconds per foot unit [us/ft]
    ...
    output: dynamic Poisson's ratio
    """

    DTC = kwargs.get('DTC')
    DTS = kwargs.get('DTS')

    # convert slowness (us/ft) to velosity (m/s)

    Vp = (1/DTC) * 304800 # 304800 for ft/us to m/s
    Vs = (1/DTS) * 304800

    # compute dynamic Poisson's ratio

    term1 = (Vp**2) - (2*(Vs**2))
    term2 = (Vp**2) - (Vs**2)

    DPR = 0.5 * (term1/term2)

    return DPR

# function for UCS computation

def ucs_cal(**kwargs):
    """
    input:  dataframe = well logging data in dataframe,
            las = well logging data in las file (.las)
    ...
    output: unconfined compressive strength (UCS)
    """

    dataframe = kwargs.get('dataframe')
    las = kwargs.get('las')

    from mem.rsepeq import ucs_sand, ucs_shale

    # calculate unconfined compressive strength

    for litho in ['SAND', 'SHALE']:

        # prepare input parameters

        Rhob = dataframe.loc[dataframe.LITHO == litho, 'RHOB_MRG'].dropna()
        dt = dataframe.loc[dataframe.LITHO == litho, 'DTC_MRG'].dropna()
        Vp = (1/dt)
        Phie = dataframe.loc[dataframe.LITHO == litho, 'PHIE'].dropna()
        Syme = dataframe.loc[dataframe.LITHO == litho, 'YME'].dropna()

        # unit conversion for empirical equations

        Syme *= 6.8948e-6 # 6.8948e-6 for psi unit to GPa unit
        Vp *= 304.800 # 304.800 for ft/us to km/s

        # calculation

        if litho == 'SAND':
            UCS = ucs_sand(Rhob=Rhob, dt=dt, Vp=Vp, Phie=Phie, Syme=Syme)
            dataframe['UCS'] = UCS
        else:
            UCS = ucs_shale(Rhob=Rhob, dt=dt, Vp=Vp, Phie=Phie, Syme=Syme)
            dataframe['UCS'].fillna(UCS, inplace = True)

    # unit conversion

    dataframe['UCS'] *= 145.038 # 145.038 for MPa unit to psi unit

    # update LAS file

    las.append_curve('UCS', dataframe['UCS'], unit = 'psi', descr = 'Unconfined compressive strength', value = '')

    return dataframe, las

# function for friction angle computation

def fang_cal(**kwargs):
    """
    input:  dataframe = well logging data in dataframe,
            las = well logging data in las file (.las)
    ...
    output: friction angle
    """

    dataframe = kwargs.get('dataframe')
    las = kwargs.get('las')

    # calculate friction angle using linear correlation method with volume of clay
    # 100% shale relate to 20 degree friction angle
    # 100% sand relate to 40 degree friction angle

    dataframe['FANG'] = ((1 - dataframe.VCL) + 1) * 20

    # update LAS file

    las.append_curve('FANG', dataframe['FANG'], unit = 'degree', descr = 'Angle of internal friction', value = '')

    return dataframe, las

# function for tensile strength computation

def tstr_cal(**kwargs):
    """
    input:  dataframe = well logging data in dataframe,
            las = well logging data in las file (.las)
    ...
    output: tensile strength
    """

    dataframe = kwargs.get('dataframe')
    las = kwargs.get('las')

    # calculate tensile strength

    dataframe['TSTR'] = dataframe.UCS * 0.1

    # update LAS file

    las.append_curve('TSTR', dataframe['TSTR'], unit = 'psi', descr = 'Tensile formation strength', value = '')

    return dataframe, las


from itertools import count
import json
import re
from time import sleep

import httpx
from lxml.etree import HTML
from _log import log

class Unipus:
    def __init__(self):
        self.client = httpx.Client()
        with open("config.json", "r") as f:
            self.config = json.load(f)
        self.baseURL = self.config["baseURL"]

    def login(self):
        index = self.client.get(f"{self.baseURL}/index.php", follow_redirects=True)
        # 这里需要更新 Referer 否则无法登录
        self.client.headers.update({"Referer": str(index.url)})
        html = HTML(index.text)

        hidden = html.xpath('//input[@type="hidden"]')
        data = {i.get("name"): i.get("value") for i in hidden}
        data.update(self.config)

        self.client.post(f"{self.baseURL}/index.php", data=data)
        # 验证登录是否成功
        hpindex_student = self.client.get(f"{self.baseURL}/login/hpindex_student.php")
        if hpindex_student.text.find(self.config["username"]) != -1:
            log.info("登陆成功")

    def book(self, bookID, classID):
        # 需要进入书籍才能进行学习
        self.client.get(
            f"{self.baseURL}/book/index.php",
            params={"BookID": bookID, "ClassID": classID, "Quiz": "N"},
        )

    def initPage(self, unitID, sectionID, sisterID):
        data = {
            "UnitID": unitID,
            "SectionID": sectionID,
            "SisterID": sisterID,
        }

        initPagePHP = self.client.post(
            f"{self.baseURL}/book/book184/initPage.php", data=data
        )
        key = json.loads(initPagePHP.text)
        # log.info(key)
        return key

    def answer(self, unitID, sectionID, sisterID):
        key = self.initPage(unitID, sectionID, sisterID)
        if not key["ItemID"]:
            log.info("题目不存在")
            return False
        elif not key["key"]:
            log.info("无需作答")
            return True

        answer = key["key"].split("^")
        answer = list(i.split("|")[0] if "|" in i else i for i in answer)

        data = {
            "ItemID": key["ItemID"],
            "answer[]": answer,
        }
        # log.info(data)

        post = self.client.post(
            "http://172.31.241.173/book/book184/postDrag.php", data=data
        )
        # log.info(post.text)

        data = {
            "UnitID": unitID,
            "SectionID": sectionID,
            "SisterID": sisterID,
        }
        done = self.client.post(
            "http://172.31.241.173/book/book184/done.php", data=data
        )
        log.info("".join(re.findall(r"Your score: (.*)%", done.text)))
        return True

    # def get

unipus = Unipus()
unipus.login()
unipus.book(184, 712)
unipus.answer(8,2,2)
unipus.answer(4,3,3)

# for i in range(1, 9):
#     for j in count(1):
#         log.info(f"{i}-{j}-{1}")
#         if not unipus.answer(i, j, 1):
#             break
#         for k in count(1):
#             log.info(f"{i}-{j}-{k}")
#             if not unipus.answer(i, j, k):
#                 break
#             sleep(1)

# for i in range(1, 9):
#     for j in range(3,4):
#         log.info(f"{i}-{j}-{1}")
#         if not unipus.answer(i, j, 1):
#             break
#         for k in count(6):
#             log.info(f"{i}-{j}-{k}")
#             if not unipus.answer(i, j, k):
#                 break
#             sleep(1)


import time
import functools

def wrapper_time(a_function):
    def func_with_wrapper(*args,**kwargs):
        time_start = time.time()
        result = a_function(*args,**kwargs)
        time_stop = time.time()
        execution_time = time_stop - time_start
        #print('Funkcja ()  wykonana w czasie ()}'.format(a_function.__name__,execution_time))
        print(">>>>>Function {} executed in {}".format(a_function.__name__, time_stop - time_start))
        return result
    return func_with_wrapper

@wrapper_time
def get_sequence(n):
    if n <= 0:
        return 1
    else:
        v = 0
        for i in range(n):
            v += 1 + (get_sequence(i - 1) + get_sequence(i))/2
        return v

print(get_sequence(10))


from pyPdf import PdfFileWriter, PdfFileReader

output = PdfFileWriter()
input1 = PdfFileReader(file("document1.pdf", "rb"))
watermark = PdfFileReader(file("watermark.pdf", "rb"))

input1.mergePage(watermark.getPage(0))

# finally, write "output" to document-output.pdf
outputStream = file("document-output.pdf", "wb")
output.write(input1)
outputStream.close()


"""
leave/models.py - Module containing models for leave management.

This module defines the LeaveApplication model for managing leave-related information.
"""

from django.db import models
from django.conf import settings
from django.contrib.auth.models import User

class LeaveApplication(models.Model):

    """
    Model for Leave Applications.

    Represents a leave application with information such as start date, end date, status,
    requested user, processing user, and reason.
    """
    STATUS_CHOICES = [
        ('pending', 'Pending'),
        ('approved', 'Approved'),
        ('rejected', 'Rejected'),
    ]

    start_date = models.DateField()
    end_date = models.DateField()
    status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='pending')
    requested_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='leave_requests'
    )
    processed_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='processed_leaves'
    )

    reason = models.TextField()

    def __str__(self):
        """
        String representation of the LeaveApplication.

        Returns a formatted string with the requested user's username.
        """
        return f"{self.requested_by.username}'s Leave Request"


#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
@DEPRECATED
This is deprecated in favor of the Meter Location History.

Static Meter Record Loading.

Insert Meter Records into the database from a tab-separated data file.

Usage:
insertMeterRecords.py ${FILENAME}

"""

__author__ = 'Daniel Zhang (張道博)'
__copyright__ = 'Copyright (c) 2013, University of Hawaii Smart Energy Project'
__license__ = 'https://raw.github' \
              '.com/Hawaii-Smart-Energy-Project/Maui-Smart-Grid/master/BSD' \
              '-LICENSE.txt'

import csv
import sys
from msg_db_connector import MSGDBConnector
from msg_db_util import MSGDBUtil
from msg_notifier import MSGNotifier
from msg_configer import MSGConfiger

if __name__ == '__main__':

    filename = sys.argv[1]

    configer = MSGConfiger()
    connector = MSGDBConnector()
    conn = connector.connectDB()
    cur = conn.cursor()
    dbUtil = MSGDBUtil()
    notifier = MSGNotifier()
    msgBody = ''

    dbName = configer.configOptionValue("Database", "db_name")

    msg = ("Loading static meter record data in file %s to database %s.\n" % (
    filename, dbName))
    sys.stderr.write(msg)
    msgBody += msg

    f = open(filename, "r")
    reader = csv.reader(f)

    data = []
    cols = ['type', 'action', 'did_sub_type', 'device_util_id', 'device_serial_no',
            'device_status', 'device_operational_status', 'device_name',
            'device_description', 'device_mfg', 'device_mfg_date',
            'device_mfg_model', 'device_sw_ver_no', 'device_sw_rev_no',
            'device_sw_patch_no', 'device_sw_config', 'device_hw_ver_no',
            'device_hw_rev_no', 'device_hw_patch_no', 'device_hw_config',
            'meter_form_type', 'meter_base_type', 'max_amp_class', 'rollover_point',
            'volt_type', 'nic_mac_address', 'nic_serial_no', 'nic_rf_channel',
            'nic_network_identifier', 'nic_model', 'nic_sw_ver_no', 'nic_sw_rev_no',
            'nic_sw_patch_no', 'nic_released_date', 'nic_sw_config',
            'nic_hw_ver_no', 'nic_hw_rev_no', 'nic_hw_patch_no', 'nic_hw_config',
            'master_password', 'reader_password', 'cust_password', 'meter_mode',
            'timezone_region', 'battery_mfg_name', 'battery_model_no',
            'battery_serial_no', 'battery_mfg_date', 'battery_exp_date',
            'battery_installed_date', 'battery_lot_no', 'battery_last_tested_date',
            'price_program', 'catalog_number', 'program_seal', 'meter_program_id',
            'device_attribute_1', 'device_attribute_2', 'device_attribute_3',
            'device_attribute_4', 'device_attribute_5', 'nic_attribute_1',
            'nic_attribute_2', 'nic_attribute_3', 'nic_attribute_4',
            'nic_attribute_5']

    lineCnt = 0

    with open(filename) as tsv:
        for line in csv.reader(tsv, delimiter = "\t"):
            if lineCnt != 0:

                data = line[0:66]

                for i in range(0, 66):

                    if len(data[i]) == 0:
                        data[i] = 'NULL'
                    else:
                        data[i] = "'" + data[i] + "'"

                sql = """INSERT INTO "MeterRecords" (%s) VALUES (%s)""" % (
                    ','.join(cols), ','.join(data))

                dbUtil.executeSQL(cur, sql)

            lineCnt += 1

    conn.commit()

    msg = ("Processed %s lines.\n" % lineCnt)
    sys.stderr.write(msg)
    msgBody += msg

    notifier.sendNotificationEmail(msgBody)


#!/usr/bin/python

from PyQt4 import QtGui, QtCore
import gui
import sys
import os
import subprocess

class Score(QtGui.QMainWindow):
	def __init__(self):
		super(Score, self).__init__()
		self.ui = gui.Ui_MainWindow()
		self.ui.setupUi(self)
		self.setWindowTitle('Alarm Manager')
		self.ui.setAlarms.clicked.connect(self.set)
		self.centerOnScreen()
		self.show()

	def centerOnScreen (self):
		resolution = QtGui.QDesktopWidget().screenGeometry()
		self.move((resolution.width() / 2) - (self.frameSize().width() / 2), (resolution.height() / 2) - (self.frameSize().height() / 2))

	def set(self):
		self.hr1 = str(self.ui.hr1.toPlainText())
		self.hr2 = str(self.ui.hr2.toPlainText())
		self.hr3 = str(self.ui.hr3.toPlainText())
		self.hr4 = str(self.ui.hr4.toPlainText())
		self.min1 = str(self.ui.min1.toPlainText())
		self.min2 = str(self.ui.min2.toPlainText())
		self.min3 = str(self.ui.min3.toPlainText())
		self.min4 = str(self.ui.min4.toPlainText())
		self.message1 = str(self.ui.message1.toPlainText())
		self.message2 = str(self.ui.message2.toPlainText())
		self.message3 = str(self.ui.message3.toPlainText())
		self.message4 = str(self.ui.message4.toPlainText())

		if(self.hr1 != "" and self.min1 != ""):
			if len(self.hr1) == 1:
				self.hr1 = "0"+self.hr1

			if len(self.min1) == 1:
				self.min1 = "0"+self.min1

			os.system("/opt/alarm-manager/./script.sh" + " " + self.hr1 + " " + self.min1 + " " + self.message1.rstrip() + "&")

		if(self.hr2 != "" and self.min2 != ""):
			if len(self.hr2) == 1:
				self.hr2 = "0"+self.hr2

			if len(self.min2) == 1:
				self.min2 = "0"+self.min2

			os.system("/opt/alarm-manager/./script.sh" + " " + self.hr2 + " " + self.min2 + " " + self.message2.rstrip() + "&")

		if(self.hr3 != "" and self.min3 != ""):
			if len(self.hr3) == 1:
				self.hr3 = "0"+self.hr3

			if len(self.min3) == 1:
				self.min3 = "0"+self.min3

			os.system("/opt/alarm-manager/./script.sh" + " " + self.hr3 + " " + self.min3 + " " + self.message3.rstrip() + "&")

		if(self.hr4 != "" and self.min4 != ""):
			if len(self.hr4) == 1:
				self.hr4 = "0"+self.hr4

			if len(self.min4) == 1:
				self.min4 = "0"+self.min4

			os.system("/opt/alarm-manager/./script.sh" + " " + self.hr4 + " " + self.min4 + " " + self.message4.rstrip() + "&")

		self.close()

if __name__ == '__main__':
	app = QtGui.QApplication(sys.argv)
	o = Score()
	sys.exit(app.exec_())


from tkinter import *

def button_clicked():
    nr_miles = int(input_val.get())
    nr_km = "{:.2f}".format(nr_miles * 1.609)
    label_km_calculated = Label(text=str(nr_km))
    label_km_calculated.grid(row=1, column=1)

# Window

window = Tk()
window.title("Miles to Km Converter")
window.minsize(width=300, height=100)
window.config(padx=50, pady=20)

# Entry

input_val = Entry()
input_val.config(width=10)
input_val.grid(row=0, column=1)

# Labels

label_miles = Label(text="Miles")
label_miles.grid(row=0, column=2)

label_is_eq = Label(text="is equal to")
label_is_eq.grid(row=1, column=0)

label_km = Label(text="Km")
label_km.grid(row=1, column=2)

# Button

button = Button(text="Calculate")
button.grid(row=2, column=1)
button.config(command=button_clicked)

window.mainloop()


from subprocess import run, getoutput
from shlex import split
import re

def get_all_nvidia_modules():
    all_modules = getoutput("lsmod").splitlines()
    modules_to_unload = set()
    for m in all_modules:
        m = m.strip()
        m_splitted = re.split("\s+", m)

        module_name = m_splitted[0]
        if len(m_splitted) == 4:
            deps = m_splitted[-1].split(",")
        else:
            deps = []

        if "nvidia" in module_name or any("nvidia" in d for d in deps):
            modules_to_unload.add(module_name)
            for d in deps:
                modules_to_unload.add(d)

    return modules_to_unload

def get_usage_pids(pattern):
    all_files = getoutput("lsof").splitlines()
    pids = set()
    commands = []
    for f in all_files:
        if pattern in f:
            f.strip()
            pid = re.split("\s+", f)[1]
            pids.add(pid)
            commands.append(f)

    return pids

def unload_all_nvidia_modules():
    cnt = 100
    while cnt > 0:
        cnt -= 1
        modules = get_all_nvidia_modules()

        if len(modules) == 0:
            break

        for m in modules:
            pids = get_usage_pids(m)
            for pid in pids:
                run(split(f"killall -9 {pid}"))

            run(split(f"rmmod {m}"))

if __name__ == "__main__":
    get_all_nvidia_modules()
    unload_all_nvidia_modules()


import nltk
import re
import os
#for text summarization
tokenizer = nltk.RegexpTokenizer(r"\w+")
stopwords = nltk.corpus.stopwords.words('english')
stemmer = nltk.stem.PorterStemmer()
class Node:
    def __init__(self, s):
        s = re.sub("\[[0-9]+\]", '', s)#remove any reference numbering
        self.origional = s
        self.content = tokenizer.tokenize(s)
        self.content = [w for w in self.content if not w in stopwords]
        for i in range(len(self.content)):
            self.content[i] = stemmer.stem(self.content[i])
        self.adjweight = []
        self.weightsum = 0.0
        self.score = 0.5

def distance(a, b):
    count = 0;
    for c in a.content:
        for d in b.content:
            if c == d:
                count = count + 1
    return float(count) / (float(len(a.content)) * float(len(b.content)))

class Graph:
    nodes = []

    def addNode(self, s):
        pos = len(self.nodes)
        self.nodes.append(Node(s))
        if(len(self.nodes[pos].content) == 0):
            self.nodes.pop()
            return
        for i in range(pos):
            weight = distance(self.nodes[pos], self.nodes[i])
            self.nodes[i].adjweight.append(weight)
            self.nodes[i].weightsum = self.nodes[i].weightsum + weight
            self.nodes[pos].adjweight.append(weight)
            self.nodes[pos].weightsum = self.nodes[pos].weightsum + weight
        self.nodes[pos].adjweight.append(0.0)

    def calcScore(self):
        #deal with totally irelevant ones
        n = 0
        i = 0
        while i + n < len(self.nodes):
            if self.nodes[i].weightsum == 0:
                self.nodes.pop(i)
                i = i - 1
                n = n + 1
            i = i + 1
        #calculate scores
        th = 0.0001
        beta = 0.875
        prev = 1.0
        current = 100000.0
        while abs(current - prev) > th:
            for i in range(len(self.nodes)):
                tmp = 0.0
                for j in range(len(self.nodes)):
                    tmp = tmp + self.nodes[j].adjweight[i] / self.nodes[j].weightsum
                self.nodes[i].score = 1 - beta + beta * tmp
            prev = current
            current = self.nodes[0].score

    def printMax(self, wc):
        c = 0
        arr = []
        for i in range(len(self.nodes)):
            arr.append((self.nodes[i].score, i))
        arr.sort(reverse = True)
        i = 0
        while c < wc:
            w = graph.nodes[arr[i][1]].origional
            c = c + len(w)
            i = i + 1

#main function
graph = Graph()
word = input()
searchword = word.replace(' ', '+')

address = "http://www.google.com/search?q='" + searchword +"'"
webpage = os.popen("lynx -accept_all_cookies -dump -width=10000 " + address).read()
ind = webpage.rfind("References")
content = webpage[:ind]
reference = webpage[ind:]

link = "[5]"
ref = "5."
ind = content.find("Showing results for")
if ind != -1: #if the input happends to be wrong
    content = content[ind+1:]
    link = "[7]"
    ref = "7."
    ind = content.find('\n')
    content = content[ind + 1:]
else:
    for i in range(4):
        ind = content.find('\n')
        content = content[ind + 1:]

ind = content.find(link)
answer = content[:ind]

if not(len(answer.strip()) == 0):#if the first is not a link to other site
    print(answer)
else:
    #find the first link
    ind = reference.find(ref)
    reference = reference[ind + 3:]
    ind = reference.find('\n')
    reference = reference[:ind]
    webpage = os.popen("lynx -accept_all_cookies -dump -width=10000 " + reference).read()
    #handle redurection of link
    ind = webpage.rfind("References")
    reference = webpage[ind:]
    ind = reference.find("1.")
    reference = reference[ind + 3:]
    ind = reference.find('\n')
    reference = reference[:ind]
    print("information was found on " + reference)
    print("the summary is:")
    webpage = os.popen("lynx -accept_all_cookies -dump -width=10000 " + reference).read()
    #handle the paper
    ind = webpage.rfind("References")
    webpage = webpage[:ind]
    ind = webpage.find("References")
    if ind != -1:
        webpage = webpage[:ind]
    ind = webpage.find("CONCLUSIONS")

    if ind != -1:
        webpage = webpage[ind:]
        ind = content.find('\n')
        content = content[ind + 1:]
        ind = content.find('\n')
        content = content[:ind  ]
        print(content)
    else:
        sent_text = nltk.sent_tokenize(webpage)
        wordCount = 0
        for s in sent_text:
            wordCount = wordCount + len(s)
            graph.addNode(s)
        graph.calcScore()
        wordCount = wordCount / 4
        if wordCount > 500:
            wordCount = 500
        graph.printMax(wordCount)


import gspread_dataframe as gd
import gspread as gs
import pandas as pd
from tkinter import messagebox
from tkinter.filedialog import askopenfilename, askdirectory
gc = gs.service_account(filename="Sale Database.json")

messagebox.showinfo(title='Open...', message="Open file to upload")
dfpath = askopenfilename()
try:
    df = pd.read_csv(dfpath)
except:
    df = pd.read_excel(dfpath)
def export_to_sheets(worksheet_name,df,mode='r'):
    ws = gc.open("Database").worksheet("Data")
    if(mode=='w'):
        ws.clear()
        gd.set_with_dataframe(worksheet=ws,dataframe=df,include_index=False,include_column_header=True,resize=True)
        return True
    elif(mode=='a'):
        ws.add_rows(df.shape[0])
        gd.set_with_dataframe(worksheet=ws,dataframe=df,include_index=False,include_column_header=False,row=ws.row_count+1,resize=False)
        return True
    else:
        return gd.get_as_dataframe(worksheet=ws)
colsneeded = ['control_number', 'first name', 'last name', 'number', 'grade', 'sport', 'school', 'team', 'parent_first_name', 'parent_last_name', 'parent_phone_number', 'parent_email', 'eight_by_ten', 'team_photo', 'fifty_package', 'banner', 'blanket', 'frame', 'payment_type', 'payment_amount', 'notes', 'date', 'full name', 'resize-first name', 'resize-last name', 'resize-full name', 'rename', 'left_number', 'right_number']
currentcols = list(df)
missingcols =(list(list(set(colsneeded)-set(currentcols)) + list(set(currentcols)-set(colsneeded))))
df=pd.concat([df,pd.DataFrame(columns=missingcols)], sort = True)
df = df[colsneeded]

export_to_sheets("Database",df,'a')
messagebox.showinfo(title='Success!', message="Your file has been uploaded")