#!/usr/bin/python

import os
import nilmtools.filter
import nilmtools.decimate
import nilmdb.client
import argparse
import fnmatch

def main(argv = None):
    parser = argparse.ArgumentParser(
        formatter_class = argparse.RawDescriptionHelpFormatter,
        description = """\
    Automatically create multiple decimations from a single source
    stream, continuing until the last decimated level contains fewer
    than 500 points total.

    Wildcards and multiple paths are accepted.  Decimated paths are
    ignored when matching wildcards.
    """)
    def_url = os.environ.get("NILMDB_URL", "http://localhost/nilmdb/")
    parser.add_argument("-u", "--url", action="store", default=def_url,
                        help="NilmDB server URL (default: %(default)s)")
    parser.add_argument("-f", "--factor", action="store", default=4, type=int,
                        help='Decimation factor (default: %(default)s)')
    parser.add_argument("-m", "--max", action="store", default=500, type=int,
                        help='Maximum number of points in last level ' +
                        '(default: %(default)s)')
    parser.add_argument("-F", "--force-metadata", action="store_true",
                        default=False,
                        help="Force metadata changes if the dest "
                        "doesn't match")
    parser.add_argument("-v", "--version", action="version",
                        version=nilmtools.__version__)
    parser.add_argument("path", action="store", nargs='+',
                        help='Path of base stream')
    args = parser.parse_args(argv)

    if args.max < 0:
        print("error: bad max, must be nonnegative")
        raise SystemExit(1)

    # Pull out info about the base stream
    client = nilmdb.client.Client(args.url)

    # Find list of paths to process
    streams = [ str(s[0]) for s in client.stream_list() ]
    streams = [ s for s in streams if "~decim-" not in s ]
    paths = []
    for path in args.path:
        new = fnmatch.filter(streams, str(path))
        if not new:
            print("error: no stream matched path:", path)
            raise SystemExit(1)
        paths.extend(new)

    for path in paths:
        do_decimation(client, args, path)

def do_decimation(client, args, path):
    print("Decimating", path)
    info = nilmtools.filter.get_stream_info(client, path)
    if not info:  # pragma: no cover (only good paths passed above)
        raise Exception("path " + path + " not found")

    meta = client.stream_get_metadata(path)
    if "decimate_source" in meta:
        print("Stream", path, "was decimated from", meta["decimate_source"])
        print("You need to pass the base stream instead")
        raise SystemExit(1)

    # Figure out the type we should use for decimated streams
    if ('int32' in info.layout_type or
        'int64' in info.layout_type or
        'float64' in info.layout_type):
        decimated_type = 'float64_' + str(info.layout_count * 3)
    else:
        decimated_type = 'float32_' + str(info.layout_count * 3)

    # Now do the decimations until we have few enough points
    factor = 1
    while True:
        print("Level", factor, "decimation has", info.rows, "rows")
        if info.rows <= args.max:
            break
        factor *= args.factor
        new_path = "%s~decim-%d" % (path, factor)

        # Create the stream if needed
        new_info = nilmtools.filter.get_stream_info(client, new_path)
        if not new_info:
            print("Creating stream", new_path)
            client.stream_create(new_path, decimated_type)

        # Run the decimation as if it were run from the commandline
        new_argv = [ "-u", args.url,
                     "-f", str(args.factor) ]
        if args.force_metadata:
            new_argv.extend([ "--force-metadata" ])
        new_argv.extend([info.path, new_path])
        nilmtools.decimate.main(new_argv)

        # Update info using the newly decimated stream
        info = nilmtools.filter.get_stream_info(client, new_path)

    return

if __name__ == "__main__":
    main()
