#!/usr/bin/env python3

import argparse
import sys
from pathlib import Path


def handle_file(path):
    """
    Return a tuple of (header, path) for the file at path.
    If the file does not have a header, the header is the empty string.
    """
    with open(path) as fd:
        header = fd.readline()
        if header.startswith("! "):
            return header, path
        else:
            return "", path


def merge(dest, files):
    """
    Merge the content of all files into the file dest.

    The first line of each file is an optional section header in the form
    e.g.
       ! model =  keycodes
    Where two sections have identical headers, the second header is skipped.

    Special case are header-less files which we store with the empty string
    as header, these need to get written out first.
    """

    def sort_basename(path):
        return path.name

    # sort the file list by basename
    files.sort(key=sort_basename)

    # pre-populate with the empty header so it's the first one to be written
    # out. We use section_names to keep the same order as we get the files
    # passed in (superfluous with python 3.6+ since the dict keeps the
    # insertion order anyway).
    sections = {"": []}
    section_names = [""]
    for path in files:
        # files may exist in srcdir or builddir, depending whether they're
        # generated
        header, path = handle_file(path)
        paths = sections.get(header, [])
        paths.append(path)
        sections[header] = paths
        if header not in section_names:
            section_names.append(header)

    for header in section_names:
        if header:
            dest.write("\n")
            dest.write(header)
        for f in sections[header]:
            with open(f) as fd:
                if header:
                    fd.readline()  # drop the header
                dest.write(fd.read())


if __name__ == "__main__":
    parser = argparse.ArgumentParser("rules file merge script")
    parser.add_argument("--dest", type=str, default=None)
    parser.add_argument("--srcdir", type=str)
    parser.add_argument("--builddir", type=str)
    parser.add_argument("files", nargs="+", type=str)
    ns = parser.parse_args()

    if ns.dest is None:
        dest = sys.stdout
    else:
        dest = ns.dest

    with dest or open(dest, "w") as fd:
        basename = Path(sys.argv[0]).name
        fd.write(
            "// DO NOT EDIT THIS FILE - IT WAS AUTOGENERATED BY {} FROM rules/*.part\n".format(
                basename
            )
        )
        fd.write("//\n")

        def find_file(f):
            if ns.builddir:
                path = Path(ns.builddir) / f
                if path.exists():
                    return path
            if ns.srcdir:
                path = Path(ns.srcdir) / f
                if path.exists():
                    return path
            return Path(f)

        merge(fd, [find_file(f) for f in ns.files])
