/*
 * Copyright 2014 Corpuslinguistic working group Humboldt University Berlin.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package annis.gui.components;

import com.google.common.base.Joiner;
import com.vaadin.annotations.JavaScript;
import com.vaadin.ui.AbstractJavaScriptComponent;
import com.vaadin.ui.JavaScriptFunction;
import com.vaadin.ui.Label;
import com.vaadin.ui.MenuBar;
import com.vaadin.ui.MenuBar.MenuItem;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.themes.ValoTheme;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.json.JSONArray;
import org.json.JSONException;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Embedds a single HTML page and adds navigation to it's headers (if they have
 * an id).
 *
 * This is e.g. usefull for documentation.
 *
 * @author Thomas Krause <krauseto@hu-berlin.de>
 */
public class NavigateableSinglePage extends VerticalLayout
{
  
  private final static Logger log = LoggerFactory.getLogger(NavigateableSinglePage.class);

  private final IFrameComponent iframe = new IFrameComponent();
  
  private MenuBar navigation;
  private Map<String, MenuItem> menuItemRegistry = new HashMap<>();
  private MenuItem lastCheckedItem;
  private final static Pattern regexHeader = Pattern.compile("h([1-6])");
  
  public NavigateableSinglePage()
  {
    iframe.setSizeFull();
   
    addComponent(iframe);
    
    setExpandRatio(iframe, 1.0f);
    
  }

  private void onScroll(String headerID)
  {
    if(navigation != null)
    {
      MenuItem navRoot =  navigation.getItems().get(0);
      MenuItem toSelect = menuItemRegistry.get(headerID);
      if(toSelect != null)
      {
        navRoot.setText(toSelect.getText());
        toSelect.setStyleName("huge-selected");
        if(lastCheckedItem != null && lastCheckedItem != toSelect)
        {
          lastCheckedItem.setStyleName("huge");
        }        
        lastCheckedItem = toSelect;
      }
    }
  }

  public void setSource(String source)
  {
    iframe.getState().setSource(source);
    if(navigation != null)
    {
      removeComponent(navigation);
    }
    menuItemRegistry.clear();
    navigation = createMenubarFromHTML(source, menuItemRegistry);
    addComponent(navigation, 0);
  }
  
  private MenuBar createMenubarFromHTML(String source, Map<String, MenuItem> idToMenuItem)
  {
    
    MenuBar mbNavigation = new MenuBar();
    mbNavigation.setStyleName("huge");
    MenuItem navRoot = mbNavigation.addItem("Choose topic", null);
    navRoot.setStyleName("huge");
    
    try
    {
      Document doc = Jsoup.connect(source).get();
      
      ArrayList<MenuItem> itemPath = new ArrayList<>();
      // find all headers that have an ID
      for(Element e : doc.getElementsByAttribute("id"))
      {
        Matcher m = regexHeader.matcher(e.tagName());
        if(m.matches())
        {
          int level = Integer.parseInt(m.group(1))-1;
          
          // decide wether to expand the path (one level deeper) or to truncate
          if(level == 0)
          {
            itemPath.clear();
          }
          else if(itemPath.size() >= level)
          {
            // truncate
            itemPath = new ArrayList<>(itemPath.subList(0, level));
          }
          
          if(itemPath.isEmpty() && level > 0)
          {
            // fill the path with empty elements
            for(int i=0; i < level; i++)
            {
              itemPath.add(createItem(navRoot, itemPath, "<empty>"));
            }
          }
          MenuItem item = createItem(navRoot, itemPath, e.text());
          itemPath.add(item);
          idToMenuItem.put(e.id(), item);
        }
      }
    }
    catch (IOException ex)
    {
      log.error("Could not parse iframe source", ex);
    }
    return mbNavigation;
  }
  
  private MenuItem createItem(MenuItem rootItem, List<MenuItem> path, String caption)
  {
    MenuItem parent;
    
    if(path.isEmpty())
    {
      parent = rootItem;
    }
    else
    {
      parent = path.get(path.size()-1);
      
    }
    MenuItem child = parent.addItem(caption, null);
    child.setStyleName("huge");
    return child;
  }

  @JavaScript(
  {
    "vaadin://jquery.js", "navigateablesinglepage.js"
  })
  private class IFrameComponent extends AbstractJavaScriptComponent
  {

    public IFrameComponent()
    {
      addFunction("scrolled", new JavaScriptFunction()
      {

        @Override
        public void call(JSONArray arguments) throws JSONException
        {
          onScroll(arguments.getString(0));
        }
      });
    }

    @Override
    public final IframeState getState()
    {
      return (IframeState) super.getState();
    }
  }
}

@@ -4256,16 +4256,22 @@
 %3Cempty%3E%22
+, null
 ));%0A    
@@ -4352,24 +4352,32 @@
 th, e.text()
+, e.id()
 );%0A         
@@ -4672,16 +4672,27 @@
  caption
+, String id
 )%0A  %7B%0A  
@@ -4889,19 +4889,41 @@
 ption, n
-ull
+ew IDSelectionCommand(id)
 );%0A    c
@@ -4966,24 +4966,700 @@
 n child;%0A  %7D
+%0A  %0A  private class IDSelectionCommand implements MenuBar.Command%0A  %7B%0A    private final String id;%0A    %0A    public IDSelectionCommand(String id)%0A    %7B%0A      this.id = id;%0A    %7D%0A    %0A    @Override%0A    public void menuSelected(MenuItem selectedItem)%0A    %7B%0A      if(id != null)%0A      %7B%0A        String lastSource = iframe.getState().getSource();%0A        // remove the fragment part%0A        if(lastSource != null)%0A        %7B%0A          int hashPos = lastSource.lastIndexOf('#');%0A          if(hashPos %3E= 0)%0A          %7B%0A            lastSource = lastSource.substring(0, hashPos);%0A          %7D%0A          iframe.getState().setSource(lastSource + %22#%22 + id);%0A        %7D%0A      %7D%0A    %7D%0A    %0A  %7D
 %0A%0A  @JavaScr

allow selection of a chapter in tutorial