#! /usr/bin/env python
# WAF build script for Postler (based on Midori's wscript)
#
# This script is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# See the file COPYING for the full license text.

import sys

# Waf version check, for global waf installs
try:
    from Constants import WAFVERSION
except:
    WAFVERSION='1.0.0'
if WAFVERSION[:3] != '1.5':
    print 'Incompatible Waf, use 1.5'
    sys.exit (1)

import Build
import Options
import Utils
import pproc as subprocess
import os
try:
    import UnitTest
except:
    import unittestw as UnitTest
import Task
from TaskGen import extension, feature, taskgen
import misc

major = 0
minor = 1
micro = 1

APPNAME = 'postler'
VERSION = str (major) + '.' + str (minor) + '.' + str (micro)

try:
    git = Utils.cmd_output (['git', 'describe'], silent=True)
    if git:
        VERSION = git.strip ()
except:
    pass

srcdir = '.'
blddir = '_build_'

def option_enabled (option):
    if getattr (Options.options, 'enable_' + option):
        return True
    if getattr (Options.options, 'disable_' + option):
        return False
    return True

def configure (conf):
    def option_checkfatal (option, desc):
        if hasattr (Options.options, 'enable_' + option):
            if getattr (Options.options, 'enable_' + option):
                Utils.pprint ('RED', desc + ' N/A')
                sys.exit (1)

    def dirname_default (dirname, default, defname=None):
        if getattr (Options.options, dirname) == '':
            dirvalue = default
        else:
            dirvalue = getattr (Options.options, dirname)
        if not defname:
            defname = dirname
        conf.define (defname, dirvalue)
        return dirvalue

    conf.check_tool ('compiler_cc')
    conf.check_tool ('vala')
    if conf.env['VALAC_VERSION'][1] < 10:
         Utils.pprint ('RED', 'valac >= 0.10.0 required')
         sys.exit (1)

    if option_enabled ('nls'):
        conf.check_tool ('intltool')
        if conf.env['INTLTOOL'] and conf.env['POCOM']:
            nls = 'yes'
            conf.env.append_value ('VALAFLAGS', '-D HAVE_NLS')
        else:
            option_checkfatal ('nls', 'localization')
            nls = 'N/A'
    else:
        nls = 'no '

    def check_pkg (name, version='', mandatory=True, var=None, args=''):
        if not var:
            var = name.split ('-')[0].upper ()
        conf.check_cfg (package=name, uselib_store=var, args='--cflags --libs ' + args,
            atleast_version=version, mandatory=mandatory)
        return conf.env['HAVE_' + var]

    def check_function (function, header, lib=None, var=None, mandatory=True):
        if not var:
            var = 'HAVE_' + function.upper ()
        if lib:
           conf.check (function_name=function, header_name=header, \
               lib=lib, mandatory=mandatory, define_name=var)
        else:
            conf.check (function_name=function, header_name=header, \
                mandatory=mandatory, define_name=var)

    check_pkg ('gio-2.0', '2.26.0')
    check_pkg ('unique-1.0', '0.9')
    check_pkg ('gtk+-2.0', '2.18.0', var='GTK')
    check_pkg ('webkit-1.0', '1.1.18')
    check_pkg ('libnotify', var='LIBNOTIFY')
    check_pkg ('libcanberra', var='LIBCANBERRA')

    if option_enabled ('libindicate'):
        check_pkg ('indicate-0.5', mandatory=False)
        if not conf.env['HAVE_INDICATE']:
            check_pkg ('indicate', mandatory=False)
        if conf.env['HAVE_INDICATE']:
            conf.env.append_value ('VALAFLAGS', '-D HAVE_INDICATE')
        else:
            Utils.pprint ('RED', 'libindicate is needed for Postler to show ' \
                'up in the Messaging Menu (Ayatana).\n' \
                'If you want to build without it, pass --disable-libindicate.')
            sys.exit (1)
    else:
        Utils.pprint ('YELLOW', 'Building without libindicate.')

    # isync
    conf.check (header_name='sys/filio.h')
    conf.check (fragment='#define _GNU_SOURCE\n#include <stdio.h>\n' \
        'int main(char** argv, int argc)\n' \
        '{ char* a;\nvasprintf(&a, "%s", ""); return 0; }', \
        define_name='HAVE_VASPRINTF', msg='Checking for function vasprintf', \
        mandatory=True)
    check_function ('socket', 'sys/socket.h')
    if conf.env['DEST_OS'] == 'freebsd':
        check_function ('inet_ntoa', 'arpa/inet.h')
        check_function ('dlopen', 'dlfcn.h')
        conf.check(function_name='db_create', header_name='db.h',
                   includes=['/usr/local/include/db42'],
                   libpath=['/usr/local/lib/db42'], lib='db', mandatory=True)
    else:
        check_function ('inet_ntoa', 'arpa/inet.h', 'nsl')
        check_function ('dlopen', 'dlfcn.h', 'dl')
        check_function ('db_create', 'db.h', 'db')
    check_pkg ('openssl')
    check_function ('CRYPTO_lock', 'openssl/crypto.h', 'crypto')
    check_function ('SSL_connect', 'openssl/ssl.h', 'ssl', 'HAVE_LIBSSL')
    conf.find_program ('msmtp', mandatory=True)
    if not conf.find_program ('dexter'):
        Utils.pprint ('YELLOW', 'Dexter is not installed. It is recommended ' \
                                'because it provides contact completion \n' \
                                'and allows adding of contacts from Postler.')
    if not conf.find_program ('lynx'):
        Utils.pprint ('YELLOW', 'Lynx is not installed. It is recommended ' \
                                'because it enables HTML to text conversion.')

    conf.define ('PACKAGE', APPNAME)
    conf.define ('VERSION', VERSION)
    conf.define ('POSTLER_CHANGES', 1)

    # Store options in env, since 'Options' is not persistent
    if 'CC' in os.environ: conf.env['CC'] = os.environ['CC'].split()
    conf.env['docs'] = option_enabled ('docs')
    if 'LINGUAS' in os.environ: conf.env['LINGUAS'] = os.environ['LINGUAS']

    dirname_default ('LIBDIR', os.path.join (conf.env['PREFIX'], 'lib'))
    if conf.env['PREFIX'] == '/usr':
        dirname_default ('SYSCONFDIR', '/etc')
    else:
        dirname_default ('SYSCONFDIR', os.path.join (conf.env['PREFIX'], 'etc'))
    dirname_default ('DATADIR', os.path.join (conf.env['PREFIX'], 'share'),
    # Use MDATADIR because DATADIR is a constant in objidl.h on Windows
        'MDATADIR')
    conf.undefine ('DATADIR')
    dirname_default ('DOCDIR', os.path.join (conf.env['MDATADIR'], 'doc'))

    conf.define ('PACKAGE_VERSION', VERSION)
    conf.define ('PACKAGE_NAME', APPNAME)
    conf.define ('PACKAGE_BUGREPORT', 'https://bugs.launchpad.net/postler')
    conf.define ('GETTEXT_PACKAGE', APPNAME)

    conf.write_config_header ('config.h')
    conf.env.append_value ('CCFLAGS', '-DHAVE_CONFIG_H -include config.h'.split ())
    debug_level = Options.options.debug_level
    compiler = conf.env['CC_NAME']
    if debug_level != '' and compiler != 'gcc':
        Utils.pprint ('RED', 'No debugging level support for ' + compiler)
        sys.exit (1)
    elif debug_level == '':
        debug_level = 'debug'
    if compiler == 'gcc':
        if debug_level == 'none':
            if 'CCFLAGS' in os.environ:
                conf.env.append_value ('CCFLAGS', os.environ['CCFLAGS'].split ())
            else:
                conf.env.append_value ('CCFLAGS',
                    '-DG_DISABLE_CHECKS -DG_DISABLE_CAST_CHECKS '
                    '-DG_DISABLE_ASSERT'.split ())
        elif debug_level == 'debug':
            conf.env.append_value ('CCFLAGS', '-w -O0 -g'.split ())
        elif debug_level == 'full':
            conf.env.append_value ('CCFLAGS', '-w -O1 -g -DG_ENABLE_DEBUG'.split ())
    conf.env.append_value ('VALAFLAGS', '--thread')
    if debug_level == 'full':
        conf.env.append_value ('VALAFLAGS', '--debug --enable-checking'.split ())
    elif debug_level == 'debug':
        conf.env.append_value ('VALAFLAGS', '--debug')
    elif debug_level == 'none':
        conf.env.append_value ('VALAFLAGS', '--disable-assert')
    conf.env.append_value ('VALAFLAGS', '--enable-deprecated')

    print '''
        Localization:        %(nls)s (intltool)
        ''' % locals ()
    if conf.check_cfg (modversion='unique-1.0') == '1.0.4':
        Utils.pprint ('RED', 'unique 1.0.4 found, this version is erroneous.')
        Utils.pprint ('RED', 'Please use an older or newer version.')

def set_options (opt):
    def add_enable_option (option, desc, group=None, disable=False):
        if group == None:
            group = opt
        option_ = option.replace ('-', '_')
        group.add_option ('--enable-' + option, action='store_true',
            default=False, help='Enable ' + desc, dest='enable_' + option_)
        group.add_option ('--disable-' + option, action='store_true',
            default=disable, help='Disable ' + desc, dest='disable_' + option_)

    opt.tool_options ('compiler_cc')
    opt.get_option_group ('--check-c-compiler').add_option('-d', '--debug-level',
        action = 'store', default = '',
        help = 'Specify the debugging level. [\'none\', \'debug\', \'full\']',
        choices = ['', 'none', 'debug', 'full'], dest = 'debug_level')
    opt.tool_options ('gnu_dirs')
    opt.parser.remove_option ('--oldincludedir')
    opt.parser.remove_option ('--htmldir')
    opt.parser.remove_option ('--dvidir')
    opt.parser.remove_option ('--pdfdir')
    opt.parser.remove_option ('--psdir')
    opt.tool_options ('intltool')
    opt.add_option ('--run', action='store_true', default=False,
        help='Run application after building it', dest='run')

    group = opt.add_option_group ('Localization and documentation', '')
    add_enable_option ('nls', 'native language support', group)
    group.add_option ('--update-po', action='store_true', default=False,
        help='Update localization files', dest='update_po')
    add_enable_option ('docs', 'informational text files', group)

    add_enable_option ('libindicate', 'Messaging Menu support (Ayatana)')

# Taken from Geany's wscript, modified to support LINGUAS variable
def write_linguas_file (self):
    linguas = ''
    # Depending on Waf version, getcwd() is different
    if os.path.exists ('./po'):
        podir = './po'
    else:
        podir = '../po'
    if 'LINGUAS' in Build.bld.env:
        files = Build.bld.env['LINGUAS']
        for f in files.split (' '):
            if os.path.exists (podir + '/' + f + '.po'):
                linguas += f + ' '
    else:
        files = os.listdir (podir)
        for f in files:
            if f.endswith ('.po'):
                linguas += '%s ' % f[:-3]
    f = open (podir + '/LINGUAS', 'w')
    f.write ('# This file is autogenerated. Do not edit.\n%s\n' % linguas)
    f.close ()
write_linguas_file = feature ('intltool_po')(write_linguas_file)

def build (bld):
    bld.add_subdirs ('postler isync')

    bld.add_group ()

    if bld.env['docs']:
        bld.install_files ('${DOCDIR}/' + APPNAME + '/', \
            'COPYING README')

    if bld.env['INTLTOOL']:
        obj = bld.new_task_gen ('intltool_po')
        obj.podir = 'po'
        obj.appname = APPNAME

    appdir = '${MDATADIR}/applications'
    if bld.env['INTLTOOL']:
        obj = bld.new_task_gen ('intltool_in')
        obj.source = 'data/' + APPNAME + '.desktop.in'
        obj.install_path = appdir
        obj.flags  = ['-d', '-c']
        bld.install_files (appdir, 'data/' + APPNAME + '.desktop')
    else:
        folder = os.path.abspath (blddir + '/default/data')
        Utils.check_dir (folder)
        desktop = APPNAME + '.desktop'
        pre = open ('data/' + desktop + '.in')
        after = open (folder + '/' + desktop, 'w')
        try:
            try:
                for line in pre:
                    if line != '':
                        if line[0] == '_':
                            after.write (line[1:])
                        else:
                            after.write (line)
                after.close ()
                Utils.pprint ('BLUE', desktop + '.in -> ' + desktop)
                bld.install_files (appdir, folder + '/' + desktop)
            except:
                Utils.pprint ('BLUE', 'File ' + desktop + ' not generated')
        finally:
            pre.close ()
    bld.install_files ('${MDATADIR}/icons/hicolor/scalable/apps',
                       srcdir + '/data/internet-mail.svg')

    if Options.commands['check']:
        bld.add_subdirs ('tests')

def check (ctx):
    # The real work happens in shutdown ()
    pass

def distclean ():
    if os.path.exists ('po/LINGUAS'):
        os.remove ('po/LINGUAS')
    if os.path.exists ('po/' + APPNAME + '.pot'):
        os.remove ('po/' + APPNAME + '.pot')

def shutdown ():
    if Options.commands['install'] or Options.commands['uninstall']:
        dir = Build.bld.get_install_path ('${MDATADIR}/icons/hicolor')
        icon_cache_updated = False
        if not Options.options.destdir:
            # update the pixmap cache directory
            try:
                command = 'gtk-update-icon-cache -q -f -t %s' % dir
                if not Utils.exec_command (command):
                    Utils.pprint ('YELLOW', "Updated Gtk icon cache.")
                    icon_cache_updated = True
            except:
                Utils.pprint ('RED', "Failed to update icon cache.")
        if not icon_cache_updated:
            Utils.pprint ('YELLOW', "Icon cache not updated. "
                                     "After install, run this:")
            Utils.pprint ('YELLOW', "gtk-update-icon-cache -q -f -t %s" % dir)

    elif Options.commands['check']:
        test = UnitTest.unit_test ()
        test.change_to_testfile_dir = True
        test.want_to_see_test_output = True
        test.want_to_see_test_error = True
        test.run ()
        test.print_results ()

    elif Options.options.update_po:
        os.chdir('./po')
        try:
            try:
                size_old = os.stat (APPNAME + '.pot').st_size
            except:
                size_old = 0
            subprocess.call (['intltool-update', '-p', '-g', APPNAME])
            size_new = os.stat (APPNAME + '.pot').st_size
            if size_new <> size_old:
                Utils.pprint ('YELLOW', "Updated po template.")
                try:
                    command = 'intltool-update -r -g %s' % APPNAME
                    Utils.exec_command (command)
                    Utils.pprint ('YELLOW', "Updated translations.")
                except:
                    Utils.pprint ('RED', "Failed to update translations.")
        except:
            Utils.pprint ('RED', "Failed to generate po template.")
            Utils.pprint ('RED', "Make sure intltool is installed.")
        os.chdir ('..')
    elif Options.options.run:
        folder = os.path.abspath (blddir + '/default')
        try:
            relfolder = folder
            relfolder = os.path.relpath (folder)
        except:
            pass
        try:
            mbsync = 'POSTLER_MBSYNC=' + relfolder +'/isync/postler-mbsync '
            command = mbsync + relfolder + os.sep + APPNAME + os.sep + APPNAME
            print command
            Utils.exec_command (command)
        except Exception, msg:
            Utils.pprint ('RED', "Failed to run application: " + str (msg))

