<div id="root"></div>
<script type="module">

import _ from "https://dev.jspm.io/lodash";
import styled from "https://dev.jspm.io/styled-components";
import React from "https://dev.jspm.io/react/index.js";
import ReactDOM from "https://dev.jspm.io/react-dom/index.js";
import ReactDOMServer from "https://dev.jspm.io/react-dom/server.js";


let Util = {
    ParsePassage:inRef=>
    {
        let indexNumeric = inRef.lastIndexOf(" ");
        let partBook = inRef.substring(0, indexNumeric);
        let chapterVerse = inRef.substring(indexNumeric+1).split(":");
        return [partBook, chapterVerse[0], chapterVerse[1]];
    }
};

let h = React.createElement;
let s = styled.default;

let ElemApp = props =>
{
    return h("div", null,
    [
        h(ElemTree, {key:"tree1", tree:App.State.Topics}),
        h(ElemTree, {key:"tree2", tree:App.State.Bible}),
        h(ElemTree, {key:"tree3", tree:App.State.Date}),
        h(ElemItems, {key:3})
    ]
    );
};

let ElemItems = props =>
{
    if(App.State.Items.Active.length == 0)
    {
        return h("div", null, 
            h("h3", null, "no results found")
        )
    }
    let list = App.State.Pages.All[App.State.Pages.Active].map( i=>h(ElemItem, {...i, key:i.ID} ) );
    let pages = h(ElemPager, {count:App.State.Pages.All.length, active:App.State.Pages.Active}, null);

    return h("div", null, [
        h("h4", {key:0}, App.State.Items.Active.length+" results"),
        h("div", {key:1}, pages),
        h("div", {key:2}, list),
        h("div", {key:3}, pages)
    ]);
};
let ElemItem = props =>
{
    return h("div", {key:0}, [
        h("div", {key:1},
        [
            h("span", {key:2}, props.title),
            h("em", {key:3}, props.id),
            h("strong", {key:4}, props.date),
        ]
        ),
        h("small", {key:4}, props.bible)
    ])
};
let ElemPager = ({count, active}) =>
{
    var children;
    var blockIntro, blockAt, blockOutro;
    var hitLeft, hitRight;

    let renderRange = (inStart, inStop) =>
    {
        let output = [];
        for(let i=inStart; i<=inStop; i++)
        {
            output.push(h("button", {key:i, onClick:e=>App.Update.Page(i)}, i+1));
        }
        return output;
    }
    let renderSpacer = () => h("span", {key:Math.random()}, "…");


    blockIntro = [0, 1, 2];
    blockAt = [active-1, active, active+1];
    blockOutro = [count-3, count-2, count-1];

    hitLeft = _.last(blockIntro) >= _.head(blockAt);
    hitRight = _.last(blockAt) >= _.head(blockOutro);

    if(count < 2)
    {
        children = [];
    }
    else
    {
        if( (hitLeft && hitRight) || count < 7)
        {
            children = renderRange(_.head(blockIntro), _.last(blockOutro)) /* merge all */
        }
        else if(!hitLeft && hitRight)
        {
            children = [
                ...renderRange(_.head(blockIntro), _.last(blockIntro)),
                renderSpacer(),
                ...renderRange(Math.min(_.head(blockAt), _.head(blockOutro)), _.last(blockOutro)) /* merge end */
            ];
        }
        else if(hitLeft && !hitRight)
        {
            children = [
                ...renderRange(_.head(blockIntro), Math.max(_.last(blockAt), _.last(blockIntro)) ), /* merge beginning */
                renderSpacer(),
                ...renderRange(_.head(blockOutro), _.last(blockOutro))
            ];
        }
        else if(!hitLeft && !hitRight)
        {
            children = [
                ...renderRange(_.head(blockIntro), _.last(blockIntro)),
                renderSpacer(),
                ...renderRange(_.head(blockAt), _.last(blockAt)),
                renderSpacer(),
                ...renderRange(_.head(blockOutro), _.last(blockOutro))
            ];
        }

    }

    return h("div", {}, children);
};

let ElemColumn = s.div`
    display:inline-block;
    vertical-align:top;
`;

let ElemTree = ({tree}) =>
{
    let activeItems = tree.Active.map( a=>{
        return h("button", {disabled:a.Parent?false:true, key:a.ID, onClick:e=>App.Update.Select(a, tree)}, a.Display )
    });
    return h(ElemColumn, {},
    [
        h("h3", {key:"title"}, tree.Display),
        h("div", {key:"filters-active"}, [
            h("strong", {key:"label"}, "Showing:"),
            h("span", {key:"list"}, activeItems)
        ]),
        h("div", {key:"filters-browse"}, [
            h("strong", {key:"label"}, "Available:"),
            h(ElemTreeNode, {key:"tree", node:tree.Root, tree:tree})
        ])
    ]);
};
let ElemTreeNode = ({node, tree}) =>
{
    let children = [];
    if(node.Children && node.Open)
    {
        children = node.Children.map( c=>h(ElemTreeNode, {node:c, tree:tree, key:c.ID}));
    }

    let elemTitle = (inExpandable, inButton) =>
    {
        let attributes = null;
        let parts = [h("span", null, node.Display+" ("+node.Leaves.length+") ")];

        if(inExpandable)
        {
            attributes = {onClick:e=>{ e.stopPropagation(); App.Update.Interact(node); }};
            parts.unshift(h("span", null, node.Open?"-":"+"));
        }
        if(inButton)
        {
            parts.push(
                h("button",
                { 
                    onClick:e=>{ e.stopPropagation(); App.Update.Select(node, tree); }
                },
                node.Active?"remove":"add")
            );
        }
        return h("div", attributes, parts);
    }

    var partTitle;

    if(!node.Parent)
    {
        partTitle = elemTitle(true, false);
    }
    else
    {
        if(node.Children)
        {
            partTitle = elemTitle(true, true);
        }
        else
        {
            partTitle = elemTitle(false, true);
        }
    }

    return h("div", {style:{padding:"10px"}}, [
        partTitle,
        h("div", {}, children)
    ]);
};


let Node = {
    Create:(inID, inDisplay, inChildren)=>
    {
        if(!inDisplay)
        {
            inDisplay = inID;
        }
        let node = {
            ID:inID,
            Display:inDisplay,
            Open:false,
            Active:false,
            Parent:false,
            Children:inChildren,
            Leaves:[]
        };
        if(inChildren)
        {
            inChildren.forEach(c=>c.Parent = node);
        }
        return node;
    },
    IterateDown:(inNode, inIterator)=>
    {
        if( inIterator(inNode) )
        {
            return true;
        }
        if(inNode.Children)
        {
            for(let i=0; i<inNode.Children.length; i++)
            {
                if(Node.IterateDown(inNode.Children[i], inIterator))
                {
                    return true;
                }
            }
        }
    },
    IterateUp:(inNode, inIterator)=>
    {
        if( inIterator(inNode) )
        {
            return true;
        }
        if(inNode.Parent)
        {
            Node.IterateUp(inNode.Parent, inIterator);
        }
    }
};

var App = {
    State:
    {
        Items:
        {
            All:[],
            Active:[]
        },
        Pages:
        {
            All:[],
            Active:0,
        },
        Topics:
        {
            Display:"Topics",
            Results:[],
            Active:[],
            Root:
            Node.Create("root", "All", [
                Node.Create("", "Unclassified"),
                Node.Create("ideologies", "Ideologies", [
                    Node.Create("gospel-the", "The Gospel"),
                    Node.Create("free-will", "Free Will"),
                    Node.Create("secular-culture", "Secular Culture")
                ]),
                Node.Create("people", "People", [
                    Node.Create("biblical-figures", "Biblical Figures"),
                    Node.Create("jesus-christ", "Jesus Christ"),
                ])
            ])
        },
        Bible:
        {
            Display:"Bible",
            Results:[],
            Active:[],
            Root:
            Node.Create("all", "All", [
                Node.Create("", "Unclassified"),
                Node.Create("old ", "Old Testament",
                [
                    Node.Create("pent", "Pentatuch",
                    [
                        Node.Create("Genesis", "Genesis"),
                        Node.Create("Exodus", "Exodus"),
                        Node.Create("Leviticus", "Leviticus"),
                        Node.Create("Duteronomy", "Duteronomy"),
                        Node.Create("Numbers", "Numbers")
                    ]),
                    Node.Create("hist", "History",
                    [
                        Node.Create("Josua", "Josua"),
                        Node.Create("Judges", "Judges"),
                        Node.Create("Ruth", "Ruth"),
                        Node.Create("1 Samuel", "1 Samuel"),
                        Node.Create("2 Samuel", "2 Samuel"),
                        Node.Create("1 Kings", "1 Kings"),
                        Node.Create("2 Kings", "2 Kings"),
                        Node.Create("1 Chronicles", "1 Chronicles"),
                        Node.Create("2 Chronicles", "2 Chronicles"),
                        Node.Create("Ezra", "Ezra"),
                        Node.Create("Nehemiah", "Nehemiah"),
                        Node.Create("Esther", "Esther")
                    ]),
                    Node.Create("poet", "Poetry",
                    [
                        Node.Create("Job", "Job"),
                        Node.Create("Psalms", "Psalms"),
                        Node.Create("Proverbs", "Proverbs"),
                        Node.Create("Ecclesiastes", "Ecclesiastes"),
                        Node.Create("Song of Solomon", "Song of Songs"),
                    ]),
                    Node.Create("majo", "Major Prophets",
                    [
                        Node.Create("Isaiah", "Isaiah"),
                        Node.Create("Jeremiah", "Jeremiah"),
                        Node.Create("Lamentations", "Lamentations"),
                        Node.Create("Ezekiel", "Ezekiel"),
                        Node.Create("Daniel", "Daniel"),
                    ]),
                    Node.Create("mino", "Minor Prophets",
                    [
                        Node.Create("Hosea"),
                        Node.Create("Joel"),
                        Node.Create("Amos"),
                        Node.Create("Obadiah"),
                        Node.Create("Jonah"),
                        Node.Create("Micah"),
                        Node.Create("Nahum"),
                        Node.Create("Habakkuk"),
                        Node.Create("Zephaniah"),
                        Node.Create("Haggai"),
                        Node.Create("Zechariah"),
                        Node.Create("Malachi")
                    ])
                ]),
                Node.Create("nt", "New Testament", [
                    Node.Create("gospels", "The Gospels", [
                        Node.Create("Matthew"),
                        Node.Create("Mark"),
                        Node.Create("Luke"),
                        Node.Create("John"),
                    ]),
                    Node.Create("Acts"),
                    Node.Create("Romans"),
                    Node.Create("1 Corinthians"),
                    Node.Create("2 Corinthians"),
                    Node.Create("Galatians"),
                    Node.Create("Ephesians"),
                    Node.Create("1 Thessalonians")
                ])
            ])
        },
        Date:
        {
            Display:"Date",
            Results:[],
            Active:[],
            Root:
            Node.Create("all", "All", [
                Node.Create("", "Unclassified"),
                Node.Create("8", "1980s"),
                Node.Create("9", "1990s"),
                Node.Create("0", "2000s"),
                Node.Create("1", "2010s"),
                Node.Create("2", "2020s")
            ])
        }
    },
    OldClassifier:()=>
    {
        let leafStructure = inTree=>
        {
            let output = [];
            Node.IterateDown(inTree.Root, n=>
            {
                if(!n.Children)
                {
                    output[n.ID] = n;
                }
            });
            return output;
        };
        let structTopics = leafStructure(App.State.Topics)
        let structBible = leafStructure(App.State.Bible);
        let structDate = leafStructure(App.State.Date);


        ////
        
        var matched, match;
        // match topics
        matched = false;
        topics.forEach(t=>
        {
            match = structTopics[t];
            if(match)
            {
                match.Leaves.push(output);
                matched = true;
            }
        });
        if(!matched)
        {
            structTopics[""].Leaves.push(output);
        }

        // match bible
        matched = false;
        bible.forEach(t=>
        {
            match = structBible[ Util.ParsePassage(t)[0] ];
            if(match)
            {
                match.Leaves.push(output);
                matched = true;
            }
        });
        if(!matched)
        {
            structBible[""].Leaves.push(output);
        }

        // match date
        matched = false;
        match = structDate[date[2]];
        if(match)
        {
            match.Leaves.push(output);
            matched = true;
        }
        if(!matched)
        {
            structDate[""].Leaves.push(output);
        }

        ////


        let itrUp = inLeaf=>
        {
            Node.IterateUp(inLeaf, inBranch=>
            {
                inBranch.Leaves = _.union(inBranch.Leaves, inLeaf.Leaves)
            });
        };
        Object.values(structTopics).forEach(itrUp);
        Object.values(structBible).forEach(itrUp);
        Object.values(structDate).forEach(itrUp);
    },
    Update:
    {
        Load:(file)=>
        {
            fetch(file)
            .then(inAccept=>inAccept.text())
            .then(inAccept=>
            {
                let columns = inAccept.split("|");
                for(let i=0; i<columns.length; i+=5)
                {
                    let title  = columns[i+0];
                    let id     = columns[i+1];
                    let date   = columns[i+2];
                    let bible  = columns[i+3].split("*");
                    let topics = columns[i+4].split("*");

                    let output = { title, id, date, topics, bible };

                    App.State.Items.All.push(output);
                }
            })
            .then(inAccept=>
            {
                App.ApplyFilters();
                App.Render();
            });
        },
        Interact:(inNode)=>
        {
            if(inNode.Open)
            {
                Node.IterateDown(inNode, n=>{n.Open=false;});
            }
            else
            {
                Node.IterateUp(inNode, n=>{n.Open=true;});
            }
            App.Render();
        },
        Select:(inNode, inTree)=>
        {
            let clear = c =>
            {
                let index = _.indexOf(inTree.Active, c);
                inTree.Active.splice( index, 1 );
                c.Active = false;
            };
            let add = a =>
            {
                a.Active = true;
                inTree.Active.push(a);
            };
            let itr = n=>
            {
                if(n.Active)
                {
                    clear(n);
                }
            };

            if(inNode.Active)
            {
                clear(inNode);
            }
            else
            {
                Node.IterateUp(inNode, itr);
                Node.IterateDown(inNode, itr);
                add(inNode);
            }

            inTree.Results = [];
            inTree.Active.forEach(inNode =>
            {
                inTree.Results = inTree.Results.concat( inNode.Leaves );
            });

            App.ApplyFilters();
            App.Render();
        },
        Page:(page)=>
        {
            if(page !== false)
            {
                App.State.Pages.Active = page;
                App.Render();
            }
        }
    },
    RootDOM:document.querySelector("#root"),
    RootComponent:ElemApp,
    Render:()=>ReactDOM.render( h(App.RootComponent), App.RootDOM ),
    ApplyFilters:()=>
    {
        App.State.Items.Active = _.union(App.State.Topics.Results, App.State.Bible.Results, App.State.Date.Results);
        if(App.State.Items.Active.length == 0)
        {
            App.State.Items.Active = App.State.Items.All;
        }
        App.State.Pages.All = _.chunk(App.State.Items.Active, 10);
        App.State.Pages.Active = 0;
    }
};

App.Update.Load("data-1.csv");
App.Update.Load("data-2.csv");
App.Update.Load("data-3.csv");

</script>