/*
 * Copyright 2013 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.
 */
package net.morilib.nina;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import net.morilib.automata.DFA;
import net.morilib.automata.DFAState;
import net.morilib.automata.NFA;
import net.morilib.automata.NFAEdges;
import net.morilib.automata.NFAState;
import net.morilib.automata.TextBound;
import net.morilib.automata.dfa.ConvertedRangeDFA;
import net.morilib.automata.dfa.DFAs;
import net.morilib.automata.nfa.RegexParseException;
import net.morilib.automata.nfa.RegexpParser;
import net.morilib.range.CharSets;
import net.morilib.range.Interval;
import net.morilib.range.IntervalsInt;
import net.morilib.range.Range;
import net.morilib.range.RangeAdder;
import net.morilib.util.TreeSectionMap;
import net.morilib.util.Tuple2;

/**
 *
 *
 * @author MORIGUCHI, Yuichiro 2013/10/14
 */
public class DFABuilder implements NinaAction {

	private static class W {

		private CharSequence wrap;
		private int quote;

		W(CharSequence s, int qc) {
			wrap = s;
			quote = qc;
		}

	}

	private static class RT {

		private String name;

		RT(String s) {
			name = s;
		}

	}

	private static class UE {

		private String edge;

		UE(String s) {
			edge = s;
		}

	}

	private static class SQ {

		private Range r;
		private int num;

		SQ(Range r, int n) {
			this.r = r;
			num = n;
		}

	}

	private static class DY {

		private String var;

		DY(String s) {
			var = s;
		}

	}

	private static class RR {

		private String name;
		private Object delim;

		RR(String s, Object d) {
			name = s;
			delim = d;
		}

	}

	private static class DG {

		private int num;
		private boolean sign;

		DG(int n, boolean s) {
			num = n;
			sign = s;
		}

	}

	private static class DH {

		private int num;

		DH(int n) {
			num = n;
		}

	}

	private static class DO {

		private int num;

		DO(int n) {
			num = n;
		}

	}

	/**
	 * 
	 *
	 *
	 * @author MORIGUCHI, Yuichiro 2013/10/14
	 */
	public static class DBS
	implements DFAState<Object, Object, Void>,
	NFAState, NFAEdges<Object> {

		TreeSectionMap<Interval, Object, DBS> edges;
		TreeSectionMap<Interval, Object, Object> mealy;
		Map<String, DBS> userEdge = null;
		Map<String, Object> userMealy = null;
		boolean initial, accepted, moore;
		String nomFichier, label, recursiveName = null;
		Object mealyOthers = null;
		Object mealyEnd = null;
		DBS others = null;
		DBS end = null;
		DBS recursive = null;
		String dynamicVariable;
		DBS dynamic = null;

		PrintWriter out = null;
		int devoirFermer = 0;

		Collection<DBS> identical = null;

		private void _linkRange(DBS d, Range o, Interval r, Object m) {
			if(d.identical != null) {
				throw new NinaException("invaliddfasubautomaton");
			} else if(edges == null) {
				edges = new TreeSectionMap<Interval, Object, DBS>(
						r, d);
				mealy = new TreeSectionMap<Interval, Object, Object>(
						r, m);
			} else {
				edges.insert(r, d);
				mealy.insert(r, m);
			}
		}

		void linkAlphabet(DBS d, Object o, Object m) {
			Interval r;

			if(identical != null) {
				for(DBS x : identical) {
					x.linkAlphabet(d, o, m);
				}
			} else {
				r = Interval.newPoint(o);
				_linkRange(d, r, r, m);
			}
		}

		void linkAlphabet(DBS d, Range o, Object m) {
			if(identical != null) {
				for(DBS x : identical) {
					x.linkAlphabet(d, o, m);
				}
			} else {
				for(Interval v : o.intervals()) {
					_linkRange(d, o, v, m);
				}
			}
		}

		void linkOthers(DBS d, Object m) {
			if(others != null || recursive != null) {
				throw new NinaNondeterministicException("others");
			} else {
				others = d;
				mealyOthers = m;
			}
		}

		void linkRecursive(DBS d, String name) {
			if(others != null || end != null) {
				throw new NinaNondeterministicException("others");
			} else {
				recursive = d;
				recursiveName = name;
			}
		}

		void linkEnd(DBS d, Object m) {
			if(end != null || recursive != null) {
				throw new NinaNondeterministicException("END");
			} else {
				end = d;
				mealyEnd = m;
			}
		}

		void linkUser(DBS d, String l, Object m) {
			if(userEdge == null) {
				userEdge  = new HashMap<String, DBS>();
				userMealy = new HashMap<String, Object>();
			}
			userEdge.put(l, d);
			userMealy.put(l, m);
		}

		void linkDynamic(DBS d, String var) {
			dynamicVariable = var;
			dynamic = d;
		}

		void sortir(Object o) {
			String s;

			try {
				if(nomFichier == null || nomFichier.equals("")) {
					return;
				} else if(out == null) {
					if(nomFichier.equals("/dev/stdout")) {
						out = new PrintWriter(System.out, true);
						devoirFermer = -1;
					} else if(nomFichier.equals("/dev/stderr")) {
						out = new PrintWriter(System.err, true);
						devoirFermer = -1;
					} else if(nomFichier.equals("/dev/null")) {
						out = null;
					} else {
						out = new PrintWriter(new FileOutputStream(
								nomFichier, devoirFermer > 0), true);
						devoirFermer = 1;
					}
				}
		
				if(out != null && o != null &&
						!(s = o.toString().trim()).equals("")) {
					out.print(s);
					out.flush();
				}
				if(devoirFermer > 0)  out.close();
			} catch(IOException e) {
				// ignore it
				throw new RuntimeException(e);
			}
		}

		void sortirMoore() {
			if(moore)  sortir(label.trim());
		}

		/**
		 * 
		 * @return
		 */
		public DBS getOthers() {
			return others;
		}

		/**
		 * 
		 * @return
		 */
		public Object getMealyOthers() {
			return mealyOthers;
		}

		/**
		 * 
		 * @return
		 */
		public DBS getEnd() {
			return end;
		}

		/**
		 * 
		 * @return
		 */
		public Object getMealyEnd() {
			return mealyEnd;
		}

		/**
		 * 
		 * @return
		 */
		public DBS getRecursive() {
			return recursive;
		}

		/**
		 * 
		 * @return
		 */
		public String getRecursiveName() {
			return recursiveName;
		}

		/**
		 * 
		 * @return
		 */
		public Set<String> getUserEdges() {
			return userEdge != null ? userEdge.keySet() : null;
		}

		/**
		 * 
		 * @param s
		 * @return
		 */
		public DBS getUserEdge(String s) {
			return userEdge != null ? userEdge.get(s) : null;
		}

		/**
		 * 
		 * @param s
		 * @return
		 */
		public Object getUserMealyEdge(String s) {
			return userMealy != null ? userMealy.get(s) : null;
		}

		/**
		 * 
		 * @return
		 */
		public String getDynamicVariable() {
			return dynamicVariable;
		}

		/**
		 * 
		 * @return
		 */
		public DBS getDynamic() {
			return dynamic;
		}

		@Override
		public DFAState<Object, Object, Void> go(Object a) {
			DFAState<Object, Object, Void> d;

			if(edges != null && (d = edges.map(a)) != null) {
				sortir(mealy.map(a));
				if(d instanceof DBS)  ((DBS)d).sortirMoore();
				return d;
			} else if(others == null) {
				return DFAs.<Object, Object, Void>deadState();
			} else {
				sortir(mealyOthers);
				return others;
			}
		}

		@Override
		public DFAState<Object, Object, Void> goInt(int x) {
			return go(Integer.valueOf(x));
		}

		@Override
		public DFAState<Object, Object, Void> goChar(char x) {
			return go(Integer.valueOf(x));
		}

		@Override
		public DFAState<Object, Object, Void> goBound(TextBound b) {
			return DFAs.deadState();
		}

		@Override
		public boolean isInitialState() {
			return initial;
		}

		@Override
		public Set<Object> getAccepted() {
			return accepted ?
					Collections.<Object>singleton(label) :
						Collections.emptySet();
		}

		@Override
		public boolean isDead() {
			return edges == null && others == null;
		}

		@Override
		public boolean isAccepted() {
			return accepted;
		}

		@Override
		public Set<Object> getAlphabets() {
			Set<Object> r = new HashSet<Object>();
			Object a, b;
			int x;

			if(edges != null) {
				for(Interval v : edges.keys()) {
					a = v.getInfimumBound();
					b = v.getSupremumBound();
					if(a.equals(b)) {
						r.add(a);
					} else if(a instanceof Integer) {
						x = ((Integer)a).intValue();
						for(int i = x; v.contains(i); i++) {
							r.add(Integer.valueOf(i));
						}
					} else {
						throw new NinaException("invalidalphabet",
								a);
					}
				}
			}
			return r;
		}

		@Override
		public Iterable<Interval> getAlphabetRanges() {
			return edges != null ?
					edges.keys() : Collections.<Interval>emptySet();
		}

		@Override
		public Object getLabel(Object a) {
			return mealy.map(a);
		}

		@Override
		public Object getLabelInt(int x) {
			return getLabel(Integer.valueOf(x));
		}

		@Override
		public Object getLabelChar(char x) {
			return getLabel(Integer.valueOf(x));
		}

		@Override
		public Set<NFAState> goNext(Object a) {
			DBS d;

			if(edges != null && (d = edges.map(a)) != null) {
				return Collections.singleton((NFAState)d);
			} else if(others == null) {
				return Collections.emptySet();
			} else {
				return Collections.singleton((NFAState)others);
			}
		}

		@Override
		public Set<NFAState> goNext(int a) {
			return goNext(Integer.valueOf(a));
		}

		@Override
		public Set<NFAState> goNext(char a) {
			return goNext(Integer.valueOf(a));
		}

		@Override
		public Set<NFAState> goNextEpsilon() {
			return Collections.emptySet();
		}

		@Override
		public Set<? extends Range> nextAlphabets() {
			Set<Range> x = new HashSet<Range>();
			RangeAdder a = new RangeAdder();

			for(Tuple2<Interval, DBS> t : edges.entries()) {
				x.add(t.getA());
				a.add(t.getA());
			}

			if(others == null) {
				// do nothing
			} else {
				x.add(CharSets.complement(a.toRange()));
			}
			return x;
		}

		@Override
		public boolean isNextEpsilon() {
			return false;
		}

		@Override
		public String toString() {
			return label;
		}

	};

	//
	private static final Object _END = new Object();
	private static final Object _FLOAT = new Object();

	//
	private static final Range DIGIT =
			IntervalsInt.newClosedInterval('0', '9');
	private static final Range SIGN_P =
			IntervalsInt.newClosedInterval('+', '+');
	private static final Range SIGN_N =
			IntervalsInt.newClosedInterval('-', '-');
	private static final Range HEXD1 =
			IntervalsInt.newClosedInterval('A', 'F');
	private static final Range HEXD2 =
			IntervalsInt.newClosedInterval('a', 'f');
	private static final Range HEXD =
			DIGIT.join(HEXD1).join(HEXD2);
	private static final Range OCTD =
			IntervalsInt.newClosedInterval('0', '7');
	private static final Range POINT =
			IntervalsInt.newClosedInterval('.', '.');
	private static final Range CHAR_E =
			IntervalsInt.newClosedInterval('e', 'e').join(
					IntervalsInt.newClosedInterval('E', 'E'));

	//
	private Map<String, DBS> map = new HashMap<String, DBS>();
	private Map<Object, String> inv =
			new IdentityHashMap<Object, String>();
	private Map<String, String> typemap =
			new HashMap<String, String>();
	private Map<String, String> dynmap =
			new HashMap<String, String>();
	private DBS initial, vertex;
	private BuildMachine accepted = null;
	private int mealyType;
	private Object mealy;
	private String name;

	public DBS createState(NinaEvent q) {
		DBS s;

		s = new DBS();
		s.nomFichier = q.getOption("out");
		s.moore = q.getOptionBoolean("moore");
		return s;
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#labelAdded(net.morilib.nina.NinaEvent, java.lang.String, boolean)
	 */
	@Override
	public void labelAdded(NinaEvent q, NinaFrameReader rd,
			boolean accept) {
		StringWriter wr = new StringWriter();
		char[] a = new char[1024];
		int l;

		while((l = rd.read(a)) >= 0)  wr.write(a, 0, l);
		if(vertex == null) {
			// set initial state if it is not set
			vertex = createState(q);
			vertex.initial = true;
			initial = vertex;
		}

		if(vertex.identical == null) {
			vertex.accepted = accept;
		} else {
			for(DBS x : vertex.identical)  x.accepted = accept;
		}
		vertex.label = wr.toString();
	}

	private DBS _linkv(NinaEvent q, DBS x, DBS a, Range r, String o) {
		x.linkAlphabet(a, r, o);
		return a;
	}

	private DBS _linkc(NinaEvent q, DBS x, int c, String o) {
		Range r;
		DBS a;

		r = Interval.newPoint(Integer.valueOf(c));
		a = createState(q);
		return _linkv(q, x, a, r, o);
	}

	private DBS _linki(NinaEvent q, DBS x, int c, String o) {
		Range r;

		r = Interval.newPoint(Integer.valueOf(c));
		r = CharSets.complement(r);
		return _linkv(q, x, vertex, r, o);
	}

	private void _linkdfa1(NinaEvent q, DFA<Object, NFAState, Void> d,
			DBS v, DBS z, Object o, String lh, String lc, boolean en) {
		Collection<DFAState<Object, NFAState, Void>> c;
		Map<DFAState<Object, NFAState, Void>, DBS> m;
		DFAState<Object, NFAState, Void> h;
		String g;
		DBS x, w;

		c = DFAs.allStateRanges(d.getInitialState());
		m = new HashMap<DFAState<Object, NFAState, Void>, DBS>();
		g = lh + lc;
		if(o != null)  g += o.toString();
		for(DFAState<Object, NFAState, Void> y : c) {
			if(y.equals(d.getInitialState())) {
				x = v;
			} else if((x = m.get(y)) == null) {
				m.put(y, x = new DBS());
			}

			if(y.isAccepted()) {
				x.linkOthers(z, g);
				if(en)  x.linkEnd(z, o);
			}

			for(Interval a : y.getAlphabetRanges()) {
				h = y.go(a.getInfimumBound());
				if(h.equals(d.getInitialState())) {
					x.linkAlphabet(v, a, lh);
				} else {
					if((w = m.get(h)) == null) {
						m.put(h, w = new DBS());
					}
					x.linkAlphabet(w, a, lh);
				}
			}
		}
	}

	private void _linkre(NinaEvent q, String re, DBS x, String lh,
			String lc) {
		NFA<Object, NFAState, Void> n;
		DFA<Object, NFAState, Void> d;

		try {
			n = RegexpParser.parse(re);
			d = ConvertedRangeDFA.convertDFA(n);
			_linkdfa1(q, d, x, vertex, mealy, lh, lc, false);
		} catch (RegexParseException e) {
			throw new NinaException("invalidregex");
		}
	}

	private void _linkdq(NinaEvent q, CharSequence s, DBS v) {
		List<DoubleQuoteTokenizer.Elems> b;
		String g, lh, lm, lc;
		boolean z;
		int k, c;
		DBS x, a;
		Range r;

		lh = q.getLanguageOption().getLookahead();
		lm = q.getLanguageOption().getLookaheadMark();
		lc = q.getLanguageOption().getLookaheadCommit();
		b  = DoubleQuoteTokenizer.parse(s.toString());
		for(DoubleQuoteTokenizer.Elems p : b) {
			x = v;
			s = p.getString();
			for(k = 0; k < s.length() - 1; k++) {
				c = s.charAt(k);
				if(x.edges == null) {
					x = _linkc(q, x, c, lh);
				} else if((a = x.edges.map(c)) != null) {
					x = a;
				} else {
					x = _linkc(q, x, c, lh);
				}
			}

			if(p.getRegex() != null) {
				x = _linkc(q, x, s.charAt(k), lh + lm);
				_linkre(q, p.getRegex(), x,
						q.getLanguageOption().getLookahead(),
						q.getLanguageOption().getLookaheadCommit());
			} else if(p.getLookahead() != null) {
				x = _linkc(q, x, s.charAt(k), lh);
				g = lm + lh;
				z = p.isInvertLookahead();
				s = p.getLookahead();
				for(k = 0; k < s.length() - 1; k++) {
					c = s.charAt(k);
					if(z)  _linki(q, x, c, g + lc);
					x = _linkc(q, x, c, g);
					g = lh;
				}
				g = g + lc;
				if(mealy != null)  g += mealy.toString();
				r = Interval.newPoint(Integer.valueOf(s.charAt(k)));
				if(p.isInvertLookahead())  r = CharSets.complement(r);
				x.linkAlphabet(vertex, r, g);
			} else {
				g = lc;
				if(mealy != null)  g += mealy.toString();
				r = Interval.newPoint(Integer.valueOf(s.charAt(k)));
				if(p.isInvertLookahead())  r = CharSets.complement(r);
				x.linkAlphabet(vertex, r, g);
			}
		}
	}

	private void _link1(NinaEvent q, DBS v) {
		CharSequence s;
		int k, b, l;
		Range r;
		DBS x;

		s = ((W)q.getEdge()).wrap;
		b = ((W)q.getEdge()).quote;
		l = s.length() - 1;
		x = v;
		if(b == '"') {
			_linkdq(q, s, v);
		} else {
			for(k = 0; k < l; k++) {
				x = _linkc(q, x, s.charAt(k), null);
			}
			r = Interval.newPoint(Integer.valueOf(s.charAt(k)));
			x.linkAlphabet(vertex, r, mealy);
		}
	}

	private void _linksq(NinaEvent q, DBS v, Range r, int len) {
		NinaLanguageOption p = q.getLanguageOption();
		String s, t = "", m;
		DBS x = v, y;

		m = mealy != null ? mealy.toString() : "";
		s = mealyType == '%' ? p.getInitBuffer(m) : "";
		for(int k = 0; k < len - 1; k++, x = y) {
			y = createState(q);
			if(mealyType == '%') {
				t = p.getAppendBuffer(m);
			} else if(mealyType == '#') {
				t = p.getAssignArray(m, k);
			}

			if(r == null) {
				x.linkOthers(y, s + t);
			} else {
				x.linkAlphabet(y, r, s + t);
			}
			s = "";
		}

		if(mealyType == '%') {
			t = p.getAppendBuffer(m);
		} else if(mealyType == '#') {
			t = p.getAssignArray(m, len - 1);
		} else {
			t = m;
		}

		if(r == null) {
			x.linkOthers(vertex, t);
		} else {
			x.linkAlphabet(vertex, r, t);
		}
	}

	@SuppressWarnings("unchecked")
	private void _linkrepr(NinaEvent q, DBS w, String sub, Object dl) {
		NinaLanguageOption l = q.getLanguageOption();
		DBS x, y, z, v;
		String t, s;
		char[] a;
		int k;

		s = l.getUnget();
		t = mealy != null ? s + mealy.toString() : s;

		v = new DBS();
		w.linkOthers(v, l.getUnget() + l.getInitList());

		if(dl == null || dl.equals("")) {
			v.linkRecursive(vertex, sub);
			vertex.linkOthers(v, s);
		} else if(dl instanceof String) {
			x = new DBS();
			v.linkRecursive(x, sub);
			x.label = l.getAddList();
			x.linkAlphabet(v, dl, null);
			x.linkOthers(vertex, t);
			x.linkEnd(vertex, mealy);
		} else if(dl instanceof DFA) {
			x = new DBS();
			v.linkRecursive(x, sub);
			x.label = l.getAddList();
			_linkdfa1(q, (DFA<Object, NFAState, Void>)dl, x, v, mealy,
					"", s, true);
			x.linkOthers(vertex, t);
			x.linkEnd(vertex, mealy);
		} else if((a = (char[])dl).length < 1) {
			v.linkRecursive(vertex, sub);
			vertex.linkOthers(v, s);
		} else {
			x = z = new DBS();
			v.linkRecursive(z, sub);
			z.label = l.getAddList();
			for(k = 0; k < a.length - 1; k++) {
				x.linkAlphabet(y = new DBS(),
						IntervalsInt.newPoint(a[k]), null);
				x = y;
			}
			x.linkAlphabet(v, IntervalsInt.newPoint(a[k]), null);
			z.linkOthers(vertex, t);
			z.linkEnd(vertex, mealy);
		}
	}

	private void _linkDigitsBig(NinaEvent q, DBS v, boolean s) {
		NinaLanguageOption p = q.getLanguageOption();
		String m, ib, ab, ed;
		DBS x, y = null;

		m  = mealy != null ? mealy.toString() : "";
		ib = p.getInitBuffer(p.getSequenceBufferName());
		ab = p.getAppendBuffer(p.getSequenceBufferName());
		ed = p.getUnget() + p.getBufferToBigInteger(10) + m;

		if(s) {
			y = createState(q);
			v.linkAlphabet(y, SIGN_P, ib);
			v.linkAlphabet(y, SIGN_N, ib + ab);
		}

		x = createState(q);
		v.linkAlphabet(x, DIGIT, ib + ab);
		if(y != null) {
			y.linkAlphabet(x, DIGIT, ab);
		}
		x.linkAlphabet(x, DIGIT, ab);
		x.linkOthers(vertex, ed);
		x.linkEnd(vertex, ed);
	}

	private void _linkDigitsInt(NinaEvent q, DBS v, int d, boolean s) {
		NinaLanguageOption p = q.getLanguageOption();
		String m, ib, ab, ed;
		DBS x, y = v;

		m  = mealy != null ? mealy.toString() : "";
		ib = p.getInitBuffer(p.getSequenceBufferName());
		ab = p.getAppendBuffer(p.getSequenceBufferName());
		ed = p.getBufferToInteger(10) + m;

		if(s) {
			y = createState(q);
			v.linkAlphabet(y, SIGN_P, ib);
			v.linkAlphabet(y, SIGN_N, ib + ab);
			v.linkOthers(y, ib);
			ib = "";
		}

		if(d > 1) {
			x = createState(q);
			y.linkAlphabet(x, DIGIT, ib + ab);
			for(int i = 1; i < d - 1; i++) {
				y = x;  x = createState(q);
				y.linkAlphabet(x, DIGIT, ab);
				y.linkOthers(vertex, p.getUnget() + ed);
				y.linkEnd(vertex, p.getUnget() + ed);
			}
			x.linkAlphabet(vertex, DIGIT, ab + ed);
			x.linkOthers(vertex, p.getUnget() + ed);
			x.linkEnd(vertex, p.getUnget() + ed);
		} else {
			y.linkAlphabet(vertex, DIGIT, ib + ab + ed);
		}
	}

	private void _linkHexBig(NinaEvent q, DBS v) {
		NinaLanguageOption p = q.getLanguageOption();
		String m, ib, ab, ed;
		DBS x;

		m  = mealy != null ? mealy.toString() : "";
		ib = p.getInitBuffer(p.getSequenceBufferName());
		ab = p.getAppendBuffer(p.getSequenceBufferName());
		ed = p.getUnget() + p.getBufferToBigInteger(16) + m;
		x  = createState(q);
		v.linkAlphabet(x, HEXD, ib + ab);
		x.linkAlphabet(x, HEXD, ab);
		x.linkOthers(vertex, ed);
		x.linkEnd(vertex, ed);
	}

	private void _linkHexInt(NinaEvent q, DBS v, int d) {
		NinaLanguageOption p = q.getLanguageOption();
		String m, ib, ab, ed;
		DBS x, y = v;

		m  = mealy != null ? mealy.toString() : "";
		ib = p.getInitBuffer(p.getSequenceBufferName());
		ab = p.getAppendBuffer(p.getSequenceBufferName());
		ed = p.getBufferToInteger(16) + m;

		x = createState(q);
		y.linkAlphabet(x, HEXD, ib + ab);
		for(int i = 1; i < d / 4 - 1; i++) {
			y = x;  x = createState(q);
			y.linkAlphabet(x, HEXD, ab);
			y.linkOthers(vertex, p.getUnget() + ed);
			y.linkEnd(vertex, p.getUnget() + ed);
		}
		x.linkAlphabet(vertex, HEXD, ab + ed);
		x.linkOthers(vertex, p.getUnget() + ed);
		x.linkEnd(vertex, p.getUnget() + ed);
	}

	private void _linkOctBig(NinaEvent q, DBS v) {
		NinaLanguageOption p = q.getLanguageOption();
		String m, ib, ab, ed;
		DBS x;

		m  = mealy != null ? mealy.toString() : "";
		ib = p.getInitBuffer(p.getSequenceBufferName());
		ab = p.getAppendBuffer(p.getSequenceBufferName());
		ed = p.getUnget() + p.getBufferToBigInteger(8) + m;
		x  = createState(q);
		v.linkAlphabet(x, OCTD, ib + ab);
		x.linkAlphabet(x, OCTD, ab);
		x.linkOthers(vertex, ed);
		x.linkEnd(vertex, ed);
	}

	private void _linkOctInt(NinaEvent q, DBS v, int d) {
		NinaLanguageOption p = q.getLanguageOption();
		String m, ib, ab, ed;
		DBS x, y = v;

		m  = mealy != null ? mealy.toString() : "";
		ib = p.getInitBuffer(p.getSequenceBufferName());
		ab = p.getAppendBuffer(p.getSequenceBufferName());
		ed = p.getBufferToInteger(8) + m;

		x = createState(q);
		v.linkAlphabet(x,
				IntervalsInt.newClosedInterval(
						'0', '0' + (1 << (d % 3)) - 1),
				ib + ab);
		y = x;  x = createState(q);
		v.linkAlphabet(x,
				IntervalsInt.newClosedInterval(
						'0' + (1 << (d % 3)), '7'),
				ib + ab);
		y.linkAlphabet(x, OCTD, ab);
		y.linkOthers(vertex, p.getUnget() + ed);
		y.linkEnd(vertex, p.getUnget() + ed);
		for(int i = 2; i < d / 3; i++) {
			y = x;  x = createState(q);
			y.linkAlphabet(x, OCTD, ab);
			y.linkOthers(vertex, p.getUnget() + ed);
			y.linkEnd(vertex, p.getUnget() + ed);
		}
		x.linkAlphabet(vertex, OCTD, ab + ed);
		x.linkOthers(vertex, p.getUnget() + ed);
		x.linkEnd(vertex, p.getUnget() + ed);
	}

	private void _linkFloat(NinaEvent q, DBS v) {
		NinaLanguageOption p = q.getLanguageOption();
		String m, ib, ab, e1, e2;
		DBS sint, ssgn, sfpt, sdec, sep1, sep2, sepd;

		m  = mealy != null ? mealy.toString() : "";
		ib = p.getInitBuffer(p.getSequenceBufferName());
		ab = p.getAppendBuffer(p.getSequenceBufferName());
		e1 = p.getUnget() + p.getBufferToNumberInteger() + m;
		e2 = p.getUnget() + p.getBufferToNumberDouble() + m;

		ssgn = createState(q);
		v.linkAlphabet(ssgn, SIGN_P, ib);
		v.linkAlphabet(ssgn, SIGN_N, ib + ab);
		sint = createState(q);
		v.linkAlphabet(sint, DIGIT, ib + ab);
		ssgn.linkAlphabet(sint, DIGIT, ab);
		sint.linkAlphabet(sint, DIGIT, ab);
		sint.linkOthers(vertex, e1);
		sint.linkEnd(vertex, e1);
		sfpt = createState(q);
		v.linkAlphabet(sfpt, POINT, ib + ab);
		ssgn.linkAlphabet(sfpt, POINT, ab);
		sint.linkAlphabet(sfpt, POINT, ab);
		sdec = createState(q);
		sfpt.linkAlphabet(sdec, DIGIT, ab);
		sdec.linkAlphabet(sdec, DIGIT, ab);
		sdec.linkOthers(vertex, e2);
		sdec.linkEnd(vertex, e2);
		sep1 = createState(q);
		sint.linkAlphabet(sep1, CHAR_E, ab);
		sdec.linkAlphabet(sep1, CHAR_E, ab);
		sep2 = createState(q);
		sep1.linkAlphabet(sep2, SIGN_P, ab);
		sep1.linkAlphabet(sep2, SIGN_N, ab);
		sepd = createState(q);
		sep2.linkAlphabet(sepd, DIGIT, ab);
		sep1.linkAlphabet(sepd, DIGIT, ab);
		sepd.linkAlphabet(sepd, DIGIT, ab);
		sepd.linkOthers(vertex, e2);
		sepd.linkEnd(vertex, e2);
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#link(net.morilib.nina.NinaEvent, java.lang.Object)
	 */
	@Override
	public void link(NinaEvent q, Object ver) {
		DBS v = vertex;
		Object o;

		vertex = (ver != null) ? (DBS)ver : createState(q);
		if((o = q.getEdge()) == null) {
			v.linkOthers(vertex, q.getLanguageOption().getUnget());
			v.linkEnd(vertex, null);
		} else if(o instanceof BuildMachine) {
			v.linkRecursive(vertex, ((BuildMachine)o).name);
		} else if(o instanceof RT) {
			v.linkRecursive(vertex, ((RT)o).name);
		} else if(o instanceof W) {
			_link1(q, v);
		} else if(o instanceof SQ) {
			_linksq(q, v, ((SQ)o).r, ((SQ)o).num);
		} else if(o.equals(CharSets.ALL_CHAR)) {
			v.linkOthers(vertex, mealy);
		} else if(o instanceof Range) {
			v.linkAlphabet(vertex, (Range)o, mealy);
		} else if(o instanceof UE) {
			v.linkUser(vertex, ((UE)o).edge, mealy);
		} else if(o instanceof DY) {
			v.linkDynamic(vertex, ((DY)o).var);
		} else if(o instanceof RR) {
			_linkrepr(q, v, ((RR)o).name, ((RR)o).delim);
		} else if(o instanceof DG) {
			if(((DG)o).num < 0) {
				_linkDigitsBig(q, v, ((DG)o).sign);
			} else {
				_linkDigitsInt(q, v, ((DG)o).num, ((DG)o).sign);
			}
		} else if(o instanceof DH) {
			if(((DH)o).num < 0) {
				_linkHexBig(q, v);
			} else {
				_linkHexInt(q, v, ((DH)o).num);
			}
		} else if(o instanceof DO) {
			if(((DO)o).num < 0) {
				_linkOctBig(q, v);
		} else {
				_linkOctInt(q, v, ((DO)o).num);
			}
		} else if(o == _FLOAT) {
			_linkFloat(q, v);
		} else if(o == _END) {
			v.linkEnd(vertex, mealy);
		} else {
			v.linkAlphabet(vertex, o, mealy);
		}
		mealy = null;
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#getLabel()
	 */
	@Override
	public String getLabel() {
		return vertex.label;
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#getVertex()
	 */
	@Override
	public Object getVertex() {
		return vertex;
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#setVertex(java.lang.Object)
	 */
	@Override
	public void setVertex(Object o) {
		vertex = (DBS)o;
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#doneBlockSearching(net.morilib.nina.NinaEvent)
	 */
	@Override
	public void doneBlockSearching(NinaEvent q) {
		if(q.getScratch() instanceof NinaParser.Z) {
			vertex = (DBS)((NinaParser.Z)q.getScratch()).vertex;
		} else {
			vertex = (DBS)q.getScratch();
		}
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#setEdge(net.morilib.nina.NinaEvent, java.lang.Object)
	 */
	@Override
	public void setEdge(NinaEvent q, Object o) {
		q.setEdge(o);
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#setEdgeCharSequence(net.morilib.nina.NinaEvent, java.lang.CharSequence)
	 */
	@Override
	public void setEdgeCharSequence(NinaEvent q, CharSequence o,
			int qc) {
		q.setEdge(new W(o, qc));
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#setEdgeResource(net.morilib.nina.NinaEvent, net.morilib.nina.NinaParser, java.lang.String)
	 */
	@Override
	public void setEdgeResource(NinaEvent q,
			NinaParser p,
			String s,
			Map<String, String> map,
			NinaSubautomata sub) {
		String t = Nina.getFAName(sub, s);

		if(sub.containsKey(t)) {
			q.setEdge(new RT(t));
		} else {
			sub.put(t, null);
			q.setEdge(p.compileSubautomaton(s, new DFABuilder(), map,
					sub, null).getMachine());
		}
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#setEdgeNFA(net.morilib.nina.NinaEvent, net.morilib.nina.NinaParser, net.morilib.automata.NFA)
	 */
	@Override
	public void setEdgeNFA(NinaEvent q, NinaParser p,
			NFA<Object, NFAState, Void> s) {
		throw new NinaException("cannotaddnfa");
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#setEdgeEnd(net.morilib.nina.NinaEvent)
	 */
	@Override
	public void setEdgeEnd(NinaEvent q) {
		q.setEdge(_END);
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#setEdgeGrammar(net.morilib.nina.NinaEvent, java.lang.String)
	 */
	@Override
	public void setEdgeGrammar(NinaEvent q, String name) {
		q.setEdge(new RT("g:" + name));
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#accept()
	 */
	@Override
	public NinaAction accept(String name) {
		if(accepted == null) {
			this.name = name;
			accepted = new BuildMachine(name, initial);
		}
		return this;
	}

	public DFA<Object, Object, Void> getMachine() {
		return accepted;
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#setMealyEdge(int)
	 */
	@Override
	public void setMealyEdge(int c) {
		mealy = Character.valueOf((char)c);
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#setMealyEdge(java.lang.Object)
	 */
	@Override
	public void setMealyEdge(Object o) {
		mealy = o;
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#setPriority(int)
	 */
	@Override
	public void setPriority(int p) {
		// do nothing
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#putStateByLabel(java.lang.String, java.lang.Object)
	 */
	@Override
	public void putStateByLabel(String l, Object s) {
		map.put(l, (DBS)s);
		inv.put(s, l);
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#getStateByLabel(java.lang.String)
	 */
	@Override
	public DBS getStateByLabel(String l) {
		return map.get(l);
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#getLabelByState(java.lang.Object)
	 */
	@Override
	public String getLabelByState(Object s) {
		return inv.get(s);
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#getDeadState()
	 */
	@Override
	public Object getDeadState() {
		return getStateByLabel(NinaParser.DEADSYM);
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#setStartState(java.lang.Object)
	 */
	@Override
	public void setStartState(Object o) {
		vertex = (DBS)o;
		vertex.initial = true;
		initial = vertex;
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#setUserEdge(net.morilib.nina.NinaEvent, java.lang.String)
	 */
	@Override
	public void setUserEdge(NinaEvent q, String o) {
		q.setEdge(new UE(o));
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#getLabels()
	 */
	@Override
	public Set<String> getLabels() {
		return map.keySet();
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#putTypeByLabel(java.lang.String, java.lang.Object)
	 */
	@Override
	public void putTypeByLabel(String l, String s) {
		typemap.put(l, s);
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#getTypeByLabel(java.lang.String)
	 */
	@Override
	public String getTypeByLabel(String l) {
		return typemap.get(l);
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#getName()
	 */
	@Override
	public String getName() {
		return name;
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#setEdgeSequence(net.morilib.nina.NinaEvent)
	 */
	@Override
	public void setEdgeSequence(NinaEvent q, Range r, int len) {
		q.setEdge(new SQ(r, len));
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#setMealyEdgeType(int)
	 */
	@Override
	public void setMealyEdgeType(int c) {
		mealyType = c;
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#setAccept(boolean)
	 */
	@Override
	public void setAccept(boolean accept) {
		vertex.accepted = accept;
	}

	@Override
	public void setEdgeDynamic(NinaEvent q, String var) {
		q.setEdge(new DY(var));
		dynmap.put(var, var);
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#getDynamicTransitMap()
	 */
	@Override
	public Map<String, String> getDynamicTransitMap() {
		return Collections.unmodifiableMap(dynmap);
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#setEdgeRepeatedResource(net.morilib.nina.NinaEvent, java.lang.String, java.lang.Object)
	 */
	@Override
	public void setEdgeRepeatedResource(NinaEvent q, String s,
			Object d) {
		q.setEdge(new RR(s, d));
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#empty(net.morilib.nina.NinaEvent, net.morilib.nina.NinaConsole)
	 */
	@Override
	public NinaAction empty(NinaEvent q, String name) {
		setStartState(createState(q));
		return accept(name);
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#setEdgeDigits(net.morilib.nina.NinaEvent)
	 */
	@Override
	public void setEdgeDigits(NinaEvent q, int digits, boolean sign) {
		q.setEdge(new DG(digits, sign));
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#setEdgeHexadecimal(net.morilib.nina.NinaEvent, int)
	 */
	@Override
	public void setEdgeHexadecimal(NinaEvent q, int bits) {
		q.setEdge(new DH(bits));
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaAction#setEdgeOctal(net.morilib.nina.NinaEvent, int)
	 */
	@Override
	public void setEdgeOctal(NinaEvent q, int bits) {
		q.setEdge(new DO(bits));
	}

	@Override
	public void setEdgeFloat(NinaEvent q) {
		q.setEdge(_FLOAT);
	}

}
