{% assign collection_list = site.collections %}

{% assign ordered = '' | split: '' %}
{% assign endered = '' | split: '' %}
{% assign unorder = '' | split: '' %}
{% for collection in collection_list %}
  {% unless collection.label == 'posts' or collection.docs.size == 0 %}
    {% if collection.tab-order %}
      {% if collection.tab-order < 0 %}{% assign endered = endered | push: collection %}
      {% else %}{% assign ordered = ordered | push: collection %}
      {% endif %}
    {% else %}{% assign unorder = unorder | push: collection %}
    {% endif %}
  {% endunless %}
{% endfor %}
{% assign ordered = ordered | sort: 'tab-order' %}
{% assign endered = endered | sort: 'tab-order' | reverse %}
{% assign collection_list = ordered | concat: unorder | concat: endered %}

{% for collection in collection_list %}
{% capture tab_class %}ui {% if forloop.first %}active {% endif %}inverted tab segment{% endcapture %}
<div class="{{ tab_class }}" data-tab="{{ collection.label | downcase }}">
<div class="ui inverted link list">
{% assign doc_list = collection.docs %}

{% unless collection.ignore-page-order %}
{% assign ordered_doc_list = '' | split: '' %}

{%- comment %}
for collection pages, we want to support user-defined ordering, but not require it on every page.

this means we have to create our own file tree, keeping children with parents as we re-sort
the collection list from its default alphabetical order into user order, and for that,
we want to perform a depth-first iterative visitation of the page tree.

here's a psuedocode implementation in a javascript-like language; given root:

  curr = root;
  stack = [];
  stack.push(root);
  while (stack.length > 0) {
    if (curr) {
      print(curr);

      if (curr.sibling)
        stack.push(curr.sibling);

      curr = curr.child;
    }
    else
      curr = stack.pop();
  }

but liquid brings some interesting challenges:
- no while loops
- no infinite loops
- no custom objects or hashes, just ordered arrays
- no array mutation in place
- no actual tree structure to start with, just a list of docs

so our strategy is to:
- start by bucketing the docs into levels, to get a tree-like context
  - levels correspond to 'generations', but not always parent-child links
    - parent-child relationships have to be tested by comparing path segments
  - items in the same level are of the same generation, but not always siblings
    - sibling relationships have to be tested by comparing path segments
- use arrays for tree nodes so we can store state (a 2-tuple: level index, item index)
- push state onto a stack made of an array and the push/pop operations from jekyll
- use a pseudo-infinite range loop with large bounds and a break clause in place of a while loop
{% endcomment -%}

{%- comment %} first, calculate max_depth as deepest level index {% endcomment -%}
{% assign max_depth = 0 %}
{% for doc in doc_list %}
  {% assign depth = doc.path | split:'/' | size | minus:2 %} {%- comment %} minus one to convert from length to index, minus two to ignore root collection directory (e.g. _guides/) {% endcomment -%}
  {% if depth > max_depth %}{% assign max_depth = depth %}{% endif %}
{% endfor %}

{%- comment %} separate docs into levels, and sort by user-defined order value {% endcomment -%}
{%- comment %} ordering: 0 is first, -1 is last, undefined orders go in the middle alphabetically
                first ---------> middle ----------------------> last --------->|
                [0..positive N]..[no order defined, use alpha]..[negative N..-1]
{% endcomment -%}
{% assign leveled = '' | split: '' %}
{% for i in (0..max_depth) %}
  {% assign new = '' | split: '' %}
  {% assign ordered = '' | split: '' %}
  {% assign endered = '' | split: '' %}
  {% assign unorder = '' | split: '' %}
  {% for doc in doc_list %}
    {% assign depth = doc.path | split:'/' | size | minus:2 %}
    {% if depth == i %}
      {% if doc.order %}
        {% if doc.order < 0 %}{% assign endered = endered | push: doc %}
        {% else %}{% assign ordered = ordered | push: doc %}
        {% endif %}
      {% else %}{% assign unorder = unorder | push: doc %}
      {% endif %}
    {% endif %}
  {% endfor %}
  {% assign ordered = ordered | sort: 'order' %}
  {% assign endered = endered | sort: 'order' %}
  {% assign new = ordered | concat: unorder | concat: endered %}
  {% assign leveled = leveled | push: new %}
{% endfor %}

{%- comment %} set root and make current be root {% endcomment -%}
{%- assign root = '' | split: '' -%}
{%- assign root = root | push: 0 -%} {%- comment %} level index {% endcomment -%}
{%- assign root = root | push: 0 -%} {%- comment %} group index {% endcomment -%}
{%- assign curr = root -%}
{%- comment %} create a stack and initialize it with the root node {% endcomment -%}
{%- assign stack = '' | split: '' -%}
{%- assign stack = stack | push: root -%}

{%- comment %} fake a while loop with a fake infinite loop, being sure to bail when all nodes are processed {% endcomment -%}
{%- assign enough = doc_list.size | times: 2 -%} {%- comment %} estimate the worst case number of iterations required {% endcomment -%}
{%- for ever in (0..enough) -%}
{%- if stack.size == 0 %}{% break %}{% endif -%}
  {%- comment %} if we have a current node, record it, push next sibling on the stack, and move on to first child {% endcomment -%}
  {%- if curr -%}
    {%- comment %} print curr {% endcomment -%}
    {%- assign l = curr[0] -%}                       {%- comment %} level {% endcomment -%}
    {%- assign e = curr[1] -%}                       {%- comment %} entry {% endcomment -%}
    {%- assign d = leveled[l][e] -%}                 {%- comment %} doc {% endcomment -%}
    {%- assign p = d.path | split: '/' -%}           {%- comment %} path parts {% endcomment -%}
    {%- assign t = p | last | split: '.' | first -%} {%- comment %} title, minus file extension {% endcomment -%}
    {%- assign _ = p | pop -%}                       {%- comment %} drop file part {% endcomment -%}
    {%- assign s = _ | last -%}                      {%- comment %} section (containing folder) {% endcomment -%}

    {%- comment %} record node docs in visitation order {% endcomment -%}
    {%- assign ordered_doc_list = ordered_doc_list | push: d -%}

    {%- comment %} clear current node now that it's recorded {% endcomment -%}
    {%- assign curr = nil -%}

    {%- comment %} find and push next sibling in current group (if any) {% endcomment -%}
    {%- assign e1 = e | plus: 1 -%}
    {%- assign en = leveled[l].size | minus: 1 -%}
    {%- for ei in (e1..en) -%} {%- comment %} look forward from current entry's index {% endcomment -%}
      {%- assign di = leveled[l][ei] -%}
      {%- assign pi = di.path | split: '/' -%}
      {%- assign pi = pi | pop -%}
      {%- assign si = pi | last -%}
      {%- if si == s %} {%- comment %} siblings share the same section {% endcomment -%}
        {%- comment %} ..and each higher path component also needs to match so we don't falsely select a separate subtree using the same names {% endcomment -%}
        {%- assign full_match = true -%}
        {%- for i in (0..l) -%}{%- unless pi[i] == p[i] -%}{%- assign full_match = false -%}{%- break -%}{%- endunless -%}{%- endfor -%}
        {%- if full_match -%}
          {%- assign next_item = '' | split: '' -%}
          {%- assign next_item = next_item | push: l -%}
          {%- assign next_item = next_item | push: ei -%}
          {%- assign stack = stack | push: next_item -%}
          {%- break -%}
        {%- endif -%}
      {%- endif -%}
    {%- endfor -%}

    {%- comment %} find and set curr to first matching child in next level group (if any) {% endcomment -%}
    {%- if l < max_depth -%}
      {%- assign l1 = l | plus: 1 -%}
      {%- assign ei = 0 -%} {%- comment %} start at beginning of entry group {% endcomment -%}
      {%- for di in leveled[l1] -%}
        {%- assign pi = di.path | split:'/' -%}
        {%- if pi[l1] == t %} {%- comment %} children are found in directories that match their parent's title {% endcomment -%}
        {%- comment %} ..and each higher path component also needs to match so we don't falsely select a separate subtree using the same names {% endcomment -%}
          {%- assign full_match = true -%}
          {%- for i in (0..l) -%}{%- unless pi[i] == p[i] -%}{%- assign full_match = false -%}{%- break -%}{%- endunless -%}{%- endfor -%}
          {%- if full_match -%}
            {%- assign curr = '' | split: '' -%}
            {%- assign curr = curr | push: l1 -%}
            {%- assign curr = curr | push: ei -%}
            {%- break -%}
          {%- endif -%}
        {%- endif -%}
        {%- assign ei = ei | plus: 1 -%}
      {%- endfor -%}
    {%- endif -%}

  {%- comment %} if no current node, pop from stack and start the loop over {% endcomment -%}
  {%- else -%}
    {%- assign curr = stack | last -%}
    {%- assign stack = stack | pop %}
  {%- endif -%}
{%- endfor -%}

{% assign doc_list = ordered_doc_list %}
{% endunless %}

{%- comment %} delegate to extension point for actual rendering of link list {% endcomment -%}
{% include render_indices.liquid doc_list=doc_list collection_label=collection.label page_title=page.title %}

</div>

&nbsp;<br>
&nbsp;<br>
</div>
{% endfor %}


{%- comment %} these fixed items (search & collection tabs) need to be above the tab segments {% endcomment -%}

{% comment %} This can be re-enabled if there are more collections than graphs to warrant other sections {% endcomment %}
<div id="sidebar-collection-tabs" class="ui inverted vertical secondary pointing fixed menu">
  <a class="{% if page.url == '/' %}active {% endif %}item" href="{{ site.baseurl }}/">Home</a>
{% for collection in collection_list %}
{% capture tab_class %}active item{% endcapture %}
  <a class="{{ tab_class }}" data-tab="{{ collection.label | downcase }}">{{ collection.title }}</a>
{% endfor %}
</div>

<div class="ui inverted large fixed menu">
<div class="item">
{% include elements/search.html %}
</div>
</div>
