#!/usr/bin/env python

# NOTE: If this doesn't work, it may be related to a policy in
#   /etc/ImageMagick-6/policy.xml
# Specifically, disable
#   <policy domain="coder" rights="none" pattern="PS" />
# by removing it or commenting it out.

import json
import os
import psutil
import signal
import six
import subprocess
import sys
import time

OriginalSize = (1200, 900)
ExtraSpace = 1  # 1, otherwise we get a black border on the bottom and right
NavbarHeight = 60
FinalSize = (800, 600)
InitialDelay = 15  # in seconds
MaxDelay = 30  # in seconds
Quality = 90
OutputFile = 'thumb.jpg'
InputList = ["examples", "tutorials"]
BrowserCommand = [
    'xvfb-run', '-s', '-ac -screen 0 %dx%dx24' % (
        OriginalSize[0] + ExtraSpace, OriginalSize[1] + ExtraSpace + NavbarHeight),
    'google-chrome', '--kiosk', '--no-pings', '--device-scale-factor=1',
    '--incognito', '--start-fullscreen', '--no-default-browser-check',
    '--user-data-dir=/tmp/chrome_geojs_thumbnails', '--no-first-run',
    '--disable-default-apps', '--disable-popup-blocking',
    '--disable-translate', '--disable-background-timer-throttling',
    '--disable-renderer-backgrounding',
    '--disable-device-discovery-notifications',
    '--window-position=0,0',
]
BrowserCommandSize = [
    '--window-size=%d,%d' % (OriginalSize[0] + ExtraSpace, OriginalSize[1] + ExtraSpace),
]
BrowserCommandSizeIgnoreNavbar = [
    '--window-size=%d,%d' % (
        OriginalSize[0] + ExtraSpace, OriginalSize[1] + ExtraSpace + NavbarHeight),
]
BrowserUrl = 'http://127.0.0.1:30100/%s'
ImageCommand = (
    'DISPLAY=:99.0 import -window root -crop %dx%d+0+0 +repage - | '
    'convert - -resize %dx%d -quality %d ' % (
        OriginalSize[0], OriginalSize[1], FinalSize[0], FinalSize[1], Quality))
ImageCommandIgnoreNavbar = (
    'DISPLAY=:99.0 import -window root -crop %dx%d+0+%d +repage - | '
    'convert - -resize %dx%d -quality %d ' % (
        OriginalSize[0], OriginalSize[1], NavbarHeight, FinalSize[0], FinalSize[1], Quality))


def process_item(path, opts):
    output = (open('/tmp/thumbnail.out', 'ab')
              if opts.get('verbose', 0) >= 1 else open(os.devnull, 'w'))
    data = json.load(open(path))
    if data.get('disabled') and not opts.get('all'):
        return
    dest = os.path.join(os.path.dirname(path), OutputFile)
    if os.path.exists(dest) and not opts.get('force'):
        return
    originalSize = 0
    if os.path.exists(dest):
        originalSize = os.path.getsize(dest)
    sys.stdout.write('\r%s %d' % (path, originalSize))
    sys.stdout.flush()
    if opts.get('simulate'):
        dest = os.path.join('/tmp', os.path.basename(os.path.dirname(
            os.path.dirname(path))) + '_' + os.path.basename(os.path.dirname(
                path)) + '_' + OutputFile)
        if os.path.exists(dest):
            os.unlink(dest)
    cmd = list(BrowserCommand)
    imgcmd = ImageCommand
    if 'example.json' in path and not data.get('hideNavbar'):
        cmd.extend(BrowserCommandSizeIgnoreNavbar)
        imgcmd = ImageCommandIgnoreNavbar
    else:
        cmd.extend(BrowserCommandSize)
    url = BrowserUrl % os.path.dirname(path)
    if data.get('thumbquery'):
        url += '?' + data['thumbquery']
    cmd.append(url)
    output.write('--> %r\n' % (cmd, ))
    output.write('    %s\n' % (' '.join([six.moves.shlex_quote(arg) for arg in cmd])))
    proc = subprocess.Popen(cmd, shell=False, stdout=output, stderr=output)
    delay = opts.get('delay', InitialDelay)
    startTime = time.time()
    time.sleep(delay)
    lastSize = 0
    while True:
        output.write('--> %r\n' % (imgcmd + six.moves.shlex_quote(dest), ))
        subprocess.Popen(
            imgcmd + six.moves.shlex_quote(dest), shell=True,
            stdout=output, stderr=output).wait()
        newSize = os.path.getsize(dest)
        if newSize and newSize == lastSize:
            break
        if time.time() - startTime > opts.get('maxdelay', MaxDelay):
            break
        lastSize = newSize
        sys.stdout.write('\r%s %d %d ' % (path, originalSize, newSize))
        sys.stdout.flush()
        time.sleep(0.5)
    for child in psutil.Process(proc.pid).children(recursive=True):
        try:
            child.send_signal(signal.SIGINT)
        except psutil.NoSuchProcess:
            pass
    os.kill(proc.pid, signal.SIGINT)
    proc.wait()
    sys.stdout.write('\n')


if __name__ == '__main__':  # noqa
    opts = {'force': False, 'verbose': 0}
    for arg in sys.argv[1:]:
        if arg in ('-a', '--all'):
            opts['all'] = True
        elif arg.startswith('--delay='):
            opts['delay'] = float(arg.split('=', 1)[1])
        elif arg == '--force':
            opts['force'] = True
        elif arg.startswith('--maxdelay='):
            opts['maxdelay'] = float(arg.split('=', 1)[1])
        elif arg.startswith('--only='):
            opts['only'] = arg.split('=', 1)[1]
        elif arg in ('-s', '--simulate'):
            opts['simulate'] = True
        elif arg in ('-v', '--verbose'):
            opts['verbose'] += 1
        else:
            opts['help'] = True
    if opts.get('help'):
        print("""
Regenerate thumbnails for examples and tutorials.

Syntax: make_thumbnails.py --force --simulate --only=(substr) --all
                           --delay=(seconds) --maxdelay=(seconds)

Run in the root geojs directory.
--all or -a generates thumbnails for disabled examples, too.
--delay is the duration after the web browser is started before a thumbnail
 snapshot might be taken.  The thumbnail is only taken after the webpage hasn't
 changed for a short duration.
--force regenerates all thumbnails.  Otherwise, only missing thumbnails are
 created.
--maxdelay is the longest to wait before taking the snapshot.  This will happen
 even if the webpage is still changing.
--only will only process examples or tutorials whose name contains the
 specified substring.
--simulate or -s determines the size of thumbnails that would be created but
 doesn't make them.
""")
        sys.exit(0)
    for inputdir in InputList:
        for root, dirs, files in os.walk(inputdir):
            dirs.sort()
            for dir in dirs:
                for name in ['example.json', 'tutorial.json']:
                    path = os.path.join(root, dir, name)
                    if opts.get('only') and not opts['only'] in path:
                        continue
                    if os.path.exists(path):
                        process_item(path, opts)
