package CK.IA;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Hashtable;
import java.util.StringTokenizer;
import java.util.Vector;

public class WavefrontParser {
	private static final String TAG_COMMENT = "#";
	private static final String TAG_FACE = "f";
	private static final String TAG_GROUP = "g";
	private static final String TAG_LINE = "l";
	private static final String TAG_MATERIALLIB = "mtllib";
	private static final String TAG_USEMATERIAL = "usemtl";
	private static final String TAG_VERTEX = "v";
	private static final String TAG_VERTEXNORMAL = "vn";
	private static final String TAG_VERTEXTEXTURE = "vt";

	public static void consoleOut(String text) {
		System.out.println(text);
	}

	public static void main(String[] args) {
		if (args == null || args.length != 1)
			terminateExecution("ERROR: invalid arguments", -1);
		else
			try {
				WavefrontParser wop = new WavefrontParser();
				wop.parseFile(args[0]);
				wop.checkCoincidentVertices(0.01, true);
				WaypointNavigation wn = new WaypointNavigation(wop.getLines(), wop.getVertices());
				// LinkedList<Integer> star = wn.AStar(1815, 1607);
				terminateExecution("done", 0);
			} catch (Exception e) {
				e.printStackTrace();
			}
	}

	public static void terminateExecution(String warning, int errornum) {
		consoleOut(warning);
		System.exit(errornum);
	}

	private Vector<Face3d> faces;
	private Vector<Line3d> lines;
	private Vector<Vector3d> vertices;
	private Vector<Vector3d> vertices_normal;

	public WavefrontParser() {
	}

	/**
	 * checks and removes vertices near each other within the given tolerance
	 * 
	 * @param tolerance
	 * @param replace
	 */
	public void checkCoincidentVertices(double tolerance, boolean replace) {
		int size = vertices.size();
		Hashtable<Integer, Integer> vertices_replacements = new Hashtable<Integer, Integer>();
		for (int i = 0; i < size; i++) {
			for (int j = i + 1; j < size; j++) {
				// vertices are coincident if the distance between them is less than 'tolerance'
				double distance = vertices.get(i).distanceTo(vertices.get(j));
				if (distance < tolerance) {
					if (vertices_replacements.containsKey(i)) {
						Integer value = vertices_replacements.get(i);
						vertices_replacements.put(j, value);
						System.out.println("vertice at " + value + " coincident with " + j);
					} else {
						vertices_replacements.put(j, i);
						System.out.println("vertice at " + i + " coincident with " + j);
					}
				}
			}
		}
		if (replace) {
			// make replacements in the faces
			for (Face3d face : faces) {
				// for all face's vertices
				for (int i = 0; i < face.indexes.size(); i++) {
					Integer vertice = face.indexes.get(i);
					if (vertices_replacements.containsKey(vertice)) {
						Integer replacement = vertices_replacements.get(vertice);
						face.indexes.set(i, replacement);
						System.out.println("face's vertex " + vertice + " replaced with " + replacement);
					}
				}
			}
			// make replacements in the lines
			for (Line3d line : lines) {
				// for the two line's vertices
				int vertice = line.first;
				Integer replacement = vertices_replacements.get(vertice);
				if (vertices_replacements.containsKey(vertice)) {
					line.first = replacement;
					System.out.println("line's first vertex " + vertice + " replaced with " + replacement);
				}
				vertice = line.second;
				replacement = vertices_replacements.get(vertice);
				if (vertices_replacements.containsKey(vertice)) {
					line.second = replacement;
					System.out.println("line's second vertex " + vertice + " replaced with " + replacement);
				}
			}
		}
	}

	private void decodeFace(String arguments) {
		StringTokenizer st = new StringTokenizer(arguments, " ", false);
		Vector<Integer> indexes = new Vector<Integer>();
		// get vertex group
		while (st.hasMoreTokens()) {
			String token = st.nextToken();
			StringTokenizer tst = new StringTokenizer(token, "/", false);
			// get vertex from group, only first
			if (tst.hasMoreTokens()) {
				// adapth vertex's index (wavefront starts at 1, we work starting at 0)
				indexes.add(Integer.parseInt(tst.nextToken()) - 1);
			}
		}
		Face3d f = new Face3d(indexes);
		faces.add(f);
		f.generateLines(lines);
		if (GoalSimulatorConfig.DEBUG)
			System.out.println("decoded face " + f);
	}

	private void decodeLine(String arguments) {
		int v0 = -1, v1 = -1;
		StringTokenizer st = new StringTokenizer(arguments, " ", false);
		String sval;
		// argument counter
		int i = 0;
		// decode arguments
		while (i < 2 && st.hasMoreTokens()) {
			int val;
			sval = "";
			try {
				sval = st.nextToken();
				// adapth vertex's index (wavefront starts at 1, we work starting at 0)
				val = Integer.parseInt(sval) - 1;
				switch (i) {
					case 0:
						v0 = val;
						break;
					case 1:
						v1 = val;
						break;
				}
				i++;
			} catch (NumberFormatException e) {
				System.out.println("unrecognized line number: " + sval);
			}
		}
		if (i == 2) {
			Line3d line = new Line3d(v0, v1);
			lines.add(line);
			if (GoalSimulatorConfig.DEBUG)
				System.out.println("decoded line " + v0 + ", " + v1);
		}
	}

	private void decodeVertex(String arguments) {
		double x = 0.0, y = 0.0, z = 0.0;
		StringTokenizer st = new StringTokenizer(arguments);
		String sval;
		// argument counter
		int i = 0;
		// decode arguments
		while (i < 3 && st.hasMoreTokens()) {
			double val;
			sval = "";
			try {
				sval = st.nextToken();
				val = Double.parseDouble(sval);
				switch (i) {
					case 0:
						x = val;
						break;
					case 1:
						y = val;
						break;
					case 2:
						z = val;
						break;
					case 3:
						break;
				}
				i++;
			} catch (NumberFormatException e) {
				System.out.println("unrecognized coordinate value: " + sval);
			}
		}
		Vector3d vertex = new Vector3d(x, y, z);
		vertices.add(vertex);
		if (GoalSimulatorConfig.DEBUG)
			System.out.println("decoded vertex " + vertex);
	}

	private void decodeVertexNormal(String arguments) {
		double x = 0.0, y = 0.0, z = 0.0;
		StringTokenizer st = new StringTokenizer(arguments);
		String sval;
		// argument counter
		int i = 0;
		// decode arguments
		while (i < 3 && st.hasMoreTokens()) {
			double val;
			sval = "";
			try {
				sval = st.nextToken();
				val = Double.parseDouble(sval);
				switch (i) {
					case 0:
						x = val;
						break;
					case 1:
						y = val;
						break;
					case 2:
						z = val;
						break;
					case 3:
						break;
				}
				i++;
			} catch (NumberFormatException e) {
				System.out.println("unrecognized coordinate value: " + sval);
			}
		}
		Vector3d vertex = new Vector3d(x, y, z);
		vertices_normal.add(vertex);
		if (GoalSimulatorConfig.DEBUG)
			System.out.println("decoded vertex normal" + vertex);
	}

	private void decodeVertexTexture(String arguments) {
		if (GoalSimulatorConfig.DEBUG)
			System.out.println("decodeVertexTexture " + arguments);
	}

	public Vector<Face3d> getFaces() {
		return faces;
	}

	public Vector<Line3d> getLines() {
		return lines;
	}

	public Vector<Vector3d> getVertices() {
		return vertices;
	}

	public Vector<Vector3d> getVerticesNormal() {
		return vertices_normal;
	}

	public void parseFile(String toparse) throws IOException, FileNotFoundException {
		// init data
		vertices = new Vector<Vector3d>();
		vertices_normal = new Vector<Vector3d>();
		lines = new Vector<Line3d>();
		faces = new Vector<Face3d>();
		BufferedReader br = new BufferedReader(new FileReader(toparse));
		String line = null, arguments = null;
		// parse ALL the file
		while (br.ready()) {
			// lines composed of
			// CODEWORD <argument0> ... <argumentn>
			line = br.readLine();
			// remove trailing and ending spaces
			line = line.trim().toLowerCase();
			try {
				arguments = line.substring(line.indexOf(" ") + 1);
			} catch (Exception e) {
				// there may not be an argument
			}
			// comments are to be ignored
			if (line.startsWith(TAG_COMMENT) || line.length() == 0) {
			} else if (line.startsWith(TAG_MATERIALLIB)) {
			} else if (line.startsWith(TAG_USEMATERIAL)) {
			} else if (line.startsWith(TAG_VERTEXNORMAL)) {
				decodeVertexNormal(arguments);
			} else if (line.startsWith(TAG_VERTEXTEXTURE)) {
				decodeVertexTexture(arguments);
			} else if (line.startsWith(TAG_VERTEX)) {
				decodeVertex(arguments);
			} else if (line.startsWith(TAG_FACE)) {
				decodeFace(arguments);
			} else if (line.startsWith(TAG_GROUP)) {
			} else if (line.startsWith(TAG_LINE)) {
				decodeLine(arguments);
			} else {
				System.out.println("unknown line syntax: " + line);
			}
		}
		System.out.println("parsing complete");
	}

	/**
	 * removes dot lines, where both it's points are the same
	 */
	public void removeDotLines() {
		for (int i = 0; i < lines.size(); i++) {
			Line3d line = lines.get(i);
			if (line.first == line.second) {
				lines.remove(i);
				System.out.println("removed line " + line.first + "-" + line.second);
			}
		}
	}

}
