package de.tud.kom.p2psim.impl.scenario.simcfg2.configuration.scanner;

import java.io.File;
import java.util.ArrayDeque;
import java.util.Map;
import java.util.HashMap;
import java.lang.reflect.Field;
import java.lang.IllegalAccessException;
import de.tud.kom.p2psim.impl.scenario.simcfg2.configuration.Component;
import de.tud.kom.p2psim.impl.scenario.simcfg2.configuration.SimCfgConfiguration;

/**
 * DO NOT EDIT!
 *
 * This class is generated by JFlex during the maven compile lifecycle
 * using the definition found in simcfg_ast.jflex.
 */

%%

%class Scanner
%unicode
%line
%column
%standalone
%public

%{
	private ScannerUtils utils = new ScannerUtils(null);
    private String temp = "";
    private String name = "";
    private String filename = "";
    private StringBuilder newLines;

    private static enum Symbol {
        COMPONENT_NAME,
        COMPONENT_HEADER,
        COMPONENT_ALIAS,
        COMPONENT_GUARD,
        FLAGS,
        MAPPINGS,
        IMPORTS,
        SETTINGS,
        CONFIGURATION,
        CURLY_OPEN,
        CURLY_CLOSE,
    }

	//ArrayDeque<Integer> symbolStack = ArrayDeque<Integer>();
	StringBuffer string = new StringBuffer();
	ArrayDeque<Integer> stateStack = new ArrayDeque<Integer>() {{
		add(YYINITIAL);
	}};
	Symbol nextSymbol;

	private void changeState(int state) {
		if (state == COMPONENT_HEADER) nextSymbol = Symbol.COMPONENT_NAME;

		stateStack.push(state);
		yybegin(state);
	}

	private void previousState() {
		stateStack.remove();
		yybegin(stateStack.peek());
	}

	private void previousState(int ignore) {
		for (int i = 0; i < ignore; i++) {
			stateStack.remove();
		}
		yybegin(stateStack.peek());
	}

	private String getCurrentStateName() {
		return getStateName(stateStack.peek());
	}

	private void append(String text) {
		string.append(text);
	}

	/*
	private boolean hasSymbol() {
		return !symbolStack.empty();
	}

	private Symbol symbol() {
		return symbolStack.pop();
	}

	private void addSymbol(Symbol symbol) {
		symbolStack.push(symbol);
	}
	*/

	private String get() {
		String tmp = string.toString();
		string.setLength(0);
		return tmp;
	}

	private static Map<Integer, String> stateMap = new HashMap<Integer, String>();

	public static String getStateName(int stateId) {
		if (stateMap.containsKey(stateId)) {
			return stateMap.get(stateId);
		}

		Field[] fields = Scanner.class.getDeclaredFields();
		for (Field field : fields) {
			Class<?> type = field.getType();
			if (type == int.class && java.lang.reflect.Modifier.isStatic(field.getModifiers()) &&
			java.lang.reflect.Modifier.isFinal(field.getModifiers())) {
				try {
					int id = field.getInt(null);
					if (stateId == id) {
						String name = field.getName();
						stateMap.put(stateId, name);
						return name;
					}
					} catch (IllegalAccessException e) {
						return "Unknown";
					}
			}
		}
		return "Unknown";
	}

	public SimCfgConfiguration getConfig() {
	    return utils.getConfig();
	}

	public void setFilenameForDebug(String filename) {
	    this.filename = filename;
	}

  public static SimCfgConfiguration scanFile(File configFile) {
      Scanner scanner = null;
      try {
          scanner = new Scanner( new java.io.FileReader(configFile) );
          scanner.utils = new ScannerUtils(configFile);
          scanner.setFilenameForDebug(configFile.getName());
          while ( !scanner.zzAtEOF ) scanner.yylex();
      }
      catch (java.io.FileNotFoundException e) {
          System.out.println("File not found: \""+configFile.getAbsolutePath()+"\"");
      }
      catch (java.io.IOException e) {
          System.out.println("IO error scanning file \""+configFile.getAbsolutePath()+"\"");
          System.out.println(e);
      }
      catch (Exception e) {
          if (e instanceof RuntimeException) {
              throw (RuntimeException)e;
          } else {
              throw new RuntimeException("", e);
          }
      }

      if (scanner != null) {
          return scanner.getConfig();
      }

      return null;
  }
	
	/*
	public static void main(String argv[]) {
    	if (argv.length == 0) {
    		System.out.println("Usage : java Scanner <inputfile>");
    	} else {
      		for (int i = 0; i < argv.length; i++) {
        		Scanner scanner = null;
    			try {
      				scanner = new Scanner( new java.io.FileReader(argv[i]) );
      				do {
      					Symbol symbol = scanner.next_token();
        				System.out.println("[" + getStateName(symbol.sym) + "] " + symbol.value);
      				} while (!scanner.zzAtEOF);

    			} catch (java.io.FileNotFoundException e) {
      				System.out.println("File not found : \""+argv[i]+"\"");
    			} catch (java.io.IOException e) {
      				System.out.println("IO error scanning file \""+argv[i]+"\"");
      				System.out.println(e);
    			} catch (Exception e) {
      				System.out.println("Unexpected exception:");
      				e.printStackTrace();
    			}
  			}
    	}
  	}*/
  	
%}

LineTerminator       = \r|\n|\r\n
InputCharacter       = [^\r\n]
WhiteSpace           = {LineTerminator} | [ \r\f]
Comment              = {TraditionalComment} | {EndOfLineComment} | {DocumentationComment}
TraditionalComment   = "/*" [^*] ~"*/" | "/*" "*"+ "/"
EndOfLineComment     = "//" {InputCharacter}* {LineTerminator}
DocumentationComment = "/**" {CommentContent} "*"+ "/"
CommentContent       = ( [^*] | \*+ [^/*] )*
Line                 = {InputCharacter}* {LineTerminator}

Identifier = [:jletter:] [:jletterdigit:]*

DevIntegerLiteral = 0 | [1-9][0-9]*

%state FLAGS
%state IMPORTS
%state IMPORT_T
%state IMPORT_EXPR
%state MAPPINGS
%state MAPPING_NAME
%state MAPPING_CLASS
%state SETTINGS
%state SETTING_VALUE
%state CONFIGURATION
%state TOP_COMPONENT
%state INSIDE_TOP_COMPONENT
%state COMPONENT_HEADER
%state COMPONENT
%state ATTRIBUTE_VALUE
%state ATTRIBUTE_STRING
%state ATTRIBUTE_ARRAY
%state ATTRIBUTE_ARRAY_STRING
%state ATTRIBUTE_VARIABLE
%state SETTING_STRING
%state SETTING_ARRAY
%state SETTING_ARRAY_STRING
%state SETTING_VARIABLE
%state VARIATION
%state VARIATION_FLAGS
%state VARIATION_SETTINGS
%state VARIATION_SETTING_VALUE
%state VARIATION_SETTING_STRING
%state VARIATION_SETTING_ARRAY
%state VARIATION_SETTING_ARRAY_STRING
%state VARIATION_NAME

%%
 /* keywords */
<YYINITIAL> "flags"           	 { changeState(FLAGS); }
<YYINITIAL> "imports"            { changeState(IMPORTS); }
<YYINITIAL> "variation"          { changeState(VARIATION); }
<YYINITIAL> \r|\n|\r\n           { }
<YYINITIAL> "mappings"           { changeState(MAPPINGS); }
<YYINITIAL> "settings"			 { changeState(SETTINGS); }
<YYINITIAL> "configuration"      { changeState(CONFIGURATION); }

<FLAGS> {
	[ \t\f]+         { }
	[^\r\n\{\} \t]+  { append(yytext()); }
	\r|\n|\r\n       { if (string.length() > 0) utils.addFlag(get()); }
	\{(\r|\n|\r\n)   { string.setLength(0); }
	\}				 { previousState(); }
}

<IMPORTS> {
	[ \t\f]+         { }
	\{(\r|\n|\r\n)   { string.setLength(0); }
	\'				 { changeState(IMPORT_T); string.setLength(0);  }
	\[				 { changeState(IMPORT_EXPR); string.setLength(0); } 
	\}               { previousState(); }
	\r|\n|\r\n       { }
}

<IMPORT_T> {
	[^\r\n\']+       { append(yytext()); utils.addImport(get());}
	\'               { previousState(); }
}

<IMPORT_EXPR> {
	[^\r\n\[\]]+         { append(yytext()); }
	\](\r|\n|\r\n)       { previousState(); utils.setLastImportGuard(get()); }
}

<MAPPINGS> {
	[ \t\f]+         { }
	\{(\r|\n|\r\n)   { string.setLength(0); }
	\'				 { changeState(MAPPING_NAME); string.setLength(0);  }
	\=\>[ ]*\'		 { changeState(MAPPING_CLASS); string.setLength(0); }
	\}               { previousState(); }
	\r|\n|\r\n       { }
}

<MAPPING_NAME> {
	\'               { previousState(); temp = get(); }
	[^\r\n\']+       { append(yytext()); }
}

<MAPPING_CLASS> {
	\'               { previousState(); utils.addMapping(temp, get()); }
	[^\r\n\']+       { append(yytext()); }
}

<SETTINGS> {
	[ \r\n\t\f]+     { }
	\{(\r\n|\r|\n)   { string.setLength(0); }
	[^ \r\n\t\f\:]+  { append(yytext()); }
	\:			     { changeState(SETTING_VALUE); temp = get(); }
	\}(\r\n|\r|\n)   { previousState(); }
}

<SETTING_VALUE> {
	[ \t\f]+         { }
	\'               { changeState(SETTING_STRING); string.setLength(0); }
	\[			     { changeState(SETTING_ARRAY); string.setLength(0); utils.newArray(); }
	[^ \n\r\[\']+    { append(yytext()); }
	\r\n|\r|\n       { previousState(); if (string.length() > 0) utils.addSetting(temp, get()); }
}

<SETTING_STRING> {
	[^ \r\n\']+      { append(yytext()); }
	\'               { previousState(); utils.addSetting(temp, get()); }
}

<SETTING_ARRAY> {
	[ \t\f]+         { }
	[^ \r\n\t\f,\'\]]+ { append(yytext()); }
	\,               { if (string.length() > 0) utils.addToArray(get()); }
	\'               { changeState(SETTING_ARRAY_STRING); string.setLength(0); }
	\]               { previousState();  if (string.length() > 0) utils.addToArray(get()); string.setLength(0); utils.addSetting(temp, utils.getArray()); }
}

<SETTING_ARRAY_STRING> {
	[^ \r\n\']+      { append(yytext()); }
	\'               { previousState(); utils.addToArray(get()); }
}

<VARIATION>          {
    [ \t\f]+         { }
    \{               { name = get(); }
    "flags"          { changeState(VARIATION_FLAGS); }
    "settings"       { changeState(VARIATION_SETTINGS); }
    \}               { previousState(); }
    \'               { changeState(VARIATION_NAME); }
    \r|\n|\r\n       {  }
}

<VARIATION_NAME> {
	[^\r\n\']+       { append(yytext()); }
	\'               { previousState(); }
}

<VARIATION_FLAGS>    {
    [ \t\f]+         { }
    \{(\r|\n|\r\n)   { }
	[^\r\n\{\} \t]+  { append(yytext()); }
	\r|\n|\r\n       { utils.addVariationFlag(name, get()); }
    \}               { previousState(); }
}

<VARIATION_SETTINGS>    {
	[ \r\n\t\f]+     { }
	\{(\r\n|\r|\n)   { string.setLength(0); }
	[^ \r\n\t\f\:]+  { append(yytext()); }
	\:			     { changeState(VARIATION_SETTING_VALUE); temp = get(); }
	\}(\r\n|\r|\n)   { previousState(); }
}

<VARIATION_SETTING_VALUE> {
	[ \t\f]+         { }
	\'               { changeState(VARIATION_SETTING_STRING); string.setLength(0); }
	\[			     { changeState(VARIATION_SETTING_ARRAY); string.setLength(0); utils.newArray(); }
	[^ \n\r\[\']+    { append(yytext()); }
	\r\n|\r|\n       { previousState(); if (string.length() > 0) utils.addVariationSetting(name, temp, get()); }
}

<VARIATION_SETTING_STRING> {
	[^\r\n\']+       { append(yytext()); }
	\'               { previousState(); utils.addVariationSetting(name, temp, get()); }
}

<VARIATION_SETTING_ARRAY> {
	[ \t\f\,]+       { }
	\'               { changeState(VARIATION_SETTING_ARRAY_STRING); string.setLength(0); }
	\]               { previousState(); string.setLength(0); utils.addVariationSetting(name, temp, utils.getArray()); }
}

<VARIATION_SETTING_ARRAY_STRING> {
	[^\r\n\']+       { append(yytext()); }
	\'               { previousState(); utils.addToArray(get()); }
}

<CONFIGURATION> {
	[ \t\f]+         { }
	\{(\r|\n|\r\n)   { string.setLength(0); }
	\}               { previousState(); }
	"static"         { changeState(TOP_COMPONENT); nextSymbol = Symbol.COMPONENT_NAME; utils.newComponent(Component.ComponentType.STATIC); }
	"factory"        { changeState(TOP_COMPONENT); nextSymbol = Symbol.COMPONENT_NAME; utils.newComponent(Component.ComponentType.FACTORY); }
	"helper"         { changeState(TOP_COMPONENT); nextSymbol = Symbol.COMPONENT_NAME; utils.newComponent(Component.ComponentType.HELPER); }
}

<TOP_COMPONENT> {
	[ \t\f]+         { }
    "redefine"       { utils.setComponentRedefine(); }
    "merge"          { utils.setComponentMerge(); }
	"as"             { nextSymbol = Symbol.COMPONENT_ALIAS; utils.setComponentName(get()); }
	"when"           {
	    Symbol oldSymbol = nextSymbol;
	    nextSymbol = Symbol.COMPONENT_GUARD;
	    if (oldSymbol == Symbol.COMPONENT_NAME) {
	        utils.setComponentName(get());
	    } else if (oldSymbol == Symbol.COMPONENT_ALIAS) {
            utils.setComponentAlias(get());
	    }
	}
	[^ \r\n\{]+      { append(yytext()); }

	\{               {
	    if (nextSymbol == Symbol.COMPONENT_NAME) {
	        utils.setComponentName(get());
	    } else if (nextSymbol == Symbol.COMPONENT_ALIAS) {
	        utils.setComponentAlias(get());
	    } else if (nextSymbol == Symbol.COMPONENT_GUARD) {
	        utils.setComponentGuard(get());
	    }
	}
	\r|\n|\r\n       { string.setLength(0); changeState(INSIDE_TOP_COMPONENT); }

	\}(\r\n|\r|\n)   { previousState(); }
}

<INSIDE_TOP_COMPONENT> {
	[ \t\f\r\n]+         { }

	[^ \r\n\{\}\:\t]+  {

		if (string.length() > 0) {
			String tmp = get();
			append(yytext());
			changeState(COMPONENT_HEADER);
			utils.newComponent(Component.ComponentType.COMPONENT);
			utils.setComponentSetter(tmp);
		} else {
		    append(yytext());
		}
	}
	
	\r|\n|\r\n       { }

	\:               { changeState(ATTRIBUTE_VALUE); temp = get(); }

	\{               { }
	\}(\r|\n|\r\n)   {
	    previousState(2);
	    //utils.walkUpComponent();
    }
}

<ATTRIBUTE_VALUE> {
	[ \t\f]+             { }
	\'                   { changeState(ATTRIBUTE_STRING); }
	\[                   { changeState(ATTRIBUTE_ARRAY); utils.newArray(); }
	[^ \r\n\{\}\'\[\$]+  { append(yytext()); utils.addAttribute(temp, get()); }
	\r|\n|\r\n           { previousState(); }
	\$                   { changeState(ATTRIBUTE_VARIABLE); }
}

<ATTRIBUTE_STRING> {
	[^\r\n\']+       { append(yytext()); }
	\'               { previousState(); utils.addAttribute(temp, get()); }
}

<ATTRIBUTE_ARRAY> {
	[ \t\f]+         { }
	[^ \r\n\t\f,\'\]]+ { append(yytext()); }
	\,               { if (string.length() > 0) utils.addToArray(get()); }
	\'               { changeState(ATTRIBUTE_ARRAY_STRING); string.setLength(0); }
	\]               { previousState(); if (string.length() > 0) utils.addToArray(get()); string.setLength(0); utils.addAttribute(temp, utils.getArray()); }
}

<ATTRIBUTE_ARRAY_STRING> {
	[^\r\n\']+       { append(yytext()); }
	\'               { previousState(); utils.addToArray(get()); }
}

<ATTRIBUTE_VARIABLE> {
	[^ \r\n]+        { append(yytext()); }
	\r|\n|\r\n       { previousState(2); utils.addAttribute(temp, "$" + get()); }
}

<COMPONENT_HEADER> {
	[ \t\f]+       { }

    "redefine"       { utils.setComponentRedefine(); }
    "merge"          { utils.setComponentMerge(); }
	"when"           {
		nextSymbol = Symbol.COMPONENT_GUARD;
		utils.setComponentName(get());
	}

	[^ \r\n\{\}]+     {
		append(yytext());
	}
	
	\{               {
	    if (nextSymbol == Symbol.COMPONENT_NAME) {
            utils.setComponentName(get());
        } else if (nextSymbol == Symbol.COMPONENT_ALIAS) {
            utils.setComponentAlias(get());
        } else if (nextSymbol == Symbol.COMPONENT_GUARD) {
            utils.setComponentGuard(get());
        }
	}

	\}               {
		nextSymbol = null;
	}

	\r|\n|\r\n       {
		if (nextSymbol == null) {
			nextSymbol = Symbol.COMPONENT_NAME;
			utils.walkUpComponent();
			previousState();
		} else {
			changeState(COMPONENT);
		}
	}
}

<COMPONENT> {
	[ \t\f]+         { }

	[^ \r\n\{\}\:\t]+  {

		if (string.length() > 0) {
			String tmp = get();
			append(yytext());
			changeState(COMPONENT_HEADER);
			utils.newComponent(Component.ComponentType.COMPONENT);
			utils.setComponentSetter(tmp);
		} else {
		    append(yytext());
		}
	}

	\r|\n|\r\n       { }

	\:               { changeState(ATTRIBUTE_VALUE); temp = get(); }

	\{(\r|\n|\r\n)   { }
	\}               { previousState(2);
			utils.walkUpComponent(); }
}

 /* error fallback */
\r|\n|\r\n                   { }
{Comment}           {
    char[] ch;
    ch = yytext().toCharArray();
    newLines = new StringBuilder();
    for (char c : ch)
    {
        if (c == '\n')
        {
            newLines.append(c);
        }
    }
}
.|\n   {throw new Error("Illegal character <"+ yytext()+"> (l:" + (yyline+1) + ", c:" + (yycolumn+1) + ") in state " + getCurrentStateName() + " of file " + this.filename); }
