package paramsim.paramicssimulator;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import paramsim.paramicssimulator.SimulationCamera.ROAD_DIRECTION;

/**
 * Creates a list of Simulation Cameras by parsing two configuration files: 
 * the camera_control file used by Paramics, and the dvdplayers.xml file 
 * used by the CAD Simulator.
 * 
 * @author Greg Eddington
 */
public class CameraParser
{
	/** Stopped DVD Title **/
	private static final String STOPPED_TITLE = "3";
	/** Slow DVD Titlee **/
	private static final String SLOW_TITLE = "2";
	/** Free Flow DVD Title **/
	private static final String FREE_FLOW_TITLE = "1";
	
	/**
	 * Enumeration containing XML tag names used to parse the XML
	 * document containing information about DVD Cameras.
	 * @author Matthew Cechini
	 * @author Greg Eddington
	 */
	private static enum CAMERA_TAGS 
	{	
		/** Top level tag. */
		DVD_PLAYERS ("DVD_PLAYERS"),
		/** DVD player info. */	
		DVD_PLAYER ("DVD_PLAYER"),
		/** CCTV camera info. */
		CCTV         ("CCTV"),
		/** CCTV camera id. */
		ID           ("id"),
		/** CCTV camera direction. */
		DIRECTION    ("dir"),
		/** Speed range mapping to a title number. */
		RANGE        ("RANGE"),
		/** Minimum speed in a range. */
		MIN_SPEED    ("min_speed"),
		/** Maximum speed in a range. */
		MAX_SPEED    ("max_speed"),
		/** Title name. */
		TITLE        ("title");
		
		/** XML Tag name. */
		public String tag;
		
		/** Constructor **/
		private CAMERA_TAGS(String t) { tag = t; }
	}
	
	/**
	 * Constructor.
	 */
	public CameraParser() 
	{
	}

	/**
	 * Parses two input files and creates a list of Simulation Cameras.
	 * 
	 * @param dvdPlayersXml The XML file which contains DVD information for the CAD Simulator.
	 * @param cameraControlFile The camera_control file used by Paramics to map cameras to a simulation.
	 * @return A list of cameras configured from the parsed files.
	 * @throws IOException On error reading or parsing one of the files.
	 */
	public static List<SimulationCamera> loadCameras(String dvdPlayersXml, String cameraControlFile) 
		throws IOException
	{
		List<SimulationCamera> cameras = new ArrayList<SimulationCamera>();
		
		// XML document
		try
		{
			Document newDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder()
				.parse(new File(dvdPlayersXml));

			Element rootElement = newDoc.getDocumentElement();		
			
			NodeList players   = rootElement.getElementsByTagName(CAMERA_TAGS.DVD_PLAYER.tag);
			
			for(int i = 0; i < players.getLength(); i++) 
			{
				SimulationCamera camera = new SimulationCamera();
				
				Element playerElement = (Element)players.item(i);
						
				Element cctvElement = (Element)playerElement.getElementsByTagName(CAMERA_TAGS.CCTV.tag).item(0);
				camera.id = Integer.parseInt(cctvElement.getAttribute(CAMERA_TAGS.ID.tag));
				switch(cctvElement.getAttribute(CAMERA_TAGS.DIRECTION.tag).charAt(0))
				{
					case 'N':
						camera.direction = ROAD_DIRECTION.NORTH; 
						break;
					case 'E':
						camera.direction = ROAD_DIRECTION.EAST;
						break;
					case 'S':
						camera.direction = ROAD_DIRECTION.SOUTH;
						break;
					default:
						camera.direction = ROAD_DIRECTION.WEST;
						break;
				}
				
				NodeList ranges = playerElement.getElementsByTagName(CAMERA_TAGS.RANGE.tag);		
							
				for(int j = 0; j < ranges.getLength(); j++) 
				{
					Element rangeElement = (Element)ranges.item(j);
					
					if (rangeElement.getAttribute(CAMERA_TAGS.TITLE.tag).contains(STOPPED_TITLE))
					{
				 		camera.minStoppedSpeed = 
							Float.parseFloat(rangeElement.getAttribute(CAMERA_TAGS.MIN_SPEED.tag)); 
						camera.maxStoppedSpeed = 
							Float.parseFloat(rangeElement.getAttribute(CAMERA_TAGS.MAX_SPEED.tag)); 
					}
					else if (rangeElement.getAttribute(CAMERA_TAGS.TITLE.tag).contains(SLOW_TITLE))
					{
						camera.minSlowSpeed = 
							Float.parseFloat(rangeElement.getAttribute(CAMERA_TAGS.MIN_SPEED.tag)); 
						camera.maxSlowSpeed = 
							Float.parseFloat(rangeElement.getAttribute(CAMERA_TAGS.MAX_SPEED.tag)); 
					}
					else if (rangeElement.getAttribute(CAMERA_TAGS.TITLE.tag).contains(FREE_FLOW_TITLE))
					{
						camera.minFreeFlowSpeed = 
							Float.parseFloat(rangeElement.getAttribute(CAMERA_TAGS.MIN_SPEED.tag)); 
						camera.maxFreeFlowSpeed = 
							Float.parseFloat(rangeElement.getAttribute(CAMERA_TAGS.MAX_SPEED.tag)); 
					}
				}
				
				// Initialize Speed
				camera.averageSpeedNE = camera.averageSpeedSW =
					((camera.minStoppedSpeed + camera.maxStoppedSpeed) / 2);
				
				cameras.add(camera);
			}		
		}
		catch (ParserConfigurationException pce)
		{
			throw new IOException(pce.toString());
		}
		catch (SAXException se)
		{
			throw new IOException(se.toString());
		}
		
		// Camera Control File
		Scanner f = new Scanner(new FileReader(cameraControlFile));
		
		// Throw away the first line
		f.nextLine();
		while (f.hasNextInt())
		{
			int id = f.nextInt();
			
			for (SimulationCamera camera : cameras)
			{
				if (camera.id == id)
				{
					// Route
					camera.route = f.next();

					// Direction
					f.next();
					
					// Postmile
					camera.postmile = f.nextFloat();
					
					break;
				}
			}
			
			// Ignore the rest
			f.nextLine();
		}
		
		return cameras;
	}
}