/*
 * Copyright (C) 2022 Tomas Tintera
 *
 * Permission to use, copy, modify, and/or distribute this
 * software for any purpose with or without fee is hereby
 * granted.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS
 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH
 * THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

var X690 = (function () {
    var X690 = function X690(bytes) {
        if (Array.isArray(bytes)) {
            this.content = {
                getByte: function getByte(index) {
                    return bytes[index];
                },

                substr: function substr(pos, length) {
                    return String.fromCharCode.apply(null, bytes.slice(pos, pos + length));
                },
            };
        }
    };

    X690.TYPE_SEQUENCE = 0x30;
    X690.TYPE_INTEGER = 0x02;
    X690.TYPE_OCTET_STRING = 0x04;
    X690.TYPE_IDENTIFIER = 0x06;

    X690.prototype.extractMeta = function extractMeta(offset) {
        var pointer = offset;

        var type = this.content.getByte(pointer++);
        if (!(~type&31)) {
            do {
                type = 256*type + this.content.getByte(pointer++);
            } while (octet&128);
        }

        var length = this.content.getByte(pointer++);
        if (length&128) {
            var after = pointer + (length&127);
            length = 0;
            while (pointer < after) {
                length = 256*length + this.content.getByte(pointer++);
            }
        }

        return {
            type: type,
            inside: pointer,
            length: length,
            next: pointer + length
        };
    };

    X690.toBytes = function toBytes(value) {
        if (Array.isArray(value)) {
            return value;
        }
        if (typeof value === "string") {
            return value.split("").map(function (chr) {
                return chr.charCodeAt(0);
            });
        }
        if (typeof value === "number") {
            for (
                var val = value,
                    bytes = [];
                val;
                val = Math.floor(val/256)
            ) {
                bytes.push(val%256);
            }
            return bytes.reverse();
        }
    };

    X690.prototype.cmp = function cmp(meta, comparable) {
        var bytes = X690.toBytes(comparable);
        if (meta.length !== bytes.length) {
            return false;
        }
        for (var i = 0; i < meta.length; i++) {
            if (this.content.getByte(meta.inside + i) !== bytes[i]) {
                break;
            }
        }
        return i === meta.length;
    };

    X690.prototype.search = function search(cb, inMeta, afterMeta) {
        var maxOffset = inMeta.next;
        if (afterMeta) {
            var offset = afterMeta.next;
        } else {
            offset = inMeta.inside;
        }

        while (offset < maxOffset) {
            var meta = this.extractMeta(offset);
            var result = cb(meta);
            if (result) {
                return result;
            }
            offset = meta.next;
        }
    };

    X690.prototype.searchType = function searchType(type, inMeta, afterMeta) {
        return this.search(function (meta) {
            if (meta.type === type) {
                return meta;
            }
        }, inMeta, afterMeta);
    };

    return X690;
}());
