/**
 * Copyright 2016 the WHATAP project authors. All rights reserved.
 * Use of this source code is governed by a license that
 * can be found in the LICENSE file.
 */

function PATH(_nodes) {
    this.nodes = (_nodes || []);
    this.level = 0;
}
PATH.prototype.hasChild = function () {
    return this.level + 1 < this.nodes.length;
};
PATH.prototype.node = function () {
    return this.nodes[this.level];
};
PATH.prototype.toString = function () {
    return "PATH [nodes=" + JSON.stringify(this.nodes) + ", level=" + this.level + "]";
};

function ENTRY() {
    this.node=null;
    this.value=null;

    /* ENTRY */
    this.right=null;
    this.child=null;
    this.parent=null;
}
ENTRY.prototype.include = function (v) {
    if(this.node) {
        return (this.node === '*' || this.node === v);
    }
    return false;
};
ENTRY.prototype.path = function (top) {
    var sk = [];
    var cur = this;
    while (cur !== top) {
        sk.push(cur.node);
        cur = cur.parent;
    }
    return sk;
};

function PathTree() {
    this.top = new ENTRY();
    this.count = 0;
}
PathTree.prototype.insert = function (paths, value) {

    if(paths == null || paths.length === 0) {
        return null;
    }
    
    if(typeof paths === 'string') {
        paths = paths.split('/').filter(function (el) {
            return el.length != 0;
        });
    }
    
    if(Array.isArray(paths) === false) {
        return null;
    }

    if(paths.length === 1) {
        return null;
    }

    var path = new PATH(paths);
    if(this.top.child == null) {
        var cur = this.top;
        cur.child = new ENTRY();
        cur.child.parent = cur;
        cur = cur.child;
        cur.node = path.node();
        this.count++;
        return this.expand(cur, path, value);
    } else {
        return this._insert(this.top, this.top.child, path, value);
    }
};
PathTree.prototype.expand = function (cur, path, value) {
    while(path.hasChild()) {
        path.level++;
        cur.child = new ENTRY();
        cur.child.parent = cur;
        cur = cur.child;
        cur.node = path.node();
        this.count++;
    }
    var old = cur.value;
    cur.value = value;
    return old;
};
PathTree.prototype._insert = function (p, cur, path, value) {
    if(path.node() === cur.node) {
        if(path.hasChild() === false) {
            var old = cur.value;
            cur.value = value;
            return old;
        }
        path.level++;

        if(cur.child !== null) {
            return this._insert(cur, cur.child, path, value);
        } else {
            cur.child = new ENTRY();
            cur.child.parent = cur;
            cur = cur.child;
            cur.node = path.node();
            return this.expand(cur, path, value);
        }
    } else if(cur.right != null) {
        return this._insert(p, cur.right, path, value);
    } else {
        if(path.node() === '*') {
            cur.right = new ENTRY();
            cur.right.parent = p;
            cur = cur.right;
            cur.node = path.node();
            this.count++;
            return this.expand(cur, path, value);
        } else {
            cur = new ENTRY();
            cur.parent = p;
            cur.right = p.child;
            p.child = cur;
            cur.node = path.node();
            this.count++;
            return this.expand(cur, path, value);
        }
    }
};
PathTree.prototype.find = function (path) {
    try {
        if(path == null) {
            return null;
        }
        if(typeof path === 'string') {
            path = path.split('/').filter(function (el) {
                return el.length != 0;
            });
        }
        if(path.length === 0) {
            return null;
        }

        return this._find(this.top.child, new PATH(path));
    } catch (e) {
        return null;
    }
};
PathTree.prototype._find = function (cur, m) {

    if(cur.include(m.node())) {
        if(m.hasChild() === false) {
            return cur.value;
        }
        m.level++;
        if(cur.child !== null) {
            return this._find(cur.child, m);
        }
    } else if(cur.right !== null) {
        return this._find(cur.right, m);
    }
    return null;
};
PathTree.prototype.size = function () {
    return this.count;
};

module.exports = PathTree;