import ByteStream from './ByteStream'
function DataParser(data) {
    this.stream = new ByteStream(data);
    // the final parsed object from the data
    this.output = {};
}

DataParser.prototype.parse = function(schema) {
    // the top level schema is just the top level parts array
    this.parseParts(this.output, schema);
    return this.output;
};

// parse a set of hierarchy parts providing the parent object, and the subschema
DataParser.prototype.parseParts = function(obj, schema) {
    for (var i = 0; i < schema.length; i++) {
        var part = schema[i];
        this.parsePart(obj, part);
    }
};

DataParser.prototype.parsePart = function(obj, part) {
    var name = part.label;
    var value;

    // make sure the part meets any parse requirements
    if (part.requires && !part.requires(this.stream, this.output, obj)) {
        return;
    }

    if (part.loop) {
        // create a parse loop over the parts
        var items = [];
        while (part.loop(this.stream)) {
            var item = {};
            this.parseParts(item, part.parts);
            items.push(item);
        }
        obj[name] = items;
    } else if (part.parts) {
        // process any child parts
        value = {};
        this.parseParts(value, part.parts);
        obj[name] = value;
    } else if (part.parser) {
        // parse the value using a parser
        value = part.parser(this.stream, this.output, obj);
        if (!part.skip) {
            obj[name] = value;
        }
    } else if (part.bits) {
        // convert the next byte to a set of bit fields
        obj[name] = this.parseBits(part.bits);
    }
};

// combine bits to calculate value
function bitsToNum(bitArray) {
    return bitArray.reduce(function(s, n) {
        return s * 2 + n;
    }, 0);
}

// parse a byte as a bit set (flags and values)
DataParser.prototype.parseBits = function(details) {
    var out = {};
    var bits = this.stream.readBitArray();
    for (var key in details) {
        var item = details[key];
        if (item.length) {
            // convert the bit set to value
            out[key] = bitsToNum(bits.slice(item.index, item.index + item.length));
        } else {
            out[key] = bits[item.index];
        }
    }
    return out;
};

export default DataParser