#
# 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" ] || extends="extends $EXTENDS"
print_imports
cat definition

echo
if [ -z "$ITERATOR" ]
then
  [ -z "$IMPLEMENTS" ] || implements="implements $IMPLEMENTS"
  echo "public $ABSTRACT class ${CLASSNAME}${TEMPLATE} $extends $implements {"
else
  [ -z "$IMPLEMENTS" ] || implements=",$IMPLEMENTS"
  echo "public $ABSTRACT class ${CLASSNAME}${TEMPLATE} $extends"
  echo "implements Iterable<Object> $implements {"
fi

cat << EOF

	public static final Object ENDMARKER = new Object() {

		public String toString() {
			return "<END>";
		}

	};

	static final int INITIAL = 0;

	private int STATE;
	private Object iseof = null;
EOF
if [ -z "$TYPE" ]
then
  echo "	private $CTYPE unread = -1;"
  echo '	private String matched;'
else
  echo "	private $CTYPE unread = null;"
fi

echo
#cat fragment
cat fragment | replace_action
echo

if [ -z "$TYPE" ]
then
  cat << EOF
	private int _read(java.io.Reader rd) throws java.io.IOException {
		int c;

		if(unread >= 0) {
			c = unread;
			unread = -1;
		} else if((c = rd.read()) < 0) {
			iseof = ENDMARKER;
		}
		return c;
	}
EOF
else
  cat << EOF
	private $CTYPE _read(java.util.Iterator<$CTYPE> itr) {
		$CTYPE c;

		if(unread != null) {
			c = unread;
			unread = null;
			return c;
		} else if(itr.hasNext()) {
			return itr.next();
		} else {
			return null;
		}
	}
EOF
fi

echo
echo "	private int _step($CTYPE" '$c) {'
echo '		switch(STATE) {'
print_states
echo '		}'
echo '		return 0;'
echo '	}'
echo

echo '	private boolean _accepted() {'
print_accepts
echo '	}'
echo

if [ -z "$TYPE" ]
then
  echo '	private Object _gettoken(StringBuffer b) {'
  echo '		String $$ = b.toString();'
else
  echo "	private Object _gettoken(java.util.List<$CTYPE> b) {"
  echo "		java.util.List<$CTYPE>" '$$ = b;'
fi
echo
echo '		switch(STATE) {'
print_token
echo '		default:  return null;'
echo '		}'
echo '	}'
echo

echo '	private boolean _isdead() {'
print_nfadead
echo '	}'

if [ -z "$TYPE" ]
then
  cat << EOF
	public boolean matches(String s) {
		int n;

		STATE = 0;  matched = null;
		for(n = 0; n < s.length(); n++) {
			if(_step(s.charAt(n)) == 0) {
				return false;
			}
		}

		if(_accepted()) {
			matched = s;
			return true;
		} else {
			return false;
		}
	}

	public boolean lookingAt(String s) {
		StringBuffer b = new StringBuffer();
		int n;

		STATE = 0;  matched = null;
		for(n = 0; n < s.length(); n++) {
			if(_accepted()) {
				matched = b.toString();
			}

			if(_step(s.charAt(n)) != 0) {
				// do nothing
			} else {
				return matched != null;
			}
			b.append(s.charAt(n));
		}

		if(_accepted()) {
			matched = b.toString();
			return true;
		} else {
			return matched != null;
		}
	}

	public boolean find(String s) {
		StringBuffer b;
		int n, k;

		STATE = 0;  matched = null;
		for(k = 0; k < s.length(); k++) {
			b = new StringBuffer();
			for(n = k; n < s.length(); n++) {
				if(_accepted()) {
					matched = b.toString();
				}

				if(_step(s.charAt(n)) != 0) {
					// do noting
				} else if(matched == null) {
					break;
				} else {
					return true;
				}
				b.append(s.charAt(n));
			}

			if(_accepted()) {
				matched = b.toString();
				return true;
			} else if(matched != null) {
				return true;
			} else {
				STATE = 0;
			}
		}
		return false;
	}

	public String group() {
		return matched;
	}

	public void reset() {
		STATE = 0;
		iseof = null;
		unread = -1;
		matched = null;
	}

	public Object searchToken(
			java.io.Reader rd) throws java.io.IOException {
		StringBuffer b = new StringBuffer();
		boolean f = false;
		Object o = null;
		int c;

		if(iseof != null)  return iseof;
		while((c = _read(rd)) >= 0) {
			b.appendCodePoint(c);
			if(_step(c) == 0) {
				if(f) {
					unread = c;
					STATE = 0;
					return o;
				} else {
					return null;
				}
			} else if(f = _accepted()) {
				o = _gettoken(b);
				if(_isdead()) {
					b->state = 0;
					return o;
				}
			} else if(_isdead()) {
				return null;
			}
		}
		return f ? o : null;
	}
EOF
else
  cat << EOF
	public void reset() {
		STATE = 0;
		iseof = null;
		unread = null;
	}

	public Object searchToken(java.util.Iterator<$CTYPE> itr) {
		java.util.List<$CTYPE> b = new java.util.ArrayList<$CTYPE>();
		boolean f = false;
		Object o = null;
		$CTYPE c;

		if(iseof != null)  return iseof;
		while((c = _read(itr)) != null) {
			b.add(c);
			if(_step(c) == 0) {
				if(f) {
					unread = c;
					STATE = 0;
					return o;
				} else {
					return null;
				}
			} else if(f = _accepted()) {
				o = _gettoken(b);
				if(_isdead()) {
					b->state = 0;
					return o;
				}
			} else if(_isdead()) {
				return null;
			}
		}
		return f ? o : null;
	}
EOF
fi

# iterator
if [ -n "$ITERATOR" ]
then
  cat << EOF

	private static final Object IGNORE = new Object() {

		public String toString() {
			return "<IGNORE>";
		}

	};
EOF

  echo
  if [ -z "$TYPE" ]
  then
    cat << EOF
	private java.io.Reader rd;

	public ${CLASSNAME}(java.io.Reader rd) {
		this.rd = rd;
	}

	public ${CLASSNAME}(java.io.InputStream rd) {
		this.rd = new java.io.InputStreamReader(rd);
	}

	private Object readtok() {
		Object o;

		try {
			while((o = searchToken(rd)) == IGNORE);
			if(o == null)  throw new ${TOKENERROR}();
			return o;
		} catch(java.io.IOException e) {
			throw new ${TOKENERROR}(e);
		}
	}
EOF
  else
    cat << EOF
	private java.util.Iterator<$CTYPE> rd;

	public ${CLASSNAME}(java.util.Iterator<$CTYPE> rd) {
		this.rd = rd;
	}

	private Object readtok() {
		Object o;

		while((o = searchToken(rd)) == IGNORE);
		if(o == null)  throw new ${TOKENERROR}();
		return o;
	}
EOF
  fi

  cat << EOF

	public java.util.Iterator<Object> iterator() {
		final ${CLASSNAME}${TEMPLATE} a = new ${CLASSNAME}${TEMPLATE}(rd);
		final Object[] o = new Object[1];

		o[0] = a.readtok();
		return new java.util.Iterator<Object>() {

			public boolean hasNext() {
				return o[0] != null;
			}

			public Object next() {
				Object p = o[0];

				if(p == null) {
					throw new java.util.NoSuchElementException();
				}
				o[0] = (p == ENDMARKER) ? null : a.readtok();
				return p;
			}

			public void remove() {
				throw new UnsupportedOperationException();
			}

		};
	}
EOF
fi

echo
echo '}'
