#!/usr/bin/env python3
from __future__ import print_function

import os
import sys
import py_compile
from zipfile import ZipFile, ZIP_DEFLATED
from io import BytesIO
from subprocess import check_call
import base64

def mk_contents(data):
    #return '[' + ','.join(str(ord(i)) for i in data) + ']'
    return (
        '(function(){'
        'var t=atob("%s"),a=new Uint8Array(%s),i;'
        'for(i=0;i<%s;i++)a[i]=t.charCodeAt(i);'
        'return a'
        '})()'
    ) % (base64.b64encode(data).decode('ascii'), len(data), len(data))

def files_to_datafilecalls(fpaths):
    basedir = '/usr/local/lib/python3.5'
    commands = []
    dpaths = set([basedir])
    for fpath, targetdir in fpaths:

        dpath = os.path.abspath(os.path.join(basedir, targetdir))

        commands.append('FS.createDataFile("%s", "%s", %s, true, true)' % (
            dpath, os.path.basename(fpath), mk_contents(open(fpath, 'rb').read())
        ))

        # Make sure we're adding all required directories
        while dpath not in dpaths:
            dpaths.add(dpath)
            dpath = os.path.dirname(dpath)

    dpaths.remove(basedir)
    for dpath in sorted(dpaths, key=len, reverse=True):
        commands.insert(0, 'FS.mkdirTree("%s")' % (dpath,))
    commands.insert(0, 'FS.createPath("/", "' + basedir[1:] + '", true, true)')

    return commands

def files_to_datafilezipcall(fpaths):
    zf = BytesIO()
    zipfile = ZipFile(zf, 'w', ZIP_DEFLATED)
    for fpath, targetdir in fpaths:
        zipfile.write(fpath, os.path.join(targetdir, os.path.basename(fpath)))
    zipfile.close()

    target = '/usr/local/lib/python35.zip'
    commands = []
    commands.insert(0, 'FS.createPath("/", "' + os.path.dirname(target)[1:] + '", true, true)')
    commands.append('FS.createDataFile("%s", "%s", %s, true, true)' % (
        os.path.dirname(target), os.path.basename(target), mk_contents(zf.getvalue())
    ))
    return commands

def main(root):
    os.chdir(root)
    fpaths = []
    for (dirpath, dirnames, filenames) in os.walk('.'):
        for dirname in dirnames[:]:
            should_remove = any([
                dirname in ['tests', 'test'], # python 3 tests will error!
                dirname == 'unittest', # gets crippled by the above
                dirname.startswith('plat-') and dirname != 'plat-linux2', # emscripten is ~linux
                dirname == 'lib2to3', # we don't package the necessary grammar files
                dirname in ['idlelib', 'lib-tk'], # Tk doesn't even compile yet
                dirname == 'ctypes', # we obviously can't call C functions
                dirname == 'distutils', # not going to be running pip any time soon
                dirname == 'bsddb', # needs compiling, deprecated anyway
                dirname == 'multiprocessing', # doesn't really make sense in JS
                dirname == 'curses', # we don't have the terminal interface (yet)
                dirname == 'sqlite3', # doesn't get compiled yet
                dirname == 'msilib', # doesn't get compiled and who cares anyway
                dirname == 'hotshot', # doesn't get compiled, unmaintained
                dirname == 'wsgiref', # not going to be building any web servers
                dirname == 'pydoc_data', # don't bundle documentation
            ])
            if should_remove:
                dirnames.remove(dirname)

        for filename in filenames:
            fpaths.append((os.path.join(dirpath, filename), dirpath))

    # _sysconfigdata is created by the build process
    fpaths.append(('../build/lib.linux-x86_64-3.5/_sysconfigdata.py', '.'))

    # Some checks and assertions
    assert all([targetdir[0] == '.' for fpath, targetdir in fpaths])
    fpaths = [
        (fpath, targetdir) for fpath, targetdir in fpaths
        if os.path.splitext(fpath)[1] == '.py'
    ]
    ## Compile to save space and time in the parser
    #check_call(['python3', '-OO', '-m', 'py_compile'] + [fpath for fpath, _ in fpaths])
    #fpaths = [(fpath + 'o', targetdir) for fpath, targetdir in fpaths]

    if sys.argv[2] == 'datafiles':
        commands = files_to_datafilecalls(fpaths)
    elif sys.argv[2] == 'datafilezip':
        commands = files_to_datafilezipcall(fpaths)
    else:
        assert False

    # Start out in a writeable folder.
    commands.append('FS.mkdirTree("/sandbox")')
    commands.append('FS.currentPath = "/sandbox"')

    # http://bugs.python.org/issue22689
    #commands = ['ENV["PYTHONHOME"] = "%s"' % (pyhomedir,)]

    print(';'.join([c for c in commands if c != '']) + ';', end='')

if __name__ == '__main__':
    if len(sys.argv) != 3 or sys.argv[2] not in ['datafiles', 'datafilezip']:
        print('Usage: %s root datafiles|datafilezip' % sys.argv[0], file=sys.stderr)
        sys.exit(1)
    else:
        main(sys.argv[1])