package org.objectstyle.wolips.wodclipse.core.validation;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

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

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.JavaModelException;
import org.objectstyle.wolips.bindings.Activator;
import org.objectstyle.wolips.bindings.preferences.PreferenceConstants;
import org.objectstyle.wolips.bindings.wod.HtmlElementName;
import org.objectstyle.wolips.bindings.wod.IWodElement;
import org.objectstyle.wolips.bindings.wod.IWodModel;
import org.objectstyle.wolips.bindings.wod.SimpleWodBinding;
import org.objectstyle.wolips.bindings.wod.WodBindingValueProblem;
import org.objectstyle.wolips.bindings.wod.WodProblem;
import org.objectstyle.wolips.locate.LocateException;
import org.objectstyle.wolips.variables.BuildProperties;
import org.objectstyle.wolips.wodclipse.core.completion.HtmlCacheEntry;
import org.objectstyle.wolips.wodclipse.core.completion.WodCacheEntry;
import org.objectstyle.wolips.wodclipse.core.completion.WodParserCache;
import org.objectstyle.wolips.wodclipse.core.util.FuzzyXMLWodElement;
import org.objectstyle.wolips.wodclipse.core.util.WodHtmlUtils;

public class TemplateValidator {
  private BuildProperties _buildProperties;
  private WodParserCache _cache;
  private Set<FuzzyXMLElement> _woElements;

  public TemplateValidator(WodParserCache cache) {
    _cache = cache;
    _buildProperties = (BuildProperties)cache.getProject().getAdapter(BuildProperties.class);
  }

  /**
   * Validates the HTML document.
   * @throws Exception 
   */
  public void validate(FuzzyXMLDocument doc) throws Exception {
    visitDocument(doc, true);
  }

  public void visitDocument(FuzzyXMLDocument doc, boolean validate) throws Exception {
    if (doc != null) {
      _woElements = new HashSet<FuzzyXMLElement>();

      final List<InlineWodProblem> inlineProblems = new LinkedList<InlineWodProblem>();

      FuzzyXMLElement rootElement = doc.getDocumentElement();
      //FuzzyXMLElement rootElement = (FuzzyXMLElement) XPath.selectSingleNode(doc.getDocumentElement(), "*");
      visitElement(rootElement, inlineProblems, validate);

      if (validate) {
        HtmlCacheEntry htmlCacheEntry = _cache.getHtmlEntry();
        htmlCacheEntry.getHtmlElementCache().clearCache();
        WodCacheEntry wodCacheEntry = _cache.getWodEntry();
        IFile wodFile = wodCacheEntry.getFile();
        IWodModel wodModel = wodCacheEntry.getModel();
        for (FuzzyXMLElement woElement : _woElements) {
          String woElementName = woElement.getAttributeValue("name");
          int startOffset = woElement.getOffset() + woElement.getNameOffset() + 1;
          int endOffset = startOffset + woElement.getNameLength() - 1;
          HtmlElementName elementName = new HtmlElementName(htmlCacheEntry.getFile(), woElementName, startOffset, endOffset);
          htmlCacheEntry.getHtmlElementCache().addHtmlElement(elementName);

          if (wodModel != null) {
            IWodElement wodElement = wodModel.getElementNamed(woElementName);
            if (wodElement == null) {
              WodProblem undefinedElement = new WodBindingValueProblem(wodElement, new SimpleWodBinding(null, "name", null), "name", "The element '" + woElementName + "' is not defined in " + wodFile.getName(), null, -1, false);
              inlineProblems.add(new InlineWodProblem(woElement, undefinedElement, _cache));
            }
          }
        }

        for (InlineWodProblem wodProblem : inlineProblems) {
          wodProblem.createProblemMarkers();
        }
      }
    }
  }

  private void visitElement(FuzzyXMLElement element, List<InlineWodProblem> inlineProblems, boolean validate) throws CoreException {
    if (element == null) {
      return;
    }

    String elementName = element.getName();
    if (WodHtmlUtils.isInline(elementName)) {
      if (validate) {
        IWodElement wodElement = new FuzzyXMLWodElement(element, _buildProperties);
        if (wodElement != null) {
          boolean validateBindingValues = Activator.getDefault().getPluginPreferences().getBoolean(PreferenceConstants.VALIDATE_BINDING_VALUES);
          //String invalidOGNLSeverity = Activator.getDefault().getPluginPreferences().getString(PreferenceConstants.INVALID_OGNL_SEVERITY_KEY);
          List<WodProblem> wodProblems = new LinkedList<WodProblem>();
          try {
            wodElement.fillInProblems(_cache.getJavaProject(), _cache.getComponentType(), validateBindingValues, wodProblems, WodParserCache.getTypeCache(), _cache.getHtmlEntry().getHtmlElementCache());
            inlineProblems.add(new InlineWodProblem(element, wodProblems, _cache));
          }
          catch (JavaModelException e) {
            try {
              WodParserCache.getTypeCache().clearCacheForType(_cache.getComponentType());
            }
            catch (LocateException e1) {
              e1.printStackTrace();
            }
          }
          catch (Throwable t) {
            Activator activator = Activator.getDefault();
            if (activator != null) {
              activator.log(t);
            }
          }
        }
      }
    }
    else if (WodHtmlUtils.isWOTag(elementName)) {
      String webobjectName = element.getAttributeValue("name");
      if (webobjectName == null) {
        if (validate) {
          inlineProblems.add(new InlineWodProblem(element, "webobject tag missing 'name' attribute", false, _cache));
        }
      }
      else {
        _woElements.add(element);
        if (validate && element.getAttributes().length > 1) {
          inlineProblems.add(new InlineWodProblem(element, "webobject tags should only have a 'name' attribute", true, _cache));
        }
      }
    }
    else {
      // System.out.println("TemplateValidator.validateElement: " + elementName);
    }

    FuzzyXMLNode[] nodes = element.getChildren();
    for (int i = 0; i < nodes.length; i++) {
      if (nodes[i] instanceof FuzzyXMLElement) {
        visitElement((FuzzyXMLElement) nodes[i], inlineProblems, validate);
      }
    }
  }
}
