#!/usr/bin/python

import nilmtools.filter
import nilmdb.client
import numpy as np
import operator

def main(argv = None):
    f = nilmtools.filter.Filter()
    parser = f.setup_parser("Decimate a stream")
    group = parser.add_argument_group("Decimate options")
    group.add_argument('-f', '--factor', action='store', default=4, type=int,
                       help='Decimation factor (default: %(default)s)')

    # Parse arguments
    try:
        args = f.parse_args(argv)
    except nilmtools.filter.MissingDestination as e:
        # If no destination, suggest how to create it by figuring out
        # a recommended layout.
        src = e.src
        dest = e.dest
        print("Source is %s (%s)" % (src.path, src.layout))
        print("Destination %s doesn't exist" % (dest.path))
        if "decimate_source" in f.client_src.stream_get_metadata(src.path):
            rec = src.layout
        elif ('int32' in src.layout_type or
              'int64' in src.layout_type or
              'float64' in src.layout_type):
            rec = 'float64_' + str(src.layout_count * 3)
        else:
            rec = 'float32_' + str(src.layout_count * 3)
        print("You could make it with a command like:")
        print("  nilmtool -u %s create %s %s" % (e.dest.url,
                                                 e.dest.path, rec))
        raise SystemExit(1)

    if not (args.factor >= 2):
        raise Exception("factor needs to be 2 or more")

    f.check_dest_metadata({ "decimate_source": f.src.path,
                            "decimate_factor": args.factor })

    # If source is decimated, we have to decimate a bit differently
    if "decimate_source" in f.client_src.stream_get_metadata(args.srcpath):
        again = True
    else:
        again = False
    f.process_numpy(decimate, args = (args.factor, again))

def decimate(data, interval, args, insert_function, final):
    """Decimate data"""
    (factor, again) = args
    (n, m) = data.shape

    # Figure out which columns to use as the source for mean, min, and max,
    # depending on whether this is the first decimation or we're decimating
    # again.  Note that we include the timestamp in the means.
    if again:
        c = (m - 1) // 3
        # e.g. c = 3
        # ts mean1 mean2 mean3 min1 min2 min3 max1 max2 max3
        mean_col = slice(0, c + 1)
        min_col = slice(c + 1, 2 * c + 1)
        max_col = slice(2 * c + 1, 3 * c + 1)
    else:
        mean_col = slice(0, m)
        min_col = slice(1, m)
        max_col = slice(1, m)

    # Discard extra rows that aren't a multiple of factor
    n = n // factor * factor
    data = data[:n,:]

    # Reshape it into 3D so we can process 'factor' rows at a time
    data = data.reshape(n // factor, factor, m)

    # Fill the result
    out = np.c_[ np.mean(data[:,:,mean_col], axis=1),
                 np.min(data[:,:,min_col], axis=1),
                 np.max(data[:,:,max_col], axis=1) ]

    insert_function(out)
    return n

if __name__ == "__main__":
    main()
