#! /usr/bin/env python2

# 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 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
    if os.path.basename(__file__) == "meld":
        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

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, 6)
    glib_requirement = (2, 34, 0)
    gtksourceview_requirement = (3, 6, 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
        from gi.repository import Gtk
        gi.require_version("Gtk", "3.0")
        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:
        from gi.repository import GtkSource
        # TODO: There is no way to get at GtkSourceView's actual version
    except (ImportError, AssertionError) as e:
        missing_reqs("GtkSourceView", gtksourceview_requirement, e)


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

    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()
    provider.load_from_path(css_file)
    Gtk.StyleContext.add_provider_for_screen(
        Gdk.Screen.get_default(), provider,
        Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)

    if Gtk.get_minor_version() >= 13:
        # Retrieve the selection colour from GtkTextView. Ideally, we'd do this
        # on MeldSourceView, but we don't really want to import that here.
        style = Gtk.StyleContext()
        widget_path = Gtk.WidgetPath()
        widget_path.append_type(Gtk.TextView)
        style.set_path(widget_path)
        # This is basically indefensible internal GTK+ ABI, but... whatever.
        style.add_class(Gtk.STYLE_CLASS_VIEW)
        color = style.get_background_color(Gtk.StateFlags.SELECTED).to_string()

        fixes_provider = Gtk.CssProvider()
        fixes_provider.load_from_data(
            "MeldSourceView { background-color: rgba(0.0, 0.0, 0.0, 0.0); } "
            "MeldSourceView:selected { background-color: %s; } " % color)
        Gtk.StyleContext.add_provider_for_screen(
            Gdk.Screen.get_default(), fixes_provider,
            Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)


def setup_settings():
    import meld.conf

    schema_path = os.path.join(meld.conf.DATADIR, "gschemas.compiled")
    if uninstalled and not os.path.exists(schema_path):
        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)


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

    import meld.meldapp
    status = meld.meldapp.app.run(sys.argv)
    sys.exit(status)
