#!/usr/local/bin/python2.7

# Copyright (C) 2002-2006 Stephen Kennedy <stevek@gnome.org>
# Copyright (C) 2009-2014 Kai Willadsen <kai.willadsen@gmail.com>
#
# 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, either version 2 of the License, or (at
# your option) any later version.
#
# 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, see <http://www.gnu.org/licenses/>.

from __future__ import print_function

import locale
import logging
import os
import signal
import subprocess
import sys

# On Windows, pythonw.exe (which doesn't display a console window) supplies
# dummy stdout and stderr streams that silently throw away any output. However,
# these streams seem to have issues with flush() so we just redirect stdout and
# stderr to actual dummy files (the equivalent of /dev/null).
# Regarding pythonw.exe stdout, see also http://bugs.python.org/issue706263
if sys.executable.endswith("pythonw.exe"):
    devnull = open(os.devnull, "w")
    sys.stdout = sys.stderr = devnull


def disable_stdout_buffering():

    class Unbuffered(object):

        def __init__(self, file):
            self.file = file

        def write(self, arg):
            self.file.write(arg)
            self.file.flush()

        def __getattr__(self, attr):
            return getattr(self.file, attr)

    sys.stdout = Unbuffered(sys.stdout)


def get_meld_dir():
    global frozen
    if frozen:
        return os.path.dirname(sys.executable)

    # Support running from an uninstalled version
    self_path = os.path.realpath(__file__)
    return os.path.abspath(os.path.join(os.path.dirname(self_path), ".."))

frozen = getattr(sys, 'frozen', False)
melddir = get_meld_dir()

uninstalled = False
if os.path.exists(os.path.join(melddir, "meld.doap")):
    sys.path[0:0] = [melddir]
    uninstalled = True
devel = os.path.exists(os.path.join(melddir, ".git"))

import meld.conf

# Silence warnings on non-devel releases (minor version is divisible by 2)
is_stable = not bool(int(meld.conf.__version__.split('.')[1]) % 2)
if is_stable:
    import warnings
    warnings.simplefilter("ignore")

if uninstalled:
    meld.conf.uninstalled()
elif frozen:
    meld.conf.frozen()

# TODO: Possibly move to elib.intl
import gettext
locale_domain = meld.conf.__package__
locale_dir = meld.conf.LOCALEDIR

gettext.bindtextdomain(locale_domain, locale_dir)
try:
    locale.setlocale(locale.LC_ALL, '')
except locale.Error as e:
    print("Couldn't set the locale: %s; falling back to 'C' locale" % e)
    locale.setlocale(locale.LC_ALL, 'C')
gettext.textdomain(locale_domain)
trans = gettext.translation(locale_domain, localedir=locale_dir, fallback=True)
try:
    _ = meld.conf._ = trans.ugettext
    meld.conf.ngettext = trans.ungettext
except AttributeError:
    # py3k
    _ = meld.conf._ = trans.gettext
    meld.conf.ngettext = trans.ngettext

try:
    if os.name == 'nt':
        from ctypes import cdll
        if frozen:
            libintl = cdll['libintl-8']
        else:
            libintl = cdll.intl
        libintl.bindtextdomain(locale_domain, locale_dir)
        libintl.bind_textdomain_codeset(locale_domain, 'UTF-8')
        del libintl
    else:
        locale.bindtextdomain(locale_domain, locale_dir)
        locale.bind_textdomain_codeset(locale_domain, 'UTF-8')
except AttributeError as e:
    # Python builds linked without libintl (i.e., OSX) don't have
    # bindtextdomain(), which causes Gtk.Builder translations to fail.
    print("Couldn't bind the translation domain. Some translations won't work.")
    print(e)
except locale.Error as e:
    print("Couldn't bind the translation domain. Some translations won't work.")
    print(e)
except WindowsError as e:
    # Accessing cdll.intl sometimes fails on Windows for unknown reasons.
    # Let's just continue, as translations are non-essential.
    print("Couldn't bind the translation domain. Some translations won't work.")
    print(e)


def check_requirements():

    pyver = (2, 7)
    gtk_requirement = (3, 14)
    glib_requirement = (2, 36, 0)
    gtksourceview_requirement = (3, 14, 0)

    def missing_reqs(mod, ver, exception=None):
        if isinstance(exception, ImportError):
            print(_("Cannot import: ") + mod + "\n" + str(e))
        else:
            modver = mod + " " + ".".join(map(str, ver))
            print(_("Meld requires %s or higher.") % modver)
        sys.exit(1)

    if sys.version_info[0] == 3:
        print(_("Meld does not support Python 3."))
        sys.exit(1)

    if sys.version_info[:2] < pyver:
        missing_reqs("Python", pyver)

    # gtk+ and related imports
    try:
        # FIXME: Extra clause for gi
        import gi
        gi.require_version("Gtk", "3.0")
        from gi.repository import Gtk
        version = (Gtk.get_major_version(), Gtk.get_minor_version())
        assert version >= gtk_requirement
    except (ImportError, AssertionError) as e:
        missing_reqs("GTK+", gtk_requirement, e)

    try:
        from gi.repository import GObject
        assert GObject.glib_version >= glib_requirement
    except (ImportError, AssertionError) as e:
        missing_reqs("GLib", glib_requirement, e)

    try:
        gi.require_version('GtkSource', '3.0')
        from gi.repository import GtkSource
        # TODO: There is no way to get at GtkSourceView's actual version
        assert hasattr(GtkSource, 'SearchSettings')
    except (ImportError, AssertionError) as e:
        missing_reqs("GtkSourceView", gtksourceview_requirement, e)


def setup_resources():
    from gi.repository import GObject
    from gi.repository import GLib
    from gi.repository import Gtk
    from gi.repository import Gdk
    from gi.repository import GtkSource

    if GObject.pygobject_version <= (3, 11):
        GObject.threads_init()
    icon_dir = os.path.join(meld.conf.DATADIR, "icons")
    Gtk.IconTheme.get_default().append_search_path(icon_dir)

    css_file = os.path.join(meld.conf.DATADIR, "meld.css")
    provider = Gtk.CssProvider()
    try:
        provider.load_from_path(css_file)
    except GLib.GError as err:
        print(_("Couldn't load Meld-specific CSS (%s)\n%s") % (css_file, err))
    Gtk.StyleContext.add_provider_for_screen(
        Gdk.Screen.get_default(), provider,
        Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)

    style_path = os.path.join(meld.conf.DATADIR, "styles")
    GtkSource.StyleSchemeManager.get_default().append_search_path(style_path)


def setup_settings():
    import meld.conf

    schema_path = os.path.join(meld.conf.DATADIR, "org.gnome.meld.gschema.xml")
    compiled_schema_path = os.path.join(meld.conf.DATADIR, "gschemas.compiled")

    try:
        schema_mtime = os.path.getmtime(schema_path)
        compiled_mtime = os.path.getmtime(compiled_schema_path)
        have_schema = schema_mtime < compiled_mtime
    except OSError:
        have_schema = False

    if uninstalled and not have_schema:
        subprocess.call(["glib-compile-schemas", meld.conf.DATADIR],
                        cwd=melddir)

    import meld.settings
    meld.settings.create_settings(uninstalled=uninstalled or frozen)


def setup_logging():
    log = logging.getLogger()

    # If we're running uninstalled and from Git, turn up the logging level
    if uninstalled and devel:
        log.setLevel(logging.INFO)
    else:
        log.setLevel(logging.CRITICAL)

    handler = logging.StreamHandler()
    formatter = logging.Formatter("%(asctime)s %(levelname)s "
                                  "%(name)s: %(message)s")
    handler.setFormatter(formatter)
    log.addHandler(handler)


def environment_hacks():
    # We manage cwd ourselves for git operations, and GIT_DIR in particular
    # can mess with this when set.
    for var in ('GIT_DIR', 'GIT_WORK_TREE'):
        try:
            del os.environ[var]
        except KeyError:
            pass


if __name__ == '__main__':
    setup_logging()
    disable_stdout_buffering()
    check_requirements()
    setup_resources()
    setup_settings()
    environment_hacks()

    import meld.meldapp
    if sys.platform != 'win32':
        from gi.repository import GLib
        GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT,
                             lambda *args: meld.meldapp.app.quit(), None)
    status = meld.meldapp.app.run(sys.argv)
    sys.exit(status)
