export class MDNav {

    constructor(public root: any, public pos: number = 0) {}

    find(test: (element: any) => boolean = () => true, index: number = 0): MDNav {
        if (!this.root || !this.root.children) {
            return new MDNav(null);
        }

        let currIndex = 0;

        for (let i = this.pos; i < this.root.children.length; i++) {
            let child = this.root.children[i];

            if (test(child)) {
                if (currIndex === index) {
                    return new MDNav(this.root, i);
                } else {
                    currIndex++;
                }
            }
        }

        return new MDNav(this.root, this.root.children.length);
    }

    findAll(test: (element: any) => boolean = () => true, index: number = 0): MDNav[] {
        if (!this.root || !this.root.children) {
            return [];
        }

        let result = [];

        let currIndex = 0;

        for (let i = this.pos; i < this.root.children.length; i++) {
            let child = this.root.children[i];

            if (test(child)) {
                if (currIndex === index) {
                    result.push(new MDNav(this.root, i));
                } else {
                    currIndex++;
                }
            }
        }

        return result;
    }


    emph(test: (element: any) => boolean = () => true, index: number = 0): MDNav {
        return this.find((h) => {
            return h.type === "emphasis" && test(h);
        }, index);
    }


    heading(test: (element: any) => boolean = () => true, index: number = 0): MDNav {
        return this.find((h) => {
            return h.type === "heading" && test(h);
        }, index);
    }


    headings(test: (element: any) => boolean = () => true, index: number = 0): MDNav[] {
        return this.findAll((h) => {
            return h.type === "heading" && test(h);
        }, index);
    }


    html(test: (element: any) => boolean = () => true, index: number = 0): MDNav {
        return this.find((h) => {
            return h.type === "html" && test(h);
        }, index);
    }

    link(test: (element: any) => boolean = () => true, index: number = 0): MDNav {
        return this.find((h) => {
            return h.type === "link" && test(h);
        }, index);
    }

    links(test: (element: any) => boolean = () => true, index: number = 0): MDNav[] {
        return this.findAll((h) => {
            return h.type === "link" && test(h);
        }, index);
    }

    list(test: (element: any) => boolean = () => true, index: number = 0): MDNav {
        return this.find((h) => {
            return h.type === "list" && test(h);
        }, index);
    }


    listItem(test: (element: any) => boolean = () => true, index: number = 0): MDNav {
        return this.find((h) => {
            return h.type === "listItem" && test(h);
        }, index);
    }

    listItems(test: (element: any) => boolean = () => true, index: number = 0): MDNav[] {
        return this.findAll((h) => {
            return h.type === "listItem" && test(h);
        }, index);
    }

    paragraph(test: (element: any) => boolean = () => true, index: number = 0): MDNav {
        return this.find((h) => {
            return h.type === "paragraph" && test(h);
        }, index);
    }


    strong(test: (element: any) => boolean = () => true, index: number = 0): MDNav {
        return this.find((h) => {
            return h.type === "strong" && test(h);
        }, index);
    }


    table(test: (element: any) => boolean = () => true, index: number = 0): MDNav {
        return this.find((h) => {
            return h.type === "table" && test(h);
        }, index);
    }


    tableRow(test: (element: any) => boolean = () => true, index: number = 0): MDNav {
        return this.find((h) => {
            return h.type === "tableRow" && test(h);
        }, index);
    }


    tableCell(test: (element: any) => boolean = () => true, index: number = 0): MDNav {
        return this.find((h) => {
            return h.type === "tableCell" && test(h);
        }, index);
    }


    text(test: (element: any) => boolean = () => true, index: number = 0): MDNav {
        return this.find((h) => {
            return h.type === "text" && test(h);
        }, index);
    }


    get item(): any {
        if (!this.root || !this.root.children) {
            return undefined;
        } else {
            return this.root.children[this.pos];
        }
    }

    get empty(): boolean {
        return  !this.root ||
                !this.root.children ||
                (this.pos >= this.root.children.length);
    }


    get childNav(): MDNav {
        return new MDNav(this.item);
    }


    get value(): any {
        if (this.item && this.item["value"]) {
            return this.item.value;
        } else {
            return "";
        }
    }


    get textValue() : string {
        if (this.item) {
            if (this.item["value"]) {
                return this.item.value;
            } else if (
                this.item.children &&
                (this.item.children.length > 0) &&
                (this.item.children[0].type === "text")
            ){
                return this.item.children[0].value;
            } else {
                return "";
            }
        } else {
            return "";
        }
    }
}