#
# Copyright 2013-2014 Yuichiro Moriguchi
#
# 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.
#
sed "s/@@YEAR@@/$YEAR/g
s/@@OWNER@@/$OWNER/g
s/@@ORGANIZATION@@/$ORGANIZATION/g" /license/$LICENSE

[ -z "$PACKAGE" ] || echo "package $PACKAGE;"
[ -z "$EXTENDS" ] || [ -z "$IMPLEMENTS" ] || cln=':'
print_imports
cat definition

if [ -z "$TYPE" ]
then
  nullsym='-1'
  streamsym='System.IO.TextReader'
  rdt='System.IO.TextReader'
else
  nullsym='null'
  rdt="Iterable<$CTYPE>"
  streamsym='java.util.Iterator'
fi
cat << EOF
public $ABSTRACT class ${CLASSNAME}${TEMPLATE} $cln $EXTENDS $IMPLEMENTS
{

	class TokenException : System.Exception
	{
	}

	interface Engine
	{
		int Step($streamsym __rd, $CTYPE c) $tre;
		bool Accepted();
		int Execaction($CTYPE c);
		bool Isend();
		int Recover(System.Exception e);
		int DeadState();
		int StateSize();
		int FinallyState();
	}
EOF

[ -n "$ENABLE_BACKTRACK" ] && cat << EOF

	class Continuation
	{

		private int STATE;
		private int[] __sts;
		private Engine[] __stk;
		private Object[][] __stv;

		Continuation(int s, int[] sts, Engine[] stk, object[][] stv,
				int len)
		{
			STATE = s;
			__sts = new int[len];
			__stk = new Engine[len];
			__stv = new object[len][];
			System.Array.Copy(sts, __sts, len);
			System.Array.Copy(stk, __stk, len);
			System.Array.Copy(stv, __stv, len);
		}

	}

EOF
cat << EOF
	private const int NinaAccept = -1;
	private const int NinaFail = -9;
	private const int NinaHaltAccept = -91;
	private const int NinaHaltReject = -72;
	private const int NinaYield = -85;
	private const int NinaBegin = -2;
	private const int NinaStacklen = 72;
	const int INITIAL = 0;

	private int STATE;
	private int[] __sts = new int[NinaStacklen];
	private Engine[] __stk = new Engine[NinaStacklen];
	private object[][] __stv = new object[NinaStacklen][];
	private int __slen = 0;

	private $CTYPE unread = $nullsym;
	object yieldObject;
	$streamsym befstream = null;
	System.Exception exception;
EOF
[ -z "$NEWLINEMODE" ] && echo "	private $CTYPE binunread = $nullsym;"
echo
for i in $SUBAUTOMATA
do
  ch1=`replace_strange_char $i`
  echo "	_Engine_${ch1} ENGINE_${ch1};"
done
echo "	${CLASSNAME}()"
echo "	{"
for i in $SUBAUTOMATA
do
  ch1=`replace_strange_char $i`
  echo "		ENGINE_${ch1} = new _Engine_${ch1}(this);"
done
echo "	}"

[ -n "$ENABLE_LOG" ] && cat << EOF
EOF

if [ -n "$ENABLE_BACKTRACK" ]
then
  echo "	private $CTYPE[] __backtrack = null;"
  echo "	private int __backtrack_ptr = -1;"
  echo "	private int __backtrack_len = -1;"
  echo "	private int __backtrack_wptr = -1;"
  echo "	private int __backtrack_sym = -1;"
  echo "	private int[] __backtrack_state = null;"
  echo "	private $CTYPE[] __backtrack_wsym = null;"
  echo "	private int[] __backtrack_ptrs = null;"
  echo "	private Continuation[] __backtrack_cont = null;"
  echo "	private int __backtrack_sptr = -1;"
  echo "	private bool __backtrack_flg = false;"
fi

if [ -n "$ENABLE_LOOKAHEAD" ]
then
  echo "	private int __lookahead_state;"
  echo "	private int __lookahead_mark = -1;"
  echo "	private $CTYPE[] __lookahead = null;"
  echo "	private int __lookahead_ptr = -1;"
  echo "	private $CTYPE[] __lookaheadw = null;"
  echo "	private int __lookaheadw_ptr = -1;"
  echo "	private bool __lookahead_ok = true;"
fi

echo
cat field
echo

echo "	$CTYPE _Read1($streamsym stream)"
echo "	{"
if [ -s read ]; then
  cat read
elif [ -z "$TYPE" ]; then
  echo '		int c;'
  echo
  echo '		c = stream.Read();'
  echo '		return c < 0 ? -1 : c;'
else
  echo "		return ($CTYPE)stream.next();"
fi
echo '	}'

cat << EOF
	private int _Read($streamsym rd) $tre {
		$CTYPE c;

		while(true)
		{
			if(false)
			{
EOF
[ -n "$ENABLE_BACKTRACK" ] && cat << EOF
			}
			else if(__backtrack_sym >= 0) 
			{
				c = __backtrack_sym;
				__backtrack_sym = -1;
				__Logprint("Read Backtracking: ", c);
EOF
cat << EOF
			}
			else if(unread != $nullsym)
			{
				c = unread;
				unread = $nullsym;
				__Logprint("Read unread: ", c);
EOF
[ -n "$ENABLE_LOOKAHEAD" ] && cat << EOF
			}
			else if(__lookahead_ptr >= 0)
			{
				if(__lookahead_ptr < __lookahead.Length)
				{
					c = __lookahead[__lookahead_ptr++];
				}
				else
				{
					__lookahead = null;
					__lookahead_ptr = -1;
					c = _Read(rd);
				}
				__Logprint("Read Lookahead: ", c);
EOF
[ -n "$ENABLE_BACKTRACK" ] && cat << EOF
			}
			else if(__backtrack_ptr >= 0)
			{
				if(__backtrack_ptr < __backtrack_len)
				{
					c = __backtrack[__backtrack_ptr++];
				}
				else
				{
					__backtrack = null;
					__backtrack_ptr = __backtrack_wptr = -1;
					c = _Read(rd);
				}
				__Logprint("Read Backtracking: ", c);
EOF
[ -z "$NEWLINEMODE" ] && cat << EOF
			}
			else if(binunread != $nullsym)
			{
				c = unread;
				unread = $nullsym;
EOF
echo "			}"
echo "			else if((c = _Read1(rd)) != $nullsym)"
echo "			{"
[ -z "$NEWLINEMODE" ] && echo "				$CTYPE d;"
[ -n "$ENABLE_BACKTRACK" ] && echo '				__Write_backtrack(c);'
echo '				__Logprint("Read: ", c);'
[ -z "$NEWLINEMODE" ] && cat <<  EOF
				if(c != '\r')
				{
				}
				else if((d = _Read1(rd)) == '\n')
				{
					c = '\n';
				}
				else
				{
					binunread = d;
				}
EOF
[ -z "$READSLOW" ] && cat << EOF
			}
			else
			{
				__Logprint("Read end-of-file");
			}
EOF
[ -z "$READSLEEP" ] || [ $READSLEEP -lt 0 ] || slp="System.Threading.Thread.Sleep($READSLEEP);"
[ -z "$READSLOW" ] || cat << EOF
			}
			else
			{
				$slp
				continue;
			}
EOF
cat << EOF
			return c;
		}
	}

	void UNGET($CTYPE c)
	{
		unread = c;
		__Logprint("Set unread: ", c);
	}
EOF

[ -n "$ENABLE_LOG" ] && cat << EOF

	private void __Logprint(string s, $CTYPE c)
	{
	}

	private void __Logopen()
	{
	}

	private void __Logprint(string s)
	{
	}

	private void __Logclose()
	{
	}

	private void __Puttrace()
	{
	}

EOF
[ -n "$ENABLE_LOG" ] || cat << EOF

	private void __Logprint(string s, $CTYPE c)
	{
	}

	private void __Logopen()
	{
	}

	private void __Logprint(string s)
	{
	}

	private void __Logclose()
	{
	}

	private void __Puttrace()
	{
	}

EOF

if [ -n "$ENABLE_LOOKAHEAD" ]
then
  cat << EOF
	void LOOKAHEAD($CTYPE c)
	{
		$CTYPE[] a;

		if(__lookaheadw == null)
		{
			__lookahead_state = STATE;
			__lookaheadw = new $CTYPE[72];
			__lookaheadw_ptr = 0;
			__lookaheadw[__lookaheadw_ptr++] = c;
		}
		else if(__lookaheadw_ptr < __lookaheadw.Length)
		{
			__lookaheadw[__lookaheadw_ptr++] = c;
		}
		else
		{
			a = new $CTYPE[__lookaheadw.Length * 2];
			System.Array.Copy(__lookaheadw, a, __lookaheadw.Length);
			__lookaheadw = a;
			__lookaheadw[__lookaheadw_ptr++] = c;
		}
	}

	private void __Copy_lookahead(int p)
	{
		$CTYPE[] a;

		if(__lookahead == null)
		{
			a = new $CTYPE[__lookaheadw_ptr];
		}
		else if(__lookaheadw_ptr < __lookahead.Length)
		{
			a = __lookahead;
		}
		else
		{
			a = new $CTYPE[__lookaheadw_ptr];
		}
		System.Array.Copy(__lookaheadw, a, __lookaheadw_ptr);
		__lookahead = a;
		__lookahead_ptr = p;
		__lookaheadw = null;
		__lookaheadw_ptr = -1;
	}

	void LOOKAHEAD_COMMIT()
	{
		if(__lookahead_mark < 0)
		{
			__lookaheadw = null;
			__lookaheadw_ptr = -1;
		}
		else
		{
			__Copy_lookahead(__lookahead_mark);
		}
		__lookahead_mark = -1;
		__Logprint("Commit Lookahead");
	}

	void LOOKAHEAD_RB()
	{
		__Copy_lookahead(0);
		STATE = __lookahead_state;
		__lookahead_ok = false;
		__lookahead_mark = -1;
		__Logprint("Rollback Lookahead");
	}

	void LOOKAHEAD_MARK()
	{
		__lookahead_mark = __lookaheadw_ptr;
	}

EOF
fi

if [ -n "$ENABLE_BACKTRACK" ]
then
  [ -z "$TYPE" ] && bkt='__backtrack_sym != c'
  [ -z "$TYPE" ] || bkt='!c.equals(__backtrack_sym)'
  cat << EOF
	void SET_BACKTRACK($CTYPE c)
	{
		$CTYPE[] a;
		int[] b;

		if(${bkt})
		{
			if(__backtrack_ptr < 0)
			{
				__backtrack = new $CTYPE[1024];
				__backtrack_wptr = 0;
			}
			else
			{
				__backtrack_wptr = __backtrack_ptr;
			}

			if(__backtrack_wsym == null)
			{
				__backtrack_state = new int[72];
				__backtrack_wsym = new $CTYPE[72];
				__backtrack_ptrs = new int[72];
				__backtrack_cont = new Continuation[72];
				__backtrack_sptr = 0;
			}
			else if(__backtrack_sptr >= __backtrack_wsym.Length)
			{
				a = __backtrack_wsym;
				b = __backtrack_ptrs;
				__backtrack_wsym = new $CTYPE[a.Length];
				__backtrack_ptrs = new int[b.Length];
				System.Array.Copy(a, __backtrack_wsym, a.Length);
				System.Array.Copy(b, __backtrack_ptrs, b.Length);
			}
			__backtrack_state[__backtrack_sptr] = STATE;
			__backtrack_wsym[__backtrack_sptr] = c;
			__backtrack_ptrs[__backtrack_sptr] = __backtrack_wptr;
			__backtrack_cont[__backtrack_sptr] = GETCC();
			__backtrack_sptr++;
			__backtrack_flg = true;
			__Logprint("Set Backtrack: ", c);
		}
	}

	private void __Write_backtrack($CTYPE c)
	{
		$CTYPE[] a;

		if(__backtrack_wptr < 0)
		{
			// do nothing
		}
		else if(__backtrack_wptr < __backtrack.Length)
		{
			__backtrack[__backtrack_wptr++] = c;
		}
		else
		{
			a = new $CTYPE[__backtrack.Length * 2];
			System.Array.Copy(__backtrack, a, __backtrack.Length);
			__backtrack = a;
			__backtrack[__backtrack_wptr++] = c;
		}
	}

	void BACKTRACK_COMMIT()
	{
		if(--__backtrack_sptr <= 0)
		{
			__backtrack_state = null;
			__backtrack_wsym = null;
			__backtrack_ptrs = null;
			__backtrack_cont = null;
			__backtrack_sptr = -1;
			__backtrack_wptr = -1;
			__Logprint("Commit Backtracking");
		}
	}

	bool BACKTRACK($CTYPE c)
	{
		if(__backtrack_wptr >= 0)
		{
			__backtrack_len = __backtrack_wptr;
			__backtrack_wptr = -1;
			__backtrack_sptr--;
			STATE = __backtrack_state[__backtrack_sptr];
			__backtrack_ptr = __backtrack_ptrs[__backtrack_sptr];
			__backtrack_sym = __backtrack_wsym[__backtrack_sptr];
			SETCC(__backtrack_cont[__backtrack_sptr]);
			__Logprint("Rollback Backtracking");
			return true;
		}
		else
		{
			return false;
		}
	}

	Continuation GETCC()
	{
		return new Continuation(STATE, __sts, __stk, __stv, __slen);
	}

	void SETCC(Continuation c)
	{
		int l;

		STATE = c.STATE;
		l = c.__stk.Length;
		l = __stk.Length > l ? __stk.Length : l * 2;
		__sts = new int[l];
		__stk = new Engine[l];
		System.Array.Copy(c.__sts, __sts, c.__sts.Length);
		System.Array.Copy(c.__stk, __stk, c.__stk.Length);
		__slen = c.__sts.Length;
	}

EOF
fi

for i in $SUBAUTOMATA
do
  ch1=`replace_strange_char $i`
  echo
  echo "	class _Engine_${ch1} : Engine"
  echo "	{"
  echo
  echo "	${CLASSNAME}${TEMPLATE} __this__;"
  echo "	public _Engine_${ch1}(${CLASSNAME}${TEMPLATE} o)"
  echo "	{"
  echo "		__this__ = o;"
  echo "	}"
  echo
  echo "	public int Step($streamsym __rd, $CTYPE __c__)"
  echo '	{'
  [ -n "$ENABLE_LOOKAHEAD" ] && echo '		bool __l__ = __this__.__lookahead_ok;'
  [ -n "$ENABLE_LOOKAHEAD" ] && echo
  [ -n "$ENABLE_LOOKAHEAD" ] && echo '		__this__.__lookahead_ok = true;'
  echo '		switch(__this__.STATE)'
  echo '		{'
  print_states $i
  echo '		}'
  echo '		return 0;'
  echo '	}'
  echo
  echo "	public bool Accepted()"
  echo "	{"
  print_accepts $i
  echo '	}'
  echo
  echo "	public int Execaction($CTYPE __c__)"
  echo '	{'
  echo '		switch(__this__.STATE) {'
  print_actions $i
  echo '		}'
  echo '		return 1;'
  echo '	}'
  echo
  echo "	public bool Isend()"
  echo "	{"
  print_isend $i
  echo '	}'
  cat << EOF
		public int Recover(System.Exception e)
		{
EOF
    print_recover $i
    cat << EOF
		}

		public int DeadState()
		{
EOF
    print_deadstate $i
    cat << EOF
		}

		public int StateSize()
		{
EOF
    print_statesize $i
    cat << EOF
		}

		public int FinallyState()
		{
EOF
    print_finallystate $i
    cat << EOF
		}

		public override string ToString()
		{
			return "${ch1}";
		}

	}
EOF
done

cat << EOF

	void __Stkpush(int st, Engine en)
	{
		object[][] c;
		Engine[] b;
		int[] a;

		if(__slen >= __sts.Length)
		{
			a = new int[__sts.Length * 2];
			b = new Engine[__stk.Length * 2];
			c = new object[__stk.Length * 2][];
			System.Array.Copy(__sts, a, __sts.Length);
			System.Array.Copy(__stk, b, __stk.Length);
			System.Array.Copy(__stv, c, __stv.Length);
			__sts = a;
			__stk = b;
			__stv = c;
		}
		__sts[__slen] = st;
		__stk[__slen] = en;
		__stv[__slen++] = new object[en.StateSize()];
	}
EOF

ch1=`replace_strange_char ${MAINNAME}`
echo
cat << EOF
	private int _Parse($streamsym rd, int x, bool? rt)
	{
		bool b = false;
		int c = x, a;
		Engine en;

		b = __stk[__slen - 1].Accepted();
		if(rd == null)
		{
			throw new System.Exception("can not recurse");
		}
		else if((bool)rt)
		{
			switch(__stk[__slen - 1].Execaction(NinaBegin))
			{
			case NinaAccept:
				__Logprint("accept " + __stk[__slen - 1]);
				return NinaAccept;
			case NinaFail:
				__Logprint("match failed: begin");
				__Puttrace();
				return NinaFail;
			case NinaHaltAccept:
				__Logprint("machine halted: begin");
				return NinaHaltAccept;
			case NinaHaltReject:
				__Logprint("machine halted: begin");
				return NinaHaltReject;
			case NinaYield:
				__Logprint("machine halted: begin");
				return NinaYield;
			}
		}

		try
		{
			do
			{
				en = __stk[__slen - 1];
				if(c < 0)
				{
					// do nothing
				}
				else if((a = en.Step(rd, c)) > 0)
				{
					__Logprint("transit to state " + STATE + ": ", c);
					b = en.Accepted();
					switch(en.Execaction(c))
					{
					case NinaAccept:
						__Logprint("accept " + __stk[__slen - 1]);
						UNGET(c);
						return NinaAccept;
					case NinaFail:
						__Logprint("match failed: ", c);
						__Puttrace();
						UNGET(c);
						return NinaFail;
					case NinaHaltAccept:
						__Logprint("machine halted: ", c);
						return NinaHaltAccept;
					case NinaHaltReject:
						__Logprint("machine halted: ", c);
						return NinaHaltReject;
					case NinaYield:
						__Logprint("machine halted: ", c);
						return NinaYield;
					}
EOF
  [ -n "$ENABLE_BACKTRACK" ] && cat << EOF
					if(__backtrack_flg)
					{
						__Write_backtrack(c);
						__backtrack_flg = false;
					}
EOF
  cat << EOF
				}
				else if(a < 0)
				{
					__Logprint("entering " + __stk[__slen - 1]);
					return c;
				}
				else if(b)
				{
					__Logprint("accept " + __stk[__slen - 1]);
					UNGET(c);
					return NinaAccept;
EOF
  [ -n "$ENABLE_LOOKAHEAD" ] && cat << EOF
				}
				else if(__lookaheadw_ptr >= 0)
				{
					__Logprint("match failed: try lookahead: ", c);
					LOOKAHEAD(c);
					LOOKAHEAD_RB();
					b = en.Accepted();
EOF
  [ -n "$ENABLE_BACKTRACK" ] && cat << EOF
				}
				else if(BACKTRACK(c))
				{
					__Logprint("match failed: try backtracking: ", c);
EOF
  cat << EOF
				}
				else
				{
					__Logprint("match failed: ", c);
					__Puttrace();
					UNGET(c);
					return NinaFail;
				}
			} while((c = _Read(rd)) >= 0);
			if(!b)  throw new ${TOKENERROR}();
			return NinaAccept;
		} catch(System.Exception e) {
			UNGET(c);
			throw e;
		}
	}

	private bool? Execfinally()
	{
		int a, b;

		if((a = __stk[__slen - 1].FinallyState()) >= 0)
		{
			b = STATE;  STATE = a;
			switch(__stk[__slen - 1].Execaction(NinaBegin))
			{
			case NinaHaltAccept:
				__slen = 0;
				return true;
			case NinaHaltReject:
				__slen = 0;
				return false;
			}
			STATE = b;
		}
		return null;
	}

	private int Getdeadstate()
	{
		return __stk[__slen - 1].DeadState();
	}

	private int Getrecover(System.Exception e)
	{
		return __stk[__slen - 1].Recover(e);
	}

	${API_ACCESS_MODIFIER} bool Parse($streamsym rd, Engine entry)
	{
		int c = -1;
		bool? b = false;

		__Logopen();
		try
		{
			if(__slen == 0) {
				b = true;
				__Stkpush(0, entry);
			}

			while(true)
			{
				try
				{
					if((c = _Parse(rd, c, b)) >= 0)
					{
						// do nothing
					}
					else if(c == NinaFail)
					{
						while((STATE = Getdeadstate()) < 0)
						{
							if((b = Execfinally()) != null)
							{
								if(!((bool)b))  throw new ${TOKENERROR}();
								return (bool)b;
							}

							if(__slen-- <= 1)
							{
								throw new TokenException();
							}
						}
					}
					else if(c == NinaHaltAccept)
					{
						if((b = Execfinally()) != null)  break;
						__slen = 0;
						b = true;  break;
					}
					else if(c == NinaHaltReject)
					{
						if((b = Execfinally()) != null)  break;
						__slen = 0;
						b = false;  break;
					}
					else if(c == NinaYield)
					{
						return false;
					}
					else if(__slen > 1)
					{
						if((b = Execfinally()) != null)  break;
						STATE = __sts[--__slen];
					}
					else
					{
						if((b = Execfinally()) != null)  break;
						b = __stk[--__slen].Accepted();  break;
					}
				}
				catch(System.Exception e)
				{
					exception = e;
					if(__slen <= 0)  throw e;
					while((STATE = Getrecover(e)) < 0)
					{
						if((b = Execfinally()) != null)  break;
						if(__slen-- <= 1)  throw e;
					}
				}
				b = true;
			}
			if(!((bool)b))  throw new ${TOKENERROR}();
			return (bool)b;
		}
		finally
		{
			__Logclose();
		}
	}

	${API_ACCESS_MODIFIER} bool Parse($streamsym rd)
	{
		return Parse(rd, ENGINE_${ch1});
	}

	${API_ACCESS_MODIFIER} void SetStream($streamsym rd) {
		if(befstream != null)
		{
			throw new System.Exception();
		}
		yieldObject = befstream = rd;
	}

	${API_ACCESS_MODIFIER} object ParseNext() {
		object o;

		if(befstream == null)
		{
			throw new System.Exception();
		}
		else if(yieldObject == null)
		{
			return null;
		}
		else if(Parse(befstream, ENGINE_${ch1}))
		{
			if(yieldObject == null)  throw new System.Exception();
			o = yieldObject;  yieldObject = null;
			return o;
		}
		else
		{
			if(yieldObject == null)  throw new System.Exception();
			return yieldObject;
		}
	}

	${API_ACCESS_MODIFIER} static bool ParseAll($streamsym rd)
	{
		${CLASSNAME} o = new ${CLASSNAME}();

		return o.Parse(rd);
	}

EOF

[ -z "$TYPE" ] && cat << EOF
	${API_ACCESS_MODIFIER} bool Parse(System.IO.Stream rd)
	{
		return Parse(new System.IO.StreamReader(rd));
	}

	${API_ACCESS_MODIFIER} static bool ParseAll(System.IO.Stream rd)
	{
		return ParseAll(new System.IO.StreamReader(rd));
	}

EOF
cat fragment
#cat fragment | replace_action
echo '}'
