/*
 * Copyright 2009-2010 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.lint;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import net.morilib.nina.NinaCompoundException;
import net.morilib.nina.NinaException;

/**
 *
 *
 * @author MORIGUCHI, Yuichiro 2014/01/11
 */
public class LintCells {

	//
	private List<String> warnings = new ArrayList<String>();
	private LintCell[][] cells;
	private int yOffset;

	/**
	 * 
	 * @param yoff
	 * @param a
	 */
	public LintCells(int yoff, List<int[]> a) {
		int c;

		cells = new LintCell[a.size()][];
		for(int y = 0; y < a.size(); y++) {
			cells[y] = new LintCell[a.get(y).length];
			for(int x = 0; x < a.get(y).length; x++) {
				c = a.get(y)[x];
				cells[y][x] = createCell(c, x, y);
			}
		}
		yOffset = yoff;
	}

	/**
	 * 
	 * @param yoff
	 * @param a
	 */
	public LintCells(int yoff, int[][] a) {
		int c;

		cells = new LintCell[a.length][];
		for(int y = 0; y < a.length; y++) {
			cells[y] = new LintCell[a[y].length];
			for(int x = 0; x < a[y].length; x++) {
				c = a[y][x];
				cells[y][x] = createCell(c, x, y);
			}
		}
		yOffset = yoff;
	}

	//
	private LintCell createCell(int c, int x, int y) {
		switch(c) {
		case '-':   return new LintCellPathEW(this, c, x, y);
		case '|':   return new LintCellPathNS(this, c, x, y);
		case '+':   return new LintCellBranch(this, c, x, y);
		case '\\':  return new LintCellRotateRight(this, c, x, y);
		case '/':   return new LintCellRotateLeft(this, c, x, y);
		case '<':   return new LintCellEntranceW(this, c, x, y);
		case '>':   return new LintCellEntranceE(this, c, x, y);
		case '^':   return new LintCellEntranceN(this, c, x, y);
		case 'v':   return new LintCellEntranceS(this, c, x, y);
		case '*':  case '=':  case '@':  case '&':
			return new LintCellFrame(this, c, x, y);
		case ' ':   return new LintCellBlank(this, c, x, y);
		default:    return new LintCellAlphabet(this, c, x, y);
		}
	}

	//
	void addWarning(String msg, Object... args) {
		warnings.add(NinaException.getMessage(msg, args));
	}

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

	/**
	 * 
	 * @return
	 */
	public int getYOffset() {
		return yOffset;
	}

	/**
	 * 
	 * @param y
	 */
	public void setYOffset(int y) {
		yOffset = y;
	}

	/**
	 * 
	 * @param x
	 * @param y
	 * @return
	 */
	public LintCell get(int x, int y) {
		if(y < 0 || y >= cells.length) {
			return new LintCellBlank(this, 0, x, y);
		} else if(x < 0 || x >= cells[y].length) {
			return new LintCellBlank(this, 0, x, y);
		} else {
			return cells[y][x];
		}
	}

	/**
	 * 
	 * @param out
	 */
	public void putBadfile(PrintStream out) {
		LintCell c;

		for(int y = 0; y < cells.length; y++) {
			for(int x = 0; x < cells[y].length; x++) {
				if((c = cells[y][x]).isError()) {
					out.print('X');
				} else if(c.getCharacter() >= 0) {
					out.print((char)cells[y][x].getCharacter());
				}
			}
			out.println();
		}
	}

	/**
	 * 
	 * @param fname
	 * @throws IOException
	 */
	public void putBadfile(String fname) throws IOException {
		PrintStream out = null;

		try {
			out = new PrintStream(new FileOutputStream(fname));
			putBadfile(out);
		} finally {
			if(out != null)  out.close();
		}
	}

	/**
	 * 
	 */
	public boolean validate(PrintStream out) {
		List<NinaException> l = new ArrayList<NinaException>();
		String a;

		for(int y = 0; y < cells.length; y++) {
			if(cells[y].length == 0)  continue;
			if(cells[y][0].getCharacter() == '#')  continue;
			for(int x = 0; x < cells[y].length; x++) {
				try {
					cells[y][x].validate();
				} catch(NinaException e) {
					cells[y][x].error = true;
					l.add(e);
				}
			}
		}

		if(l.size() > 0) {
			throw new NinaCompoundException(l, this);
		} else if(out != null && warnings.size() > 0) {
			a = NinaException.getMessage("warning");
			for(String s : warnings) {
				out.printf("%s: %s\n", a, s);
			}
			return true;
		} else {
			return false;
		}
	}

}
