package sim;

import java.io.*;
import java.lang.reflect.*;
import java.awt.*;
import java.net.URL;

import gjt.Util;

public class GuiFileLink
{
	public static Image getImage(String path)
	{
		Image result;
		
		if(MainWindow.MASTER == null)
		{
			Toolkit access = MainWindow.MAIN_WINDOW.getToolkit();
			result = access.getImage(path);
			Util.waitForImage(MainWindow.MAIN_WINDOW, result);
		}
		else
		{
			URL cb = MainWindow.MASTER.getCodeBase();
			result = MainWindow.MASTER.getImage(cb, path);
			Util.waitForImage(MainWindow.MAIN_WINDOW, result);
		}
		
		return result;
	}
	
	private static char BLANK = '#';
	private static String LIBRARY = "RETRO_Library";
	
/* ==================================================================
	Load Provided to SaveLoadShortcut
	================================================================= */
	public void load(Reader in, Grid g) throws SimException
	{
		String[] basics, specifics;
		String className, componentName, readIn;
		Wrapper created;
		
		readIn = this.extractParameter(in);
		
		// Check Version
		if(readIn.equals(MainWindow.VERSION))
		{
			// Read in Grid information
			this.readBlank(in);
			basics = this.extractParameters(g.getNumberOfParameters(), in);
			g.loadGrid(basics);
			
			// Read number of components
			this.readBlank(in);
			int size = Integer.valueOf(this.extractParameter(in)).intValue();
			
			for(int index = 0; index < size; index++)
			{
				this.readBlank(in);
				
				className = this.extractParameter(in);	
				componentName = this.extractParameter(in);	
				created = this.getWrapper(className);
				
				basics = this.extractParameters(created.getNumberOfBasicParameters(), in);
				specifics = this.extractParameters(created.getNumberOfSpecificParameters(), in);
				created.initialiseWrapper(basics, specifics);
				g.addModule(created);
				
				if(created.canDrop())
				{
					created.droped();
					created.setName(componentName);
				}
				else
				{
					throw (new SimException("illegal component position"));
				}
			}
		}
		else
		{
			throw (new SimException("not valid file"));
		}
	}
	
/* ==================================================================
	Load Library Provided to WestPanel
	================================================================= */
	
	public ModulePanel loadLibrary(Reader in) throws SimException
	{
		ModulePanel result = new ModulePanel();
		String className, readIn;
		CreationModule created;
		readIn = this.extractParameter(in);
		
		// Check whether this is library file
		if(readIn.equals(GuiFileLink.LIBRARY))
		{
			// Read name of the library
			this.readBlank(in);
			result.setName(this.extractParameter(in));
			
			// Read number of components
			this.readBlank(in);
			int size = Integer.valueOf(this.extractParameter(in)).intValue();
			
			for(int index = 0; index < size; index++)
			{
				this.readBlank(in);
				className = this.extractParameter(in);
				created = this.getCreationModule(className);
				result.add(new ModuleButton(created));
			}
		}
		else
		{
			throw (new SimException("not valid file"));
		}
		
		return result;
	}
	
	private void readBlank(Reader in) throws SimException
	{
		try
		{
			byte readIn[] = new byte[1];
			String converted;
			
			readIn[0] = (byte)in.read();
			if(readIn[0] == -1)
				throw (new SimException("incorrect file format"));
			converted = new String(readIn);
			
			if(! converted.equals((new Character(GuiFileLink.BLANK)).toString()))
				throw (new SimException("incorrect component arrangement"));
		}
		catch(IOException e)
		{
			throw (new SimException("IO exception has occured"));
		}
	}
	
	private String[] extractParameters(int size, Reader in) throws SimException
	{
		String[] result = new String[size];
		
		for(int index = 0; index < size; index++)
			result[index] = this.extractParameter(in);
		
		return result;
	}
	
	private String extractParameter(Reader in) throws SimException
	{
		String result = null;
		String wSeparator = (new Character(Wrapper.SEPARATOR)).toString();
		
		try
		{
			boolean finished = false;
			byte readIn[] = new byte[1];
			String converted;
			
			readIn[0] = (byte)in.read();
			if(readIn[0] == -1)
				throw (new SimException("incorrect file format"));
			converted = new String(readIn);
			
			if(!converted.equals(wSeparator))
				result = converted;
			else
				finished = true;
			
			while(!finished)
			{
				readIn[0] = (byte)in.read();
				if(readIn[0] == -1)
					throw (new SimException("incorrect file format"));		
				converted = new String(readIn);
				
				if(!converted.equals(wSeparator))
					result = result + converted;
				else
					finished = true;
			}
		}
		catch(IOException e)
		{
			throw (new SimException("IO exception has occured"));
		}
		
		return result;
	}
	
	private Wrapper getWrapper(String name) throws SimException
	{
		Object result = null;
		
		try
		{
			Class comp = Class.forName(name);
			Constructor con = comp.getConstructor(null);
			result = con.newInstance(null);
		}
		catch(ClassNotFoundException e)
		{
			// from Class.forName(name)
			throw (new SimException("component does not exist"));
		}
		catch(NoSuchMethodException e)
		{
			// from comp.getConstructor(parameters)
			throw (new SimException("component does not have required constructor"));
		}
		catch(SecurityException e)
		{
			// from comp.getConstructor(parameters)
			throw (new SimException("access to constructor was denied"));
		}
		catch(InstantiationException e)
		{
			// from con.newInstance(arguments)
			throw (new SimException("component is abstract"));
		}
		catch(IllegalAccessException e)
		{
			// from con.newInstance(arguments)
			throw (new SimException("illegal constructor access"));
		}
		catch(IllegalArgumentException e)
		{
			// from con.newInstance(arguments)
			throw (new SimException("illegal arguments passed to constructor"));
		}
		catch(InvocationTargetException e)
		{
			// from con.newInstance(arguments)
			throw (new SimException("constructor generated exception"));
		}
		
		if(!(result instanceof Wrapper))
		{
			throw (new SimException("incorrect component type"));
		}
		
		return (Wrapper)result;
	}
	
	private CreationModule getCreationModule(String name) throws SimException
	{
		Object result = null;
		
		try
		{
			Class comp = Class.forName(name);
			Constructor con = comp.getConstructor(null);
			result = con.newInstance(null);
		}
		catch(ClassNotFoundException e)
		{
			// from Class.forName(name)
			throw (new SimException("component does not exist"));
		}
		catch(NoSuchMethodException e)
		{
			// from comp.getConstructor(parameters)
			throw (new SimException("component does not have required constructor"));
		}
		catch(SecurityException e)
		{
			// from comp.getConstructor(parameters)
			throw (new SimException("access to constructor was denied"));
		}
		catch(InstantiationException e)
		{
			// from con.newInstance(arguments)
			throw (new SimException("component is abstract"));
		}
		catch(IllegalAccessException e)
		{
			// from con.newInstance(arguments)
			throw (new SimException("illegal constructor access"));
		}
		catch(IllegalArgumentException e)
		{
			// from con.newInstance(arguments)
			throw (new SimException("illegal arguments passed to constructor"));
		}
		catch(InvocationTargetException e)
		{
			// from con.newInstance(arguments)
			throw (new SimException("constructor generated exception"));
		}
		
		if(!(result instanceof CreationModule))
		{
			throw (new SimException("incorrect component type"));
		}
		
		return (CreationModule)result;
	}
	
/* ==================================================================
	Save Provided to SaveLoadShortcut
	================================================================= */
	public void save(Writer out, Grid g) throws SimException
	{
		try
		{
			out.write(this.getData(g));
		}
		catch(IOException e)
		{
			throw (new SimException("IO exception has occured"));
		}
	}
	
	private String getData(Grid target)
	{
		int start = target.getNumberOfJunctions();
		int end = start + target.getNumberOfComponents() + target.getNumberOfWires() + target.getNumberOfSplitters();
		Wrapper toSave;
		
		// Version of the program
		String toStore = MainWindow.VERSION + Wrapper.SEPARATOR + GuiFileLink.BLANK;
		
		// Grid properties
		toStore = toStore + target.getParameters() + GuiFileLink.BLANK;
		
		// Number of components
		toStore = toStore + Integer.toString(end - start) + Wrapper.SEPARATOR + GuiFileLink.BLANK;
		
		// Components
		for(int index = start; index < end; index++)
		{
			// Type and Name
			toSave = (Wrapper)target.getComponent(index);
			toStore = toStore + toSave.getClass().getName() + Wrapper.SEPARATOR;
			toStore = toStore + toSave.getName() + Wrapper.SEPARATOR;
			
			// Parameters
			toStore = toStore + toSave.getParameters();
			toStore = toStore + GuiFileLink.BLANK;
		}
		
		return toStore;
	}
	
}
