/*
 * Copyright 2000-2016 JetBrains s.r.o.
 *
 * 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 com.intellij.lexer;

import com.intellij.lang.HtmlInlineScriptTokenTypesProvider;
import com.intellij.lang.Language;
import com.intellij.lang.LanguageHtmlInlineScriptTokenTypesProvider;
import com.intellij.lang.LanguageUtil;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.psi.TokenType;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.xml.XmlTokenType;
import org.jetbrains.annotations.NotNull;

/**
 * @author Maxim.Mossienko
 */
public class HtmlLexer extends BaseHtmlLexer {
  private static final IElementType ourInlineStyleElementType;
  private static final IElementType ourInlineScriptElementType;

  public static final String INLINE_STYLE_NAME = "css-ruleset-block";

  static {
    EmbeddedTokenTypesProvider[] extensions = Extensions.getExtensions(EmbeddedTokenTypesProvider.EXTENSION_POINT_NAME);
    IElementType inlineStyleElementType = null;
    for (EmbeddedTokenTypesProvider extension : extensions) {
      if (INLINE_STYLE_NAME.equals(extension.getName())) {
        inlineStyleElementType = extension.getElementType();
        break;
      }
    }
    ourInlineStyleElementType = inlineStyleElementType;
    // At the moment only JS.
    HtmlInlineScriptTokenTypesProvider provider = LanguageHtmlInlineScriptTokenTypesProvider.getInlineScriptProvider(ourDefaultLanguage);
    ourInlineScriptElementType = provider != null ? provider.getElementType() : null;
  }

  private IElementType myTokenType;
  private int myTokenStart;
  private int myTokenEnd;

  @Override
  public void start(@NotNull CharSequence buffer, int startOffset, int endOffset, int initialState) {
    myTokenType = null;
    super.start(buffer, startOffset, endOffset, initialState);
  }

  public void advance() {
    myTokenType = null;
    super.advance();
  }

  public IElementType getTokenType() {
    if (myTokenType!=null) return myTokenType;
    IElementType tokenType = super.getTokenType();

    myTokenStart = super.getTokenStart();
    myTokenEnd = super.getTokenEnd();

    if (hasSeenStyle()) {
      if (hasSeenTag() && isStartOfEmbeddmentTagContent(tokenType)) {
        Language stylesheetLanguage = getStyleLanguage();
        IElementType currentStylesheetElementType;
        if ((stylesheetLanguage == null || LanguageUtil.isInjectableLanguage(stylesheetLanguage)) &&
            (currentStylesheetElementType = getCurrentStylesheetElementType()) != null) {
          myTokenEnd = skipToTheEndOfTheEmbeddment();
          tokenType = currentStylesheetElementType;
        }
      } else if (ourInlineStyleElementType!=null && isStartOfEmbeddmentAttributeValue(tokenType) && hasSeenAttribute()) {
        tokenType = ourInlineStyleElementType;
      }
    } else if (hasSeenScript()) {
      if (hasSeenTag() && isStartOfEmbeddmentTagContent(tokenType)) {
        Language scriptLanguage = getScriptLanguage();
        IElementType currentScriptElementType;
        if ((scriptLanguage == null || LanguageUtil.isInjectableLanguage(scriptLanguage)) &&
            (currentScriptElementType = getCurrentScriptElementType()) != null) {
          myTokenEnd = skipToTheEndOfTheEmbeddment();
          tokenType = currentScriptElementType;
        }
      } else if (hasSeenAttribute() && isStartOfEmbeddmentAttributeValue(tokenType) && ourInlineScriptElementType!=null) {
        myTokenEnd = skipToTheEndOfTheEmbeddment();
        tokenType = ourInlineScriptElementType;
      }
    }

    return myTokenType = tokenType;
  }

  private static boolean isStartOfEmbeddmentAttributeValue(final IElementType tokenType) {
    return tokenType == XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN;
  }

  private static boolean isStartOfEmbeddmentTagContent(final IElementType tokenType) {
    return (tokenType == XmlTokenType.XML_DATA_CHARACTERS ||
            tokenType == XmlTokenType.XML_CDATA_START ||
            tokenType == XmlTokenType.XML_COMMENT_START ||
            tokenType == XmlTokenType.XML_REAL_WHITE_SPACE || tokenType == TokenType.WHITE_SPACE
    );
  }

  public HtmlLexer() {
    this(new MergingLexerAdapter(new FlexAdapter(new _HtmlLexer()), TOKENS_TO_MERGE),true);
  }

  protected HtmlLexer(Lexer _baseLexer, boolean _caseInsensitive) {
    super(_baseLexer,_caseInsensitive);
  }

  protected boolean isHtmlTagState(int state) {
    return state == _HtmlLexer.START_TAG_NAME || state == _HtmlLexer.END_TAG_NAME;
  }

  public int getTokenStart() {
    if (myTokenType!=null) {
      return myTokenStart;
    }
    return super.getTokenStart();
  }

  public int getTokenEnd() {
    if (myTokenType!=null) {
      return myTokenEnd;
    }
    return super.getTokenEnd();
  }
}

@@ -2778,59 +2778,8 @@
 ();%0A
-        IElementType currentStylesheetElementType;%0A
     
@@ -2785,17 +2785,16 @@
      if 
-(
 (stylesh
@@ -2863,34 +2863,33 @@
 sheetLanguage)) 
-&&
+%7B
 %0A            (cu
@@ -2875,35 +2875,99 @@
 e)) %7B%0A          
-  (
+myTokenEnd = skipToTheEndOfTheEmbeddment();%0A          IElementType 
 currentStyleshee
@@ -3010,36 +3010,25 @@
 lementType()
-) != null) %7B
+;
 %0A          m
@@ -3030,73 +3030,93 @@
     
-myT
+t
 oken
-End = skipToTheEndOfTheEmbeddment();%0A          tokenType =
+Type = currentStylesheetElementType == null ? XmlTokenType.XML_DATA_CHARACTERS :
  cur
@@ -3492,55 +3492,8 @@
 ();%0A
-        IElementType currentScriptElementType;%0A
     
@@ -3499,17 +3499,16 @@
      if 
-(
 (scriptL
@@ -3577,18 +3577,17 @@
 guage)) 
-&&
+%7B
 %0A       
@@ -3593,11 +3593,75 @@
     
-  (
+myTokenEnd = skipToTheEndOfTheEmbeddment();%0A          IElementType 
 curr
@@ -3716,20 +3716,9 @@
 pe()
-) != null) %7B
+;
 %0A   
@@ -3728,73 +3728,89 @@
     
-myT
+t
 oken
-End = skipToTheEndOfTheEmbeddment();%0A          tokenType =
+Type = currentScriptElementType == null ? XmlTokenType.XML_DATA_CHARACTERS :
  cur

Revert "don't skip style/script content when there's no token type to parse them as corresponding language"