package org.bench4q.agent.scenario.dfa;

import java.util.ArrayList;
import java.util.List;

import org.bench4q.agent.scenario.dfa.ParamPart.ParamPartType;
import org.bench4q.agent.utils.ParameterParser;

public abstract class State {
	private StringBuilder bufferCommon;
	private StringBuilder bufferForContextVariable;
	private List<ParamPart> result;

	StringBuilder getBufferCommon() {
		return bufferCommon;
	}

	private void setBufferCommon(StringBuilder bufferCommon) {
		this.bufferCommon = bufferCommon;
	}

	StringBuilder getBufferForContextVariable() {
		return bufferForContextVariable;
	}

	private void setBufferForContextVariable(
			StringBuilder bufferForContextVariable) {
		this.bufferForContextVariable = bufferForContextVariable;
	}

	public List<ParamPart> getResult() {
		return result;
	}

	private void setResult(List<ParamPart> result) {
		this.result = result;
	}

	public State() {
		this.setBufferCommon(new StringBuilder());
		this.setBufferForContextVariable(new StringBuilder());
		this.setResult(new ArrayList<ParamPart>());
	}

	protected State convertTo(Class<? extends State> targetState) {
		try {
			State result = targetState.newInstance();
			result.setBufferCommon(this.getBufferCommon());
			result.setBufferForContextVariable(this
					.getBufferForContextVariable());
			result.setResult(this.getResult());
			return result;
		} catch (InstantiationException e) {
			throw new RuntimeException(e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}

	public abstract State digest(char c);

	public void addResult(ParamPart paramPart) {
		this.getResult().add(paramPart);
	}

	public void clearResult() {
		this.getResult().clear();
	}

	public static State getLiteralState() {
		return new LiteralState();
	}

	public static class LiteralState extends State {
		@Override
		public State digest(char c) {
			if (c == '$') {
				if (this.getBufferCommon().length() > 0) {
					this.getResult().add(
							new ParamPart(ParamPartType.STRING, this
									.getBufferCommon().toString(), null, null));
					this.getBufferCommon().setLength(0);
				}
				return convertTo(ContextState.class);
			}
			this.getBufferCommon().append(c);
			return this;
		}
	}

	public static class ContextState extends State {
		@Override
		public State digest(char c) {
			switch (c) {
			case '#':
				this.getResult().add(
						new ParamPart(ParamPartType.SESSION_ID, null, null,
								null));
				return convertTo(LiteralState.class);
			case '{':
				return convertTo(PluginIdState.class);
			case '$': /* $$ is the escape sequence for $ */
				this.getBufferCommon().append(c);
				return convertTo(LiteralState.class);
			default: /* Syntax error */
				throw new RuntimeException("can't digest this tocken");
			}
		}
	}

	public static class PluginIdState extends State {
		/**
		 * When arrive at the PluginIdState, the char before must be '$'.
		 * 
		 * In PluginIdState, if recognize a ':', then should convertTo
		 * VariableState. Else if recognize a '}', then it's a PROPERTY's
		 * terminal
		 * 
		 */
		@Override
		public State digest(char c) {
			switch (c) {
			case ':':
				return convertTo(VariableState.class);
			case '{':
				throw new RuntimeException("can't digest this tocken");
			case '}': /* no ':' means system property */
				this.getResult().add(
						new ParamPart(ParamPartType.PROPERTY, null, null,
								ParameterParser.unescape(this.getBufferCommon()
										.toString())));
				this.getBufferCommon().setLength(0);
				return convertTo(LiteralState.class);
			default:
				this.getBufferCommon().append(c);
				return this;
			}
		}
	}

	public static class VariableState extends State {
		@Override
		public State digest(char c) {
			switch (c) {
			case '}':
				this.getResult().add(
						new ParamPart(ParamPartType.CONTEXT_CALL, null,
								ParameterParser.unescape(this.getBufferCommon()
										.toString()), ParameterParser
										.unescape(this
												.getBufferForContextVariable()
												.toString())));
				this.getBufferCommon().setLength(0);
				this.getBufferForContextVariable().setLength(0);
				return convertTo(LiteralState.class);
			case '{': /* Syntax error */
				throw new RuntimeException("can't digest this tocken");
			default:
				this.getBufferForContextVariable().append(c);
				return this;
			}
		}
	}
}
