/*
 * 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.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.morilib.nina.lint.LintCells;

/**
 *
 *
 * @author MORIGUCHI, Yuichiro 2013/10/15
 */
public class Quadro implements NinaEvent {

	/**
	 * 
	 *
	 *
	 * @author MORIGUCHI, Yuichiro 2013/10/02
	 */
	public static enum Direction {
		WEST, EAST, NORTH, SOUTH, CR
	}

	//
	private static final String LOPT = "targetLanguage";
	private static final Pattern PTN1 = Pattern.compile(
			"--[ \t]+(.+)[ \t]+--");
	private static final Pattern PTN2 = Pattern.compile("%.*\\{");

	//
	private static boolean iswarning = false;
	public static PrintStream errst = null;

	//
	static final int EQ_TO_LEFT = -1;
	static final int BLANKX = -2;
	static final int BLANKY = -3;
	static final int E2 = -100;
	static final int W2 = -101;
	static final int N2 = -102;
	static final int S2 = -103;
	static final int E3 = -200;
	static final int W3 = -201;
	static final int N3 = -202;
	static final int S3 = -203;
	static final int DONE = -300;
	static final int ENTRY = -301;
	static final int WARP2 = -400;
	static final int WARP3 = -402;
	static final int FRAME_N = -501;
	static final int FRAME_A = -502;
	static final int BRANCH   = -5000000;
	static final int BRANCH_N = 1000;
	static final int BRANCH_E =  100;
	static final int BRANCH_S =   10;
	static final int BRANCH_W =    1;

	//
	Map<String, String> aliases = new HashMap<String, String>();
	Map<String, String> options;
	Map<String, StringBuffer> buffers =
			new HashMap<String, StringBuffer>();
	List<String> imports = new ArrayList<String>();
	String fragment, type;

	//
	private int[][] quadro;
	private Object[][] scratch, scratch2;
	private String[][] labels, ltypes;
	private String[][] visited;
	private String[][] visiting;
	private int curx = 0, cury = 0;
	private Object edge;
	private Map<String, Quadro> subquadro;
	private LintCells lint;
	String rootResource;
	String rootPackage;
	String name;

	//
	private NinaLanguageOption langopt = null;

	//
	Quadro(String n, List<int[]> q, String f, Map<String, Quadro> sub,
			Map<String, String> opts) {
		options   = opts;
		name      = n;
		fragment  = f;
		quadro    = new int[q.size()][];
		scratch   = new Object[q.size()][];
		scratch2  = new Object[q.size()][];
		labels    = new String[q.size()][];
		ltypes    = new String[q.size()][];
		visited   = new String[q.size()][];
		visiting  = new String[q.size()][];
		subquadro = sub;
		for(int i = 0; i < q.size(); i++) {
			quadro[i]   = q.get(i);
			scratch[i]  = new Object[q.get(i).length];
			scratch2[i] = new Object[q.get(i).length];
			labels[i]   = new String[q.get(i).length];
			ltypes[i]   = new String[q.get(i).length];
			visited[i]  = new String[q.get(i).length];
			visiting[i] = new String[q.get(i).length];
		}
		lint = new LintCells(0, q);
	}

	//
	Quadro(String n, int[][] q, String f, Map<String, Quadro> sub,
			Map<String, String> opts) {
		options   = opts;
		name      = n;
		fragment  = f;
		quadro    = new int[q.length][];
		scratch   = new Object[q.length][];
		scratch2  = new Object[q.length][];
		labels    = new String[q.length][];
		ltypes    = new String[q.length][];
		visited   = new String[q.length][];
		visiting  = new String[q.length][];
		subquadro = sub;
		for(int i = 0; i < q.length; i++) {
			quadro[i]   = new int[q[i].length];
			scratch[i]  = new Object[q[i].length];
			scratch2[i] = new Object[q[i].length];
			labels[i]   = new String[q[i].length];
			ltypes[i]   = new String[q[i].length];
			visited[i]  = new String[q[i].length];
			visiting[i] = new String[q[i].length];
			for(int j = 0; j < q[i].length; j++) {
				quadro[i][j] = q[i][j];
			}
		}
		lint = new LintCells(0, q);
	}

	//
	Quadro(String n, List<int[]> q, String f,
			Map<String, String> opts) {
		this(n, q, f, null, opts);
	}

	/**
	 * 
	 * @param options
	 */
	public Quadro(Map<String, String> options) {
		this.options = options;
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaEvent#getRootResource()
	 */
	public String getRootResource() {
		return rootResource;
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaEvent#getRootPackage()
	 */
	public String getRootPackage() {
		return rootPackage;
	}

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

	/**
	 * 
	 * @param x
	 */
	public void setRootResource(String x) {
		rootResource = x;
	}

	/**
	 * 
	 * @param x
	 */
	public void setRootPackage(String x) {
		rootPackage = x;
	}

	/**
	 * @return the edge
	 */
	public Object getEdge() {
		return edge;
	}

	/**
	 * @param edge the edge to set
	 */
	public void setEdge(Object edge) {
		this.edge = edge;
	}

	/**
	 * 
	 * @return
	 */
	public boolean isInBounds() {
		return (cury >= 0 && cury < quadro.length) &&
				(curx >= 0 && curx < quadro[cury].length);
	}

	/**
	 * 
	 * @return
	 */
	public int get() {
		if(cury < 0 || cury >= quadro.length) {
			return BLANKY;
		} else if(curx < 0 || curx >= quadro[cury].length) {
			return BLANKX;
		} else {
			return quadro[cury][curx];
		}
	}

	/**
	 * 
	 * @return
	 */
	public Object getScratch() {
		if(cury < 0 || cury >= quadro.length) {
			return null;
		} else if(curx < 0 || curx >= quadro[cury].length) {
			return null;
		} else {
			return scratch[cury][curx];
		}
	}

	/**
	 * 
	 * @return
	 */
	public Object getScratch2() {
		if(cury < 0 || cury >= quadro.length) {
			return null;
		} else if(curx < 0 || curx >= quadro[cury].length) {
			return null;
		} else {
			return scratch2[cury][curx];
		}
	}

	/**
	 * 
	 * @return
	 */
	public boolean isBlankX() {
		return get() == BLANKX;
	}

	/**
	 * 
	 * @return
	 */
	public boolean isBlankY() {
		return get() == BLANKY;
	}

	/**
	 * 
	 * @return
	 */
	public boolean isLetter() {
		int c = get();

		return c >= 0 && Character.isLetter(c);
	}

	/**
	 * 
	 * @return
	 */
	public boolean isDigit() {
		int c = get();

		return c >= 0 && Character.isDigit(c);
	}

	/**
	 * 
	 * @return
	 */
	public boolean isArrow() {
		return "^><v".indexOf(get()) >= 0;
	}

	/**
	 * 
	 * @return
	 */
	public boolean isEntry() {
		return get() == ENTRY;
	}

	/**
	 * 
	 * @return
	 */
	public boolean isDone() {
		return get() == DONE;
	}

	/**
	 * 
	 * @return
	 */
	public boolean isFrameBefore() {
		return "*@=&".indexOf(get()) >= 0;
	}

	/**
	 * 
	 */
	public void transformFrame() {
		if(get() == '@' || get() == '&') {
			set(FRAME_A);
		} else if(get() == '*' || get() == '=') {
			set(FRAME_N);
		}
	}

	/**
	 * 
	 * @return
	 */
	public boolean isFrame() {
		return get() == FRAME_N || get() == FRAME_A;
	}

	/**
	 * 
	 * @return
	 */
	public boolean isArrow2() {
		return get() <= E2 && get() >= S2;
	}

	/**
	 * 
	 * @return
	 */
	public boolean isArrow3() {
		return get() <= E3 && get() >= S3;
	}

	/**
	 * 
	 * @return
	 */
	public boolean isBranch() {
		int x = get();

		return x == '+' || x < BRANCH;
	}

	/**
	 * 
	 * @return
	 */
	public boolean isBranchN() {
		int x = get();

		if(x == '+') {
			return true;
		} else if((x = -(x - BRANCH)) > 0) {
			return ((x / BRANCH_N) % 10) != 1;
		} else {
			return false;
		}
	}

	/**
	 * 
	 * @return
	 */
	public boolean isBranchE() {
		int x = get();

		if(x == '+') {
			return true;
		} else if((x = -(x - BRANCH)) > 0) {
			return ((x / BRANCH_E) % 10) != 1;
		} else {
			return false;
		}
	}

	/**
	 * 
	 * @return
	 */
	public boolean isBranchS() {
		int x = get();

		if(x == '+') {
			return true;
		} else if((x = -(x - BRANCH)) > 0) {
			return ((x / BRANCH_S) % 10) != 1;
		} else {
			return false;
		}
	}

	/**
	 * 
	 * @return
	 */
	public boolean isBranchW() {
		int x = get();

		if(x == '+') {
			return true;
		} else if((x = -(x - BRANCH)) > 0) {
			return ((x / BRANCH_W) % 10) != 1;
		} else {
			return false;
		}
	}

	/**
	 * 
	 * @return
	 */
	public boolean isWedgeN() {
		int x = get();

		return x < BRANCH && (-(x - BRANCH) / BRANCH_N) % 10 == 1;
	}

	/**
	 * 
	 * @return
	 */
	public boolean isWedgeE() {
		int x = get();

		return x < BRANCH && (-(x - BRANCH) / BRANCH_E) % 10 == 1;
	}

	/**
	 * 
	 * @return
	 */
	public boolean isWedgeS() {
		int x = get();

		return x < BRANCH && (-(x - BRANCH) / BRANCH_S) % 10 == 1;
	}

	/**
	 * 
	 * @return
	 */
	public boolean isWedgeW() {
		int x = get();

		return x < BRANCH && (-(x - BRANCH) / BRANCH_W) % 10 == 1;
	}

	/**
	 * 
	 * @return
	 */
	public boolean isWhitespace() {
		return Character.isWhitespace(get());
	}

	/**
	 * 
	 * @param a
	 */
	public void set(int a) {
		if(isInBounds()) {
			quadro[cury][curx] = a;
		}
	}

	/**
	 * 
	 * @param a
	 */
	public void setScratch(Object a) {
		if(isInBounds()) {
			scratch[cury][curx] = a;
		}
	}

	/**
	 * 
	 * @param a
	 */
	public void setScratch2(Object a) {
		if(isInBounds()) {
			scratch2[cury][curx] = a;
		}
	}

	/**
	 * 
	 * @param d
	 * @return
	 */
	public Quadro move(Quadro.Direction d) {
		switch(d) {
		case WEST:
			curx = curx < 0 ? curx : curx - 1;
			return this;
		case EAST:
			curx = cury < 0 || cury >= quadro.length || curx > quadro[cury].length ?
					curx : curx + 1;
			return this;
		case NORTH:
			cury = cury < 0 ? cury : cury - 1;
			return this;
		case SOUTH:
			cury = cury > quadro.length ? cury : cury + 1;
			return this;
		case CR:
			curx = 0;
			cury = cury > quadro.length ? cury : cury + 1;
			return this;
		default:  throw new RuntimeException();
		}
	}

	/**
	 * 
	 * @param x
	 * @param y
	 * @return
	 */
	public Quadro move(int x, int y) {
		if(cury + y < 0) {
			cury = -1;
		} else if(cury + y > quadro.length) {
			cury = quadro.length;
		}

		if(curx + x < 0) {
			curx = -1;
		} else if(curx + x > quadro[cury].length) {
			curx = quadro[cury].length;
		}
		return this;
	}

	/**
	 * 
	 * @return
	 */
	public Quadro west() {
		return move(Quadro.Direction.WEST);
	}

	/**
	 * 
	 * @return
	 */
	public Quadro east() {
		return move(Quadro.Direction.EAST);
	}

	/**
	 * 
	 * @return
	 */
	public Quadro north() {
		return move(Quadro.Direction.NORTH);
	}

	/**
	 * 
	 * @return
	 */
	public Quadro south() {
		return move(Quadro.Direction.SOUTH);
	}

	/**
	 * 
	 * @return
	 */
	public Quadro cr() {
		return move(Quadro.Direction.CR);
	}

	/**
	 * 
	 * @param name
	 * @return
	 */
	public Quadro getSubQuadro(String name) {
		return subquadro != null ? subquadro.get(name) : null;
	}

	/**
	 * 
	 * @return
	 */
	public Map<String, Quadro> getSubQuadros() {
		return subquadro != null ?
				Collections.unmodifiableMap(subquadro) :
					Collections.<String, Quadro>emptyMap();
	}

	/**
	 * 
	 * @param q
	 * @return
	 */
	public Quadro prototype(Quadro q) {
		return new Quadro(name, quadro, fragment,
				q.getSubQuadros(), q.options);
	}

	/**
	 * 
	 * @return
	 */
	public String getLabel() {
		if(cury < 0 || cury >= quadro.length) {
			return null;
		} else if(curx < 0 || curx >= quadro[cury].length) {
			return null;
		} else {
			return labels[cury][curx];
		}
	}

	/**
	 * 
	 * @param a
	 */
	public void setLabel(String a) {
		if(isInBounds()) {
			labels[cury][curx] = a;
		}
	}

	/**
	 * 
	 * @return
	 */
	public String getLabelType() {
		if(cury < 0 || cury >= quadro.length) {
			return null;
		} else if(curx < 0 || curx >= quadro[cury].length) {
			return null;
		} else {
			return ltypes[cury][curx];
		}
	}

	/**
	 * 
	 * @param a
	 */
	public void setLabelType(String a) {
		if(isInBounds()) {
			ltypes[cury][curx] = a;
		}
	}

	/**
	 * 
	 * @param l
	 * @return
	 */
	public boolean isVisited(String l) {
		if(cury < 0 || cury >= quadro.length) {
			return false;
		} else if(curx < 0 || curx >= quadro[cury].length) {
			return false;
		} else if(visited[cury][curx] == null) {
			return false;
		} else {
			return visited[cury][curx].indexOf("(" + l + ")") >= 0;
		}
	}

	/**
	 * 
	 * @param l
	 */
	public void addVisited(String l) {
		if(!isInBounds()) {
			// do nothing
		} else if(visited[cury][curx] == null) {
			visited[cury][curx] = "(" + l + ")";
		} else {
			visited[cury][curx] += "(" + l + ")";
		}
	}

	/**
	 * 
	 * @return
	 */
	public String getVisiting() {
		if(cury < 0 || cury >= quadro.length) {
			return null;
		} else if(curx < 0 || curx >= quadro[cury].length) {
			return null;
		} else {
			return visiting[cury][curx];
		}
	}

	/**
	 * 
	 * @param a
	 */
	public void setVisiting(String a) {
		if(isInBounds()) {
			visiting[cury][curx] = a;
		}
	}

	/**
	 * 
	 * @param l
	 * @return
	 */
	public Object getStateByLabel(String l) {
		for(int y = 0; y < labels.length; y++) {
			for(int x = 0; x < labels[y].length; x++) {
				if(l.equals(labels[y][x])) {
					return scratch[y][x];
				}
			}
		}
		return null;
	}

	/**
	 * 
	 * @return
	 */
	public Object getDeadState() {
		return getStateByLabel(NinaParser.DEADSYM);
	}

	//
	static int len(char c) {
		Character.UnicodeBlock b;

		b = Character.UnicodeBlock.of(c);
		if(b == null) {
			return 1;
		} else if(c >= 0xff01 && c <= 0xff60) {
			return 2;
		} else if(c >= 0xffe0 && c <= 0xffe6) {
			return 2;
		} else if(b.equals(Character.UnicodeBlock.CJK_COMPATIBILITY) ||
				b.equals(Character.UnicodeBlock.CJK_COMPATIBILITY_FORMS) ||
				b.equals(Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS) ||
				b.equals(Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT) ||
				b.equals(Character.UnicodeBlock.CJK_RADICALS_SUPPLEMENT) ||
//				b.equals(Character.UnicodeBlock.CJK_STROKES) ||
				b.equals(Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION) ||
				b.equals(Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS) ||
				b.equals(Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A) ||
				b.equals(Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B) ||
				b.equals(Character.UnicodeBlock.KATAKANA) ||
				b.equals(Character.UnicodeBlock.HIRAGANA) ||
				b.equals(Character.UnicodeBlock.HANGUL_SYLLABLES)) {
			return 2;
		} else {
			return 1;
		}
	}

	static int len(String s) {
		int r = 0;

		for(int i = 0; i < s.length(); i++) {
			r = r + len(s.charAt(i));
		}
		return r;
	}

	static int putWedgeN(int x) {
		if(x == '+') {
			return BRANCH - BRANCH_N;
		} else if(x < BRANCH) {
			return x - BRANCH_N;
		} else {
			throw new RuntimeException();
		}
	}

	static int putWedgeE(int x) {
		if(x == '+') {
			return BRANCH - BRANCH_E;
		} else if(x < BRANCH) {
			return x - BRANCH_E;
		} else {
			throw new RuntimeException();
		}
	}

	static int putWedgeS(int x) {
		if(x == '+') {
			return BRANCH - BRANCH_S;
		} else if(x < BRANCH) {
			return x - BRANCH_S;
		} else {
			throw new RuntimeException();
		}
	}

	static int putWedgeW(int x) {
		if(x == '+') {
			return BRANCH - BRANCH_W;
		} else if(x < BRANCH) {
			return x - BRANCH_W;
		} else {
			throw new RuntimeException();
		}
	}

	/**
	 * 
	 * @param classe
	 * @return
	 * @throws IOException
	 */
	public static Quadro readResource(String name) throws IOException {
		InputStream in = null;
		Reader rd;

		try {
			in = Quadro.class.getResourceAsStream(name);
			rd = new InputStreamReader(in);
			return read(name, rd);
		} finally {
			if(in != null)  in.close();
		}
	}

	/**
	 * 
	 * @param s
	 * @return
	 */
	public static Quadro read(String name, String s) {
		StringReader r;

		try {
			r = new StringReader(s);
			return read(name, r);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	private static int[] toiarray(String s) {
		int[] a;
		int m;

		a = new int[len(s)];
		for(int i = 0, j = 0; j < s.length(); i += m, j++) {
			m = len(s.charAt(j));
			a[i] = s.charAt(j);
			for(int k = i + 1; k < i + m; k++) {
				a[k] = Quadro.EQ_TO_LEFT;
			}
		}
		return a;
	}

	/**
	 * 
	 * @param name
	 * @param rd
	 * @return
	 * @throws IOException
	 */
	public static Quadro read(String name,
			Reader rd) throws IOException {
		Map<String, Quadro> q = new HashMap<String, Quadro>();
		Map<String, String> m = new HashMap<String, String>();
		List<int[]> l = new ArrayList<int[]>(), z = null;
		BufferedReader br = new BufferedReader(rd);
		List<String> sl = new ArrayList<String>();
		StringWriter sw = new StringWriter();
		PrintWriter pw = new PrintWriter(sw);
		String s, x = null;
		boolean b = false;
		int y = 0, p = 0;
		Matcher h;
		Quadro v;
		int[] a;

		for(; (s = br.readLine()) != null && !s.equals("%%"); p++) {
			if(s.equals("")) {
				continue;
			} else if(!b && z == null && s.charAt(0) == '#') {
				sl.add(s);
				a = toiarray(s);
				l.add(a);
			} else if(!b && PTN2.matcher(s).lookingAt()) {
				b = true;
				a = toiarray(s);
				l.add(a);
			} else if(b && s.startsWith("%}")) {
				b = false;
				a = toiarray(s);
				l.add(a);
			} else if(!(h = PTN1.matcher(s)).matches()) {
				a = toiarray(s);
				l.add(a);
			} else {
				if(z != null) {
					q.put(x, v = new Quadro(x, l, "", m));
					v.lint.setYOffset(y);
					iswarning = v.lint.validate(errst) | iswarning;
					y = p + 1;
				} else {
					z = l;
				}

				x = h.group(1);
				l = new ArrayList<int[]>();
				for(String t : sl) {
					a = toiarray(t);
					l.add(a);
				}
			}
		}

		while((s = br.readLine()) != null) {
			pw.println(s);
		}
		pw.flush();

		if(z != null) {
			q.put(x, v = new Quadro(x, l, "", m));
			v.lint.setYOffset(y);
			iswarning = v.lint.validate(errst) | iswarning;
		} else {
			z = l;
		}
		v = new Quadro(name, z, sw.toString(), q,
				new HashMap<String, String>());
		v.lint.setYOffset(0);
		iswarning = v.lint.validate(errst) | iswarning;
		return v;
	}

	/**
	 * 
	 * @param s
	 * @return
	 */
	public String getOption(String s) {
		return options.get(s);
	}

	/**
	 * 
	 * @param s
	 * @param n
	 * @return
	 */
	public String getOptionNvl(String s, String n) {
		String x = options.get(s);

		return x != null ? x : n;
	}

	/**
	 * 
	 * @param s
	 * @param x
	 * @return
	 */
	public int getOptionInt(String s, int x) {
		try {
			return Integer.parseInt(options.get(s));
		} catch(NumberFormatException e) {
			return x;
		}
	}

	/**
	 * 
	 * @param s
	 * @return
	 */
	public boolean isOptionDefined(String s) {
		return options.get(s) != null;
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaEvent#getOptionBoolean(java.lang.String)
	 */
	@Override
	public boolean getOptionBoolean(String s) {
		String x;

		if((x = options.get(s)) == null)  return false;
		return (x.equalsIgnoreCase("true") ||
				x.equalsIgnoreCase("yes")  ||
				x.equalsIgnoreCase("on")   ||
				x.equalsIgnoreCase("oui"));
	}

	/**
	 * 
	 * @param s
	 * @return
	 */
	public String getAlias(String s) {
		return aliases.get(s);
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaEvent#getCharset()
	 */
	@Override
	public String getCharset() {
		String s;

		return (s = options.get("charset")) != null ? s : "UTF-8";
	}

	/**
	 * 
	 * @return
	 */
	public String getType() {
		return type;
	}

	/**
	 * 
	 * @return
	 */
	public List<String> getImports() {
		return Collections.unmodifiableList(imports);
	}

	//
	void addImports(String s) {
		imports.add(s);
	}

	/**
	 * 
	 * @return
	 */
	public String getFragment() {
		return fragment != null ? fragment : "";
	}

	/**
	 * 
	 * @return
	 */
	public String getDefinition() {
		return getFragmentByName("");
	}

	/**
	 * 
	 * @param s
	 * @return
	 */
	public String getFragmentByName(String s) {
		StringBuffer b;

		return (b = buffers.get(s)) != null ? b.toString() : "";
	}

	/**
	 * 
	 * @return
	 */
	public String getFragmentField() {
		StringBuffer b, a;
		String n = System.getProperty("line.separator");

		b = new StringBuffer();
		if((a = buffers.get("field"))  != null)  b.append(a);
		if((a = buffers.get("member")) == null) {
			// do nothing
		} else {
			if(b.length() > 0)  b.append(n);
			b.append(a);
		}
		return b.toString();
	}

	/**
	 * 
	 * @param pr
	 */
	public void printTrace(PrintStream pr) {
		pr.println(toString());
	}

	/* (non-Javadoc)
	 * @see net.morilib.nina.NinaEvent#getLanguageOption()
	 */
	public NinaLanguageOption getLanguageOption() {
		String s;

		if(langopt == null) {
			s = options.get(LOPT);
			s = s != null ? s : "Java";
			langopt = (NinaLanguageOption)Nina.prendClasse(
					NinaLanguageOption.class.getName() + s);
		}
		return langopt;
	}

	/**
	 * 
	 * @return
	 */
	public static boolean isWarning() {
		return iswarning;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	public String toString() {
		StringBuffer b = new StringBuffer();

		for(int i = 0; i < quadro.length; i++) {
			for(int j = 0; j < quadro[i].length; j++) {
				if(cury == i && curx == j) {
					b.append('x');
				} else {
					switch(quadro[i][j]) {
					case EQ_TO_LEFT:  break;
					case BLANKX:  b.append('$');  break;
					case BLANKY:  b.append('#');  break;
					case N2:      b.append('^');  break;
					case E2:      b.append('>');  break;
					case S2:      b.append('v');  break;
					case W2:      b.append('<');  break;
					case N3:      b.append('A');  break;
					case E3:      b.append(')');  break;
					case S3:      b.append('w');  break;
					case W3:      b.append('(');  break;
					case DONE:    b.append('0');  break;
					default:
						if(quadro[i][j] >= 0) {
							b.append((char)quadro[i][j]);
						} else {
							b.append('.');
						}
						break;
					}
				}
			}
			b.append('\n');
		}
		return b.toString();
	}

}
