"""
A script to generate the schemas for the GA4GH protocol. These are generated
from a copy of the Protocol Buffers schema and use it to generate
the Python class definitions. These are also stored in revision
control to aid Travis building.
"""
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

import os
import os.path
import subprocess
import fnmatch
import re
import argparse

import utils


class ProtobufGenerator(object):

    def __init__(self, version):
        self.version = version

    def _assertSchemasExist(self, schemas):
        if not os.path.exists(schemas):
            raise Exception(
                "Can't find schemas folder. " +
                "Thought it would be at {}".format(
                    os.path.realpath(schemas)))

    def _assertProtoDirectoryExists(self, src):
        if not os.path.exists(src):
            msg = "Can't find source proto directory {}".format(
                os.path.realpath(src))
            raise Exception(msg)

    def _find_in_path(self, cmd):
        PATH = os.environ.get("PATH", os.defpath).split(os.pathsep)
        for x in PATH:
            possible = os.path.join(x, cmd)
            if os.path.exists(possible):
                return possible
        return None

    # From http://stackoverflow.com/a/1714190/320546
    def _version_compare(self, version1, version2):
        def normalize(v):
            return [int(x) for x in re.sub(r'(\.0+)*$', '', v).split(".")]
        return cmp(normalize(version1), normalize(version2))

    def _getProtoc(self, server):
        protocs = [
            os.path.realpath(x) for x in
            "{}/protobuf/src/protoc".format(server),
            self._find_in_path("protoc")
            if x is not None]
        protoc = None
        for c in protocs:
            if not os.path.exists(c):
                continue
            output = subprocess.check_output([c, "--version"]).strip()
            try:
                (lib, version) = output.split(" ")
                if lib != "libprotoc":
                    raise Exception("lib didn't match 'libprotoc'")
                if self._version_compare("3.0.0", version) > 0:
                    raise Exception("version < 3.0.0")
                protoc = c
                break
            except Exception:
                utils.log(
                    "Not using {path} because it returned " +
                    "'{version}' rather than \"libprotoc <version>\", where " +
                    "<version> >= 3.0.0").format(path=c, format=output)

        if protoc is None:
            raise Exception("Can't find a good protoc. Tried {}".format(
                protocs))
        utils.log("Using protoc: '{}'".format(protoc))
        return protoc

    def _writePythonFiles(self, src, protoc, server):
        protos = []
        for root, dirs, files in os.walk(src):
            protos.extend([
                os.path.join(root, f)
                for f in fnmatch.filter(files, "*.proto")])
        if len(protos) == 0:
            raise Exception("Didn't find any proto files in ".format(src))
        cmd = "{protoc} -I {src} --python_out={server} {proto_files}".format(
            protoc=protoc, src=src,
            server=server, proto_files=" ".join(protos))
        utils.runCommand(cmd)

    def _writeVersionFile(self):
        with open("ga4gh_client/_protocol_version.py", "w") as version_file:
            version_file.write(
                "# File generated by scripts/process_schemas.py; "
                "do not edit\n")
            version_file.write("version = '{}'\n".format(self.version))

    def run(self, args):
        script_path = os.path.dirname(os.path.realpath(__file__))
        server = os.path.realpath(os.path.join(script_path, ".."))
        schemas = os.path.realpath(args.schemapath)
        self._assertSchemasExist(schemas)
        schemasSubdir = "src/main/proto"
        src = os.path.realpath(os.path.join(schemas, schemasSubdir))
        self._assertProtoDirectoryExists(src)
        protoc = self._getProtoc(server)
        self._writePythonFiles(src, protoc, server)
        self._writeVersionFile()


def main():
    parser = argparse.ArgumentParser(
        description="Script to process GA4GH Protocol buffer schemas")
    parser.add_argument(
        "version", help="Version number of the schema we're compiling")
    parser.add_argument(
        "schemapath",
        help="Path to schemas.")
    args = parser.parse_args()
    pb = ProtobufGenerator(args.version)
    pb.run(args)

if __name__ == "__main__":
    main()
