package hc.adl;

import java.util.HashMap;
import java.util.Map;

/**
 * This class creates an automat based on an ADL description file.
 * 
 * @author hc
 * 
 */
public class AutomatCreator {
	/**
	 * block types
	 */
	protected Map<String, EBlockType> hBTypes;

	/**
	 * where to get subautomats from
	 */
	protected String sSubAutomatPath;

	/**
	 * Initialize global variables
	 * 
	 */
	public AutomatCreator() {
		hBTypes = new HashMap<String, EBlockType>();

		/*
		 * initialize known block in a hash table for faster lookup
		 */
		hBTypes.put("def", EBlockType.def);
		hBTypes.put("if", EBlockType.block_if);
		hBTypes.put("elif", EBlockType.block_elif);
		hBTypes.put("else", EBlockType.block_else);
		hBTypes.put("while", EBlockType.block_while);

		sSubAutomatPath = "";
	}

	/**
	 * Set subautomat load path
	 * 
	 * @param s
	 *            subautomat load path
	 */
	public void SetSubautomatPath(String s) {
		if (s.length() == 0)
			this.sSubAutomatPath = "";
		else
			this.sSubAutomatPath = s + "/";
	}

	/**
	 * deindent a block one step
	 * 
	 * @param s
	 *            block to deindent
	 * @return one step deindendet block
	 */
	public static String StripOneTab(String s) {
		return s.replace("\n\t", "\n");
	}

	/**
	 * strip trans blocks in the top level (not indended)
	 * 
	 * @param s
	 *            block to strip
	 * @return block with stripped top-level trans statements
	 */
	public static String StripTopLevelTranss(String s) {
		int i;
		String lines[];
		String line;
		lines = s.split("\n");

		// iterate linewise
		s = "";
		for (i = 0; i < lines.length; i++) {
			line = lines[i];
			// are we a trans block? are we top-level? no? add us!
			if ((line.charAt(0) == '\t') || (!line.startsWith("trans ")))
				s = s + lines[i] + "\n";
		}

		// return the result
		return s;
	}

	/**
	 * Get rid of empty lines
	 * 
	 * @param s
	 *            block
	 * @return block without empty lines
	 */
	public static String StripEmptyLines(String s) {
		int i;
		String lines[];
		String line;
		lines = s.split("\n");

		// iterate linewise
		s = "";
		for (i = 0; i < lines.length; i++) {
			line = lines[i].trim();

			if (line.trim().length() != 0) {
				// empty line? no? add it!
				if (!line.endsWith("\t"))
					s = s + lines[i] + "\n";
			}
		}

		// return the result
		return s;
	}

	/**
	 * Who needs comments? This function gets rid of them
	 * 
	 * @param s
	 *            block with comments
	 * @return block without comments
	 */
	public static String StripComments(String s) {
		int i, j;
		boolean bInString;
		boolean bFound;
		int iPos;
		String lines[];
		lines = s.split("\n");

		// iterate linewise
		s = "";
		for (i = 0; i < lines.length; i++) {
			// got a non-zero length line
			if (lines[i].length() > 0) {
				bInString = false;
				bFound = false;
				iPos = 0;
				for (j = 0; j < lines[i].length(); j++) {
					switch (lines[i].charAt(j)) {
					case '"':
						bInString = !bInString;
						break;
					case '#':
						if (!bInString) {
							bFound = true;
							iPos = j;
						}
						break;
					default:
						break;
					}

					if (bFound)
						break;
				}
				if (bFound)
					s = s + lines[i].substring(0, iPos) + "\n";
				else
					s = s + lines[i] + "\n";
			}
		}

		// return the result
		return s;
	}

	/**
	 * Builds the automat
	 * 
	 * @param str
	 *            ADL language buffer
	 * @param baseAutomat
	 *            Parent automat
	 * @throws FSMException
	 *             Things can go wrong, after all...
	 */
	public Map<String, IfBlock> BuildAutomat(String str) {
		String sParts[], sParams[];
		String sLine;
		int pos;
		int iLen;
		EBlockType eType;
		HashMap<String, IfBlock> blocks = new HashMap<String, IfBlock>();
		IfBlock ifBlock;

		// rid us of empty lines and comments
		str = StripEmptyLines(StripComments(str));

		// we have something to process
		while (str.length() > 0) {
			pos = 0;
			iLen = str.length();
			/*
			 * get one full block
			 */
			do {
				pos = str.indexOf("\n", pos + 1);
				sLine = str.substring(pos + 1);
			} while ((pos != -1) && (iLen > (pos + 1))
					&& (str.charAt(pos + 1) == '\t'));

			/*
			 * found the last block
			 */
			if (pos == -1) {
				sLine = str;
				str = "";
			}
			/*
			 * found one block
			 */
			else {
				sLine = str.substring(0, pos);
				str = str.substring(pos + 1);
			}

			/*
			 * get the block parts
			 */
			sParts = sLine.split(" ", 2);
			if (sParts.length >= 2) {
				sParams = sParts[1].split(":\n", 2);
				if (sParams.length == 2) {
					sParams[1] = "\n" + sParams[1];
				}
			} else {
				sParams = "".split(" ");
			}

			/*
			 * we found a statement
			 */
			if ((sParts.length != 2) || (sParams.length != 2)) {
				/*
				 * could be a trans or from builtin command (detected by ahack)
				 */
				if (sLine.startsWith("trans ")) {
					sParams = sLine.split(" ");
				} else if (sLine.startsWith("from ")) {
					sParams = sLine.split(" ");
					// HandleInclude(baseAutomat, sParams);
				} else {
					/*
					 * Do, basically, nothing
					 */
				}

			} else {
				/*
				 * we're dealing with a subblock. Do we know the type?
				 */
				if (hBTypes.containsKey(sParts[0])) {
					/*
					 * yes, we do. Which one is it?
					 */
					eType = hBTypes.get(sParts[0]);
					switch (eType) {
					/*
					 * Okay, we found a state block. Find out its name, then
					 * create a sub-automat. De-indent the block and call us
					 * recursively
					 */
					case def:
						ifBlock = new IfBlock();
						HandleIfBlock(StripOneTab(sParams[1]), ifBlock);
						String[] funcSignature = sParams[0].split("[\\(\\)]");
						String funcName = funcSignature[0];
						if (funcSignature.length > 1) {
							String[] funcParams = funcSignature[1].split(",");
							for (int pi = 0; pi < funcParams.length; pi++) {
								funcParams[pi] = funcParams[pi].trim();
							}
							ifBlock.setParamerets(funcParams);
						}
						blocks.put(funcName, ifBlock);
						break;
					}
				}
				/*
				 * The block is not known, don't give a fuck, just do nothing
				 */
				else {
				}
			}
		}

		return blocks;
	}

	/**
	 * Process pre-input parts
	 * 
	 * @param str
	 *            pre-input block
	 * @param myAutomat
	 *            automat to add the preinputs to
	 */
	public void HandleIfBlock(String str, IPreInput myAutomat) {
		String sParts[], sParams[];
		String sLine;
		int pos;
		int iLen;
		EBlockType eType;
		IfBlock myIfBlock = null;

		// get rid of empty lines and comments
		str = StripEmptyLines(StripComments(str));

		// traverse the string
		while (str.length() > 0) {
			/*
			 * find a block
			 */
			pos = 0;
			iLen = str.length();
			do {
				pos = str.indexOf("\n", pos + 1);
				sLine = str.substring(pos + 1);
			} while ((pos != -1) && (iLen > (pos + 1))
					&& (str.charAt(pos + 1) == '\t'));

			/*
			 * last block
			 */
			if (pos == -1) {
				sLine = str;
				str = "";
			}
			/*
			 * found a block
			 */
			else {
				sLine = str.substring(0, pos);
				str = str.substring(pos + 1);
			}

			/*
			 * split block
			 */
			sParts = sLine.split(" ", 2);
			if ((sParts.length >= 2)
					|| (/* hack */sParts[0].startsWith("else:"))) {
				// hack
				if (sParts[0].startsWith("else:")) {
					sParams = sLine.split(":", 2);
					sParts = new String[2];
					sParts[0] = sParams[0];
				} else {
					sParams = sParts[1].split(":\n", 2);
					if (sParams.length == 2) {
						sParams[1] = "\n" + sParams[1];
					}
				}
			} else {
				sParams = "".split(" ");
			}

			/*
			 * simple statement
			 */
			if ((sParts.length != 2) || (sParams.length != 2)) {
				myAutomat.AddPreInput(new IfBlock(null, sLine));
			} else {
				/*
				 * sub-block
				 */
				if (hBTypes.containsKey(sParts[0])) {
					eType = hBTypes.get(sParts[0]);
					switch (eType) {
					case block_if:
					case block_while:
						myIfBlock = new IfBlock();

						myIfBlock.SetCondition(sParams[0]);
						myIfBlock.SetWhile(eType == EBlockType.block_while);
						HandleIfBlock(StripOneTab(sParams[1]), myIfBlock);
						myAutomat.AddPreInput(myIfBlock);
						break;

					case block_elif:
						if (myIfBlock == null) {
							break;
						}
						IfBlock myElifBlock = new IfBlock();
						HandleIfBlock(StripOneTab(sParams[1]), myElifBlock);
						myElifBlock.SetCondition(sParams[0]);
						myIfBlock.AddElifBlock(myElifBlock);
						break;

					case block_else:
						if (myIfBlock == null) {
							break;
						}
						IfBlock myElseBlock = new IfBlock();
						HandleIfBlock(StripOneTab(sParams[1]), myElseBlock);
						myIfBlock.SetElseIfBlock(myElseBlock);
						myIfBlock = null;
						break;
					}
				}
			}
		}
	}

	public static Object makeString(Object object) {
		try {
			String s = (String) object;
			if (s.endsWith(";")) {
				return StringTools.convertToNL(null, s);
			}
			return s;
		} catch (Throwable t) {
			t.printStackTrace();
			return t;
		}
	}
}
