import IPython.ipapi
ip = IPython.ipapi.get()

import win32api
import win32ui
import win32console
import dde
import os
import scitedirector

# test to write.

def set_hook(synchronize_with_editor):
    """Set the synchronize with editor hook with a callable object.
    
    The callable object will be called with the following arguments when
    IPython wants to synchronize with you favorite editor:
    
     - ip: a running IPython instance.
     
     - filename: the path of the file the editor is supposed to display.
     
     - lineno : the line number of the line the editor is supposed to
     highlight.
     
     - columnno : the column number of the character the editor is supposed
     to highlight.
    """
    ip.set_hook("synchronize_with_editor", synchronize_with_editor)


def find_filename(filename):
    """Return the filename to synchronize with based on """
    filename = os.path.splitext(filename)
    if filename[1] == ".pyc":
        filename = (filename[0], ".py")
    filename = "".join(filename)

    if not os.path.isabs(filename):
        filename = os.path.join(os.getcwd(), filename)

    if os.path.isfile(filename):
        return filename

    return ""


def run_command(path, command, arguments, asynchronous = True):
    """Run a shell command and return the exit code of the command"""
    # This is a thin wrapper around os.system that:
    #  - Let you run command asynchronously.
    #  - Accept spaces in command path.
    #  - Dont throw exception if the command don't exist.
    line = ''
    if asynchronous:
        line += 'start '
        
    try:
        line += win32api.GetShortPathName(os.path.join(path, command) + ".exe") + " "
    except:
        print 'could not find: "%s"' % (os.path.join(path, command) + ".exe")
        return -1
        
    line += arguments
    r = os.system(line)
    return r


def sleep(milliseconds):
    """Wait some milliseconds."""
    # This is used to make sure the editor did its job before we reset the focus on the console.
    win32api.Sleep(milliseconds)


def restore_console_focus():
    """Restore the focus to the IPython console."""
    h = win32console.GetConsoleWindow()
    console_window = win32ui.CreateWindowFromHandle(h)
    console_window.SetForegroundWindow()
    

# This is the most simple example of hook:
class GVimHook:
    def __init__(self, path, wakeup_duration):
        self.path = path
        self.wakeup_duration = wakeup_duration

    def __call__(self, ip, filename, lineno, columnno):
        filename = find_filename(filename)

        if not filename:
            return

        run_command(self.path, 'gvim', '--remote-silent +%d "%s"' % (lineno, filename))

        sleep(self.wakeup_duration)

        restore_console_focus()


def gvim(path = r"C:\Program Files\vim\vim71", wakeup_duration = 100):
    synchronize_with_editor = GVimHook(path, wakeup_duration)
    set_hook(synchronize_with_editor)


class EmacsHook:
    def __init__(self, path, wakeup_duration, start_duration):
        self.path = path
        self.wakeup_duration = wakeup_duration
        self.start_duration = start_duration

    def __call__(self, ip, filename, lineno, columnno):
        filename = find_filename(filename)

        if not filename:
            return

        r = run_command(self.path, "emacsclient", '-n +%d:%d "%s" 2>nul' % (lineno, columnno, filename), False)
        if r != 0:
            run_command(self.path, 'runemacs', '--quick -f server-start +%d:%d "%s"' % (lineno, columnno, filename))
            sleep(self.start_duration)
        else:
            sleep(self.wakeup_duration)

        restore_console_focus()


def emacs(path = r"C:\Program Files\emacs\bin", wakeup_duration = 100, start_duration = 2000):
    synchronize_with_editor = EmacsHook(path, wakeup_duration, start_duration)
    set_hook(synchronize_with_editor)


class SciteHook:
    def __init__(self, path, wakeup_duration, start_duration):
        self.path = path
        self.wakeup_duration = wakeup_duration
        self.start_duration = start_duration

    def __call__(self, ip, filename, lineno, columnno):
        filename = find_filename(filename)

        if not filename:
            return

        scites = scitedirector.findWindows()
        if not scites:
            run_command(self.path, "scite", '"-open:%s" -goto:%d' % (filename.replace("\\", "/"), lineno))

            sleep(self.start_duration)
            restore_console_focus()
        else:
            scite = scites[0]
            scitedirector.sendCommand(scite, 'open:%s' % filename.replace("\\", "/"))
            scitedirector.sendCommand(scite, "goto:%d" % lineno)


def scite(path = r"C:\Program Files\SciTE Source Code Editor", wakeup_duration = 100, start_duration = 500):
    synchronize_with_editor = SciteHook(path, wakeup_duration, start_duration)
    set_hook(synchronize_with_editor)


class NodePadPlusPlusHook:
    def __init__(self, path, wakeup_duration):
        self.path = path
        self.wakeup_duration = wakeup_duration

    def __call__(self, ip, filename, lineno, columnno):
        filename = find_filename(filename)

        if not filename:
            return

        run_command(self.path, "notepad++", '"%s" -n%d' % (filename, lineno))

        sleep(self.wakeup_duration)

        restore_console_focus()


def notepadplusplus(path = r"C:\Program Files\Notepad++", wakeup_duration = 100):
    synchronize_with_editor = NodePadPlusPlusHook(path, wakeup_duration)
    set_hook(synchronize_with_editor)


class PsPadHook:
    def __init__(self, path, wakeup_duration):
        self.path = path
        self.wakeup_duration = wakeup_duration

    def __call__(self, ip, filename, lineno, columnno):
        filename = find_filename(filename)

        if not filename:
            return

        run_command(self.path, "pspad", '"%s" -%d' % (filename, lineno))

        sleep(self.wakeup_duration)

        restore_console_focus()


def pspad(path = r"C:\Program Files\PSPad editor", wakeup_duration = 100):
    synchronize_with_editor = PsPadHook(path, wakeup_duration)
    set_hook(synchronize_with_editor)


# This is an example of DDE hook:
class UltraEditHook:
    def __init__(self, path, wakeup_duration, start_duration):
        self.path = path
        self.wakeup_duration = wakeup_duration
        self.start_duration = start_duration

    def __call__(self, ip, filename, lineno, columnno):
        filename = find_filename(filename)

        if not filename:
            return

        server = dde.CreateServer()
        server.Create("myddeserver")
        conversation = dde.CreateConversation(server)
        try:
            conversation.ConnectTo("uedit32", "System")
            conversation.Exec(r'[open("%s/%d"])' % (filename, lineno))
            
            sleep(self.wakeup_duration)
        except:
            run_command(self.path, 'uedit32', '"%s/%d"' % (filename, lineno))
            
            sleep(self.start_duration)

        server.Shutdown()

        restore_console_focus()


def ultraedit(path = r"C:\Program Files\IDM Computer Solutions\UltraEdit-32", wakeup_duration = 10, start_duration = 2000):
    synchronize_with_editor = UltraEditHook(path, wakeup_duration, start_duration)
    set_hook(synchronize_with_editor)
    