package org.objectstyle.wolips.templateeditor;

import java.util.LinkedList;
import java.util.List;

import jp.aonir.fuzzyxml.FuzzyXMLDocument;
import jp.aonir.fuzzyxml.FuzzyXMLElement;
import jp.aonir.fuzzyxml.FuzzyXMLNode;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.ui.editors.text.TextEditor;
import org.objectstyle.wolips.bindings.wod.IWodElement;
import org.objectstyle.wolips.variables.BuildProperties;
import org.objectstyle.wolips.wodclipse.core.completion.WodParserCache;
import org.objectstyle.wolips.wodclipse.core.document.WodElementTypeHyperlink;
import org.objectstyle.wolips.wodclipse.core.util.ICursorPositionListener;
import org.objectstyle.wolips.wodclipse.core.util.WodHtmlUtils;

public class TemplateBreadcrumb extends Composite implements ICursorPositionListener, MouseListener {
  private TemplateSourceEditor _editor;

  public TemplateBreadcrumb(TemplateSourceEditor editor, Composite parent, int style) {
    super(parent, style);

    setBackground(parent.getBackground());
    RowLayout breadcrumbLayout = new RowLayout();
    breadcrumbLayout.wrap = false;
    breadcrumbLayout.pack = true;
    breadcrumbLayout.justify = false;
    breadcrumbLayout.type = SWT.HORIZONTAL;
    breadcrumbLayout.marginLeft = 5;
    breadcrumbLayout.marginTop = 3;
    breadcrumbLayout.marginRight = 5;
    breadcrumbLayout.marginBottom = 3;
    breadcrumbLayout.spacing = 5;
    setLayout(breadcrumbLayout);

    _editor = editor;
    _editor.addCursorPositionListener(this);
    addPaintListener(new PaintListener() {
      public void paintControl(PaintEvent e) {
        GC gc = e.gc;
        
        Color separatorColor = new Color(gc.getDevice(), 205, 205, 205);
        gc.setForeground(separatorColor);
        gc.drawLine(0, 0, getBounds().width, 0);
        separatorColor.dispose();
      }
    });

    updateBreadcrumb();
  }

  public void cursorPositionChanged(TextEditor editor, Point selectionRange) {
    updateBreadcrumb();
  }

  public void updateBreadcrumb() {
    try {
      WodParserCache cache = _editor.getParserCache();
      Point selectionRange = _editor.getSelectedRange();

      FuzzyXMLElement element = null;
      FuzzyXMLDocument document = cache.getHtmlEntry().getModel();
      if (document != null) {
        element = document.getElementByOffset(selectionRange.x);
      }

      Control[] children = getChildren();
      for (int i = children.length - 1; i >= 0; i--) {
        children[i].dispose();
      }

      boolean emptyBreadcrumb = true;
      if (element != null) {
        List<FuzzyXMLNode> elementStack = new LinkedList<FuzzyXMLNode>();
        FuzzyXMLNode currentNode = element;
        do {
          elementStack.add(currentNode);
          currentNode = currentNode.getParentNode();
        } while (currentNode != null);

        for (int i = elementStack.size() - 2; i >= 0; i--) {
          FuzzyXMLNode stackNode = elementStack.get(i);
          if (stackNode instanceof FuzzyXMLElement) {
            emptyBreadcrumb = false;
            FuzzyXMLElement stackElement = (FuzzyXMLElement) stackNode;

            FuzzyXMLElementWithWodElement data;
            String tagName = stackElement.getName();
            boolean isWOTag = WodHtmlUtils.isWOTag(tagName);
            String displayName = null;
            if (isWOTag) {
              BuildProperties buildProperties = (BuildProperties)cache.getProject().getAdapter(BuildProperties.class);
              IWodElement wodElement = WodHtmlUtils.getWodElement(stackElement, buildProperties, true, cache);
              if (wodElement != null) {
                displayName = wodElement.getElementType();
              }
              data = new FuzzyXMLElementWithWodElement(stackElement, wodElement);
            }
            else {
              displayName = tagName;
              data = new FuzzyXMLElementWithWodElement(stackElement, null);
            }

            if (displayName == null) {
              displayName = "<unknown>";
            }

            Label nodeButton = new Label(this, SWT.NONE);
            nodeButton.setData(data);
            nodeButton.setBackground(getBackground());
            nodeButton.setText(displayName);
            nodeButton.addMouseListener(this);
            nodeButton.setCursor(getDisplay().getSystemCursor(SWT.CURSOR_HAND));

            if (!isWOTag) {
              nodeButton.setForeground(getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY));
            }
          }

          if (i > 0) {
            Label arrowLabel = new Label(this, SWT.NONE);
            arrowLabel.setBackground(getBackground());
            arrowLabel.setForeground(getDisplay().getSystemColor(SWT.COLOR_GRAY));
            arrowLabel.setText(">");
          }
        }
        
        if (emptyBreadcrumb) {
          Label emptyLabel = new Label(this, SWT.NONE);
          emptyLabel.setBackground(getBackground());
          emptyLabel.setForeground(getDisplay().getSystemColor(SWT.COLOR_GRAY));
          emptyLabel.setText("...");
        }
        
        layout();
        getParent().layout();
      }
    }
    catch (Throwable t) {
      t.printStackTrace();
    }
  }

  public void mouseDoubleClick(MouseEvent event) {
    Label label = (Label) event.getSource();
    FuzzyXMLElementWithWodElement data = (FuzzyXMLElementWithWodElement) label.getData();
    if (data != null && data.getWodElement() != null) {
      try {
        WodElementTypeHyperlink.toElementTypeHyperlink(data.getWodElement(), _editor.getParserCache()).open();
      }
      catch (Exception e) {
        e.printStackTrace();
      }
    }
  }

  public void mouseDown(MouseEvent event) {
    Label label = (Label) event.getSource();
    FuzzyXMLElementWithWodElement data = (FuzzyXMLElementWithWodElement) label.getData();
    if (data != null && data.getElement() != null) {
      FuzzyXMLElement element = data.getElement();
      int offset = element.getOffset();
      int length = element.getLength();
      _editor.selectAndReveal(offset, length);
    }
  }

  public void mouseUp(MouseEvent event) {
    // DO NOTHING
  }

  public static class FuzzyXMLElementWithWodElement {
    private FuzzyXMLElement _element;
    private IWodElement _wodElement;

    public FuzzyXMLElementWithWodElement(FuzzyXMLElement element, IWodElement wodElement) {
      _element = element;
      _wodElement = wodElement;
    }

    public FuzzyXMLElement getElement() {
      return _element;
    }

    public IWodElement getWodElement() {
      return _wodElement;
    }
  }
}
