#!/usr/bin/env python

import os
import shutil
import subprocess
import urllib2
import tempfile
import zipfile
from distutils.dir_util import mkpath


DEFAULT_API_URL="https://csdms.colorado.edu/wmt/api-dev/"
DEFAULT_EXECUTION_SERVERS="beach.colorado.edu"
GWT_URL="http://google-web-toolkit.googlecode.com/files/gwt-2.5.1.zip"


class cd(object):
    def __init__(self, dir):
        self._dir = dir

    def __enter__(self):
        self._starting_dir = os.path.abspath(os.getcwd())
        if not os.path.isdir(self._dir):
            mkpath(self._dir)
        os.chdir(self._dir)
        return os.path.abspath(os.getcwd())

    def __exit__(self, type, value, traceback):
        os.chdir(self._starting_dir)


def check_output(*args, **kwds):
    kwds.setdefault('stdout', subprocess.PIPE)
    return subprocess.Popen(*args, **kwds).communicate()[0]


def system(*args, **kwds):
    verbose = kwds.pop('verbose', True)

    status(' '.join(args[0]))

    if verbose:
        call = subprocess.check_call
    else:
        call = check_output

    try:
        call(*args, **kwds)
    except subprocess.CalledProcessError:
        status('Error')
        raise


def which(prog, env=None):
    prog = os.environ.get(env or prog.upper(), prog)

    try:
        prog = check_output(['which', prog],
                            stderr=open('/dev/null', 'w')).strip()
    except subprocess.CalledProcessError:
        return None
    else:
        return prog


def git_clone(url, git=None, dest='.'):
    git = git or which('git')

    with cd(dest):
        system([git, 'init', '-q'])
        system([git, 'config', 'remote.origin.url', url])
        system([git, 'config', 'remote.origin.fetch',
                '+refs/head/*:refs/remotes/origin/*'])
        system([git, 'fetch', 'origin',
                'master:refs/remotes/origin/master', '-n', '--depth=1'])
        system([git, 'reset', '--hard', 'origin/master'])


def git_pull(url, git=None, dest='.'):
    git = git or which('git')

    with cd(dest):
        system([git, 'checkout', '-q', 'master'])
        system([git, 'pull', 'origin', '-q',
                'refs/heads/master:refs/remotes/origin/master'])


def git_clone_or_update(url, dest='.'):
    if os.path.isdir(os.path.join(dest, '.git')):
        status('Updating %s' % url)
        git_pull(url, dest=dest)
    else:
        status('Cloning %s' % url)
        git_clone(url, dest=dest)


def checksum_matches(path, md5):
    import hashlib

    if md5 is None:
        return False

    hasher = hashlib.md5()
    with open(path, 'r') as contents:
        hasher.update(contents.read())

    return hasher.hexdigest() == md5


def status(message):
    print ' '.join(['==>', message])


def download_url(url, dest, md5=None, cache='.'):
    dest = os.path.abspath(os.path.join(cache, dest))

    if os.path.exists(dest):
        if checksum_matches(dest, md5):
            status('md5 %s' % url)
            return dest
        else:
            os.remove(dest)

    status('Fetching %s' % url)

    try:
        response = urllib2.urlopen(url)
    except urllib2.HTTPError as error:
        raise
    except urllib2.URLError as error:
        raise
    else:
        with open(dest, 'w') as destination:
            shutil.copyfileobj(response, destination)

    return os.path.abspath(dest)


def download_gwt():
    dest = os.path.basename(GWT_URL)
    md5 = '6ada64bdd849abd0d954c44d42187340'
    return download_url(GWT_URL, dest, md5=md5)


def install_gwt(prefix='.'):
    gwt_file = os.path.basename(GWT_URL)
    gwt_version = os.path.splitext(gwt_file)[0]
    with zipfile.ZipFile(gwt_file, 'r') as zf:
        zf.extractall(prefix)

    os.environ['GWT_HOME'] = os.path.abspath(os.path.join(prefix, gwt_version))

    os.environ['PATH'] = os.pathsep.join([
        os.environ['GWT_HOME'],
        os.environ['PATH']])

    status('GWT installed to %s' % os.environ['GWT_HOME'])


def fetch_wmt_client(url=None):
    git_clone_or_update(url or 'https://github.com/csdms/wmt-client')


def build_wmt_client(dest='.'):
    dest = os.path.abspath(dest)
    with cd(os.path.join(dest, 'wmt-client')):
        fetch_wmt_client()
        system([which('ant'), 'build'])
        system([which('ant'), 'buildclean'])


def update_config_file(api_url, execution_servers, dest='.'):
    import json
    import posixpath

    dest = os.path.abspath(dest)
    config_file = 'config.json'
    with cd(os.path.join(dest, 'wmt-client', 'war')):

        with open(config_file, 'r') as ifp:
            config = json.load(ifp)

        config['api_url'] = posixpath.join(api_url, '')  # needs trailing /
        config['execution_servers'] = execution_servers.split()

        with open(config_file, 'w') as ofp:
            json.dump(config, ofp, indent=4)


def install_wmt_client(prefix):
    src = os.path.join('wmt-client', 'war')
    dest = os.path.join(os.path.abspath(prefix), 'wmt')
    try:
        shutil.copytree(src, dest)
    except EnvironmentError as err:
        status('Installing wmt-client failed with %s' % err)


if __name__ == '__main__':
    import argparse

    parser = argparse.ArgumentParser()
    parser.add_argument('prefix', help='install prefix for wmt-client')

    parser.add_argument('--api-url',
                        default=DEFAULT_API_URL,
                        type=str,
                        help='URL for WMT API server')
    parser.add_argument('--build-directory',
                        default=tempfile.mkdtemp(prefix='wmt_', suffix='.d'),
                        type=str,
                        help='specify directory for building wmt-client')
    parser.add_argument('--execution-servers',
                        default=DEFAULT_EXECUTION_SERVERS,
                        type=str,
                        help='space-delimited hostnames of WMT EXE servers')
    parser.add_argument('--keep-build-directory',
                        default=False,
                        action='store_true',
                        help='don\'t delete build directory when finished')

    args = parser.parse_args()

    status('Build directory is %s' % os.path.abspath(args.build_directory))

    with cd(args.build_directory) as _:
        download_gwt()
        install_gwt()
        build_wmt_client()
        update_config_file(args.api_url, args.execution_servers)
        install_wmt_client(args.prefix)

    if args.keep_build_directory is False:
        shutil.rmtree(args.build_directory)
