#!/usr/bin/env python
#
#  KeepNote - note-taking and organization
#  Copyright (c) 2008-2009 Matt Rasmussen
#  Author: Matt Rasmussen <rasmus@mit.edu>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
#

# python imports
import exceptions
import sys
import os
from os.path import basename, dirname, realpath, join, isdir
import time
import optparse
import thread
import traceback



#=============================================================================
# KeepNote import

"""
Three ways to run KeepNote

bin_path = os.path.dirname(sys.argv[0])

(1) directly from source dir
    
    pkgdir = bin_path + "../keepnote"
    basedir = pkgdir
    sys.path.append(pkgdir)

    src/bin/keepnote
    src/keepnote/__init__.py
    src/keepnote/images
    src/keepnote/rc

(2) from installation location by setup.py 

    pkgdir = keepnote.get_basedir()
    basedir = pkgdir

    prefix/bin/keepnote
    prefix/lib/python-XXX/site-packages/keepnote/__init__.py
    prefix/lib/python-XXX/site-packages/keepnote/images
    prefix/lib/python-XXX/site-packages/keepnote/rc
    

(3) windows py2exe dir

    pkgdir = bin_path
    basedir = bin_path

    dir/keepnote.exe
    dir/library.zip
    dir/images
    dir/rc

"""


# try to infer keepnote lib path from program path
pkgdir = dirname(dirname(realpath(sys.argv[0])))
if os.path.exists(os.path.join(pkgdir, "keepnote", "__init__.py")):
    sys.path.append(pkgdir)
    import keepnote

    # if this works we know we are running from src_path (1)
    basedir = keepnote.get_basedir()

else:
    # try to import from python path
    import keepnote
    
    # sucessful import, therefore we are running with (2) or (3)
    
    # attempt to use basedir for (2)
    basedir = keepnote.get_basedir()
    
    if not isdir(join(basedir, "images")):
        # we must be running (3)
        basedir = dirname(realpath(sys.argv[0]))
    


# keepnote imports
import keepnote
from keepnote.commands import get_command_executor, CommandExecutor
from keepnote.teefile import TeeFileStream
import keepnote.compat.pref
_ = keepnote.translate



#=============================================================================
# options

o = optparse.OptionParser(usage=
                "%prog [options] [NOTEBOOK]")
o.set_defaults(default_notebook=True)
o.add_option("-c", "--cmd", dest="cmd",
             action="store_true",
             help="treat remaining arguments as a command")
o.add_option("-l", "--list-cmd", dest="list_cmd",
             action="store_true",
             help="list available commands")
o.add_option("--no-gui", dest="no_gui",
             action="store_true",
             help="run in non-gui mode")
o.add_option("", "--show-errors", dest="show_errors",
             action="store_true",
             help="show errors on console")
o.add_option("--no-show-errors", dest="show_errors",
             action="store_false",
             help="do not show errors on console")
o.add_option("--no-default", dest="default_notebook",
             action="store_false",
             help="do not open default notebook")
o.add_option("", "--newproc", dest="newproc",
             action="store_true",
             help="start KeepNote in a new process")
o.add_option("-p", "--port", dest="port",
             default=None,
             type="int",
             help="use a specified port for listening to commands")


#=============================================================================

def start_error_log(show_errors):
    """Starts KeepNote error log"""

    keepnote.init_error_log()

    errorlog = open(keepnote.get_user_error_log(), "a")
    if show_errors:
        sys.stderr = TeeFileStream([sys.stderr, errorlog],
                                   autoflush=True)
    else:
        sys.stderr = errorlog
    

    sys.stderr.write("==============================================\n"
                     "%s %s: %s\n" % (keepnote.PROGRAM_NAME,
                                      keepnote.PROGRAM_VERSION_TEXT,
                                      time.asctime()))


def parse_argv(argv):
    """Parse arguments"""

    # set default arguments
    options = o.get_default_values()    
    if keepnote.get_platform() == "windows":        
        options.show_errors = False
    else:
        options.show_errors = True

    # parse args and process
    (options, args) = o.parse_args(argv[1:], options)
    return options, args


def setup_threading():
    """Initialize threading environment"""

    import gtk.gdk
    import gobject

    if keepnote.get_platform() == "windows":
        # HACK: keep gui thread active
        def sleeper():
            time.sleep(.001)
            return True # repeat timer
        gobject.timeout_add(400, sleeper)

    else:
        gtk.gdk.threads_init()


def gui_exec(function, *args, **kw):
    """Execute a function in the GUI thread"""
    
    import gtk.gdk
    import gobject

    def idle_func():
        gtk.gdk.threads_enter()
        try:
            function(*args, **kw)
            return False
        finally:
            gtk.gdk.threads_leave()
    gobject.idle_add(idle_func)


def start_gui(argv, options, args, cmd_exec):
    import keepnote.gui

    # pygtk imports
    import pygtk
    pygtk.require('2.0')
    import gtk

    # setup threading environment
    setup_threading()

    # start error log
    start_error_log(options.show_errors)

    # read preferences
    app = keepnote.gui.KeepNote(basedir)
    app.init()
    cmd_exec.set_app(app)
    execute_command(app, argv)
    
    gtk.main()


def start_non_gui(argv, options, args, cmd_exec):

    # start error log
    start_error_log(options.show_errors)

    # read preferences
    app = keepnote.KeepNote(basedir)
    app.init()
    cmd_exec.set_app(app)

    execute_command(app, argv)


def execute_command(app, argv):
    """Execute commands given on command line"""
    
    options, args = parse_argv(argv)

    # parse builtin commands
    if options.list_cmd:
        list_commands(app)
        sys.exit()
    

    if options.cmd:
        # process application command (AppCommand)
        if len(args) == 0:
            raise Exception(_("Expected command"))

        command = app.get_command(args[0])
        if command:
            command.func(app, args)
        else:
            raise Exception(_("Unknown command '%s'") % args[0])
            
    else:
        # process a non-command

        if options.no_gui:
            sys.exit()

        if len(args) > 0:
            if keepnote.extension.is_extension_install_file(args[0]):
                # install extension
                if len(app.get_windows()) == 0:
                    app.new_window()

                app.install_extension(args[0])
            else:
                # open specified notebook
                win = app.new_window()
                win.open_notebook(args[0].rstrip("/"))

        else:
            # no arguments        

            if options.default_notebook and app.pref.default_notebook != "":
                # open default notebook
                win = app.new_window()
                win.open_notebook(app.pref.default_notebook)
            else:
                # open empty window
                app.new_window()


def list_commands(app):
    """List available commands"""
    
    commands = app.get_commands()
    commands.sort(key=lambda x: x.name)

    print
    print "available commands:"
    for command in commands:
        print " " + command.name,
        if command.metavar:
            print " " + command.metavar,            
        if command.help:
            print " -- " + command.help,
        print



def main(argv):
    """Main execution"""    

    options, args = parse_argv(argv)

    # check for old preference info
    keepnote.compat.pref.check_old_user_pref_dir()

    # init preference dir
    if not os.path.exists(keepnote.get_user_pref_dir()):
        keepnote.init_user_pref_dir()

    # get command executor
    if options.newproc:
        main_proc = True
        cmd_exec = CommandExecutor()
    else:
        main_proc, cmd_exec = get_command_executor(
            lambda app, argv: gui_exec(
                lambda: execute_command(app, argv)),
            port=options.port)
    

    if main_proc:
        # initiate main process
        if options.no_gui:
            start_non_gui(argv, options, args, cmd_exec)
        else:
            start_gui(argv, options, args, cmd_exec)
    else:
        # this is a command process, send command to main process
        cmd_exec.execute(argv)


try:
    main(sys.argv)
    
except exceptions.SystemExit, e:
    # sys.exit() was called
    pass

except Exception, e:
    traceback.print_exc()
    
    # flush final errors
    sys.stderr.flush()


