public class FileStoreInterface 
{
	private static final int BUFFER = 8192;

	public String root = "/";
	
	//Locked files, with temp files that have been changed
	
	//This class is used to make accessing and changing large collections of files more reliable
	//The MVStore database used in the DatabaseInterface is fast and reliable 
	//however it is designed to load stored data entirely into memory
	//if large files, such as videos, are stored directly in the database 
	//then when they are accessed they will be entirely loaded into the computers memory
	//this will probably crash the program or at the very least make it run unreliably
	//for this reason larger files are typically stored directly on the hard disk or remote
	//server
	//
	public FileStoreInterface(String rootpath)
	{
		File rootf = new File(rootpath);
		try
		{
			root = rootf.getCanonicalPath();
		}
		catch(Exception e)
		{
			System.out.println(e.toString());
		}
	}
	
	public void createAllParentDirectories(String dir)
	{
		String[] split = dir.split("/");
		String path = "";
		for(int i=1;i<(split.length-1);i++)
		{
			path += "/"+split[i];
			if(!exists(path))
				createDirectory(path);
		}
	}

	public String decodeFilePath( String uri )
	{
		String path = WebSecurity.decodePercent(uri);

		try
		{
			File f = new File(root+"/"+path);
			String canonical = f.getCanonicalPath();
			if(canonical.startsWith(root))
				return canonical;
			else
				return null;
		}
		catch(Exception e)
		{
			return null;
		}
	}

	public long lockForRead(String file)
	{
		//is the file already locked?
		return 0;
	}
	
	public long lockForWrite(String file)
	{
		//is the file already locked?
		return 0;
	}
	
	public void unlockForRead(String file,long id)
	{
		
	}
	public void unlockForWrite(String file,long id)
	{
		
	}
	
	//A queue of file operations that have been blocked because of a lock on the file
	public void processQueue()
	{
		//Go through the file operations in the queue and if the file is not locked
	}
	
	public boolean exists(String dir)
	{
		File dirf = new File(dir);
		return dirf.exists()&&dirf.isFile();
	}


	public String newuniquefile(String in)
	{
		if(!exists(in))
			return in;
		int num = 0;
		while(true)
		{
			String tryf = in+num;
			if(!exists(tryf))
				return tryf;
			num++;
		}
	}

	public boolean copyFile(String in, String out) 
	{
		File inf = new File(in);
		File outf = new File(out);
		return copyFile(inf, outf);
	}
	public boolean swapFiles(String in, String out) 
	{
		File inf = new File(in);
		File outf = new File(out);
		File outft = new File(newuniquefile(out));
		inf.renameTo(outft);
		outf.renameTo(inf);
		outft.renameTo(outf);
		return true;
	}
	public boolean moveFile(File file,File intodir)
	{
		boolean success = file.renameTo(new File(intodir, file.getName()));
		return success;
	}
	
	public static void deepCopy(File dir,String toFolder,GeneralMatrixString replace)
	{
		(new File(toFolder)).mkdir();
		File[] cs = dir.listFiles();
		for(int i=0;i<cs.length;i++)
		{
			if(cs[i].isFile())
			{
				GeneralMatrixString lines = new GeneralMatrixString(1);
				TextImportExport.loadTextFile(cs[i].getAbsolutePath(), lines);
				for(int li=0;li<lines.height;li++)
				{
					for(int ri=0;ri<replace.height;ri++)
					{
						String in = replace.value[ri*2+0];
						String out = replace.value[ri*2+1];
						
						if(lines.value[li].contains(in))
						{
							System.out.println(in);
						}
						lines.value[li] = lines.value[li].replace(in, out);
					}
				}
				TextImportExport.saveTextFile(toFolder+"/"+cs[i].getName(), lines);;
			}
			else
			{
				deepCopy(cs[i],toFolder+"/"+cs[i].getName(),replace);
			}
		}
	}

	public void deepCopy(File dir,String toFolder)
	{
		(new File(toFolder)).mkdir();
		File[] cs = dir.listFiles();
		for(int i=0;i<cs.length;i++)
		{
			if(cs[i].isFile())
			{
				copyFile(cs[i], new File(toFolder+"/"+cs[i].getName()));
			}
			else
			{
				deepCopy(cs[i],toFolder+"/"+cs[i].getName());
			}
		}
	}

	private static void close(Closeable closable) {
	    if (closable != null) {
	        try {
	            closable.close();
	        } catch (IOException e) {
	            e.printStackTrace();
	        }
	    }
	}
	
	public void createDirectory(String dir)
	{
		boolean success = (new File(dir)).mkdir();
	    if (success) {
	      System.out.println("Directory: " + dir + " created");
	    }    		
	    else
	    {
	    	System.out.println("!Directory: " + dir + " not created");
	    }
	}

	public boolean copyFile(File source, File target) 
	{
		if(source.isDirectory())
		{
			createDirectory(target.getAbsolutePath());
			return true;
		}
		
        FileChannel in = null;
        FileChannel out = null;

        boolean success = true;
        try {
            in = new FileInputStream(source).getChannel();
            out = new FileOutputStream(target).getChannel();

            ByteBuffer buffer = ByteBuffer.allocateDirect(BUFFER);
            while (in.read(buffer) != -1) {
                buffer.flip();

                while(buffer.hasRemaining()){
                    out.write(buffer);
                }

                buffer.clear();
            }
        } catch (IOException e) {
        	success = false;
            e.printStackTrace();
        } finally {
            close(in);
            close(out);
        }

	    return success;
	  }
	  
	public void deleteFile(File in) 
	{
		in.delete();
	}
	public void deleteFile(String in) 
	{
		new File(in).delete();
	}
	
	public static void deleteDirectory(String dir)
	{
		File dirf = new File(dir);
		deleteDirectory(dirf);
	}
	public static void deleteDirectory(File dirf)
	{
		if( dirf.exists() ) {
		      File[] files = dirf.listFiles();
		      for(int i=0; i<files.length; i++) {
		         if(files[i].isDirectory()) 
		         {
		           deleteDirectory(files[i]);
		         }
		         else {
		           files[i].delete();
		         }
		      }
		      dirf.delete();
		}
	}

	public void rename(String from,String to) 
	{
		File f = new File(from);
		File fto = new File(to);
		if(fto.exists())
		{
			System.out.println(to+":exists");
		}
		else
			f.renameTo(fto);
	}
} public class WebClient
{
	public String root = "http://localhost";
	public String rootSSL = "https://localhost";
	//the web requests to be processed by the client
	ArrayList<ArrayList<String> > queue = new ArrayList<ArrayList<String> >();

	public WebClient()
	{
	}
	
	public WebClient(String root)
	{
		this.root = root;
	}
	
	public static void setupSSL()
	{
//    	System.setProperty("javax.net.ssl.trustStore", f.getAbsolutePath());
//		keytool -import -alias gridserver -file gridserver.crt -storepass $PASS -keystore gridserver.keystore
//		
//		-Djavax.net.ssl.keyStoreType=pkcs12
//				-Djavax.net.ssl.trustStoreType=jks
//				-Djavax.net.ssl.keyStore=clientcertificate.p12
//				-Djavax.net.ssl.trustStore=gridserver.keystore
//				-Djavax.net.debug=ssl # very verbose debug
//				-Djavax.net.ssl.keyStorePassword=$PASS
//				-Djavax.net.ssl.trustStorePassword=$PASS		
		disableCertificateValidation();
	}
	public static void disableCertificateValidation() 
	{
	    // Create a trust manager that does not validate certificate chains
	    TrustManager[] trustAllCerts = new TrustManager[] 
	    { 
	      new TrustAllManager() 
	    };

	    // Ignore differences between given hostname and certificate hostname
	    HostnameVerifier hv = new TrustAllHostnameVerifier();
	    
	    // Install the all-trusting trust manager
	    try 
	    {
	      SSLContext sc = SSLContext.getInstance("SSL");
	      sc.init(null, trustAllCerts, new SecureRandom());
	      HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
	      HttpsURLConnection.setDefaultHostnameVerifier(hv);
	    } catch (Exception e) {}
	}

	public synchronized void getQueue(ArrayList<ArrayList<String> > q)
	{
	    q.addAll(queue);
	    queue.clear();
	}
	public synchronized void queue(ArrayList<String> e)
	{
		queue.add(e);
	}

	public void post(String path,HashMap<String,String> parameters)
	{
		String paramstring = "";
		Iterator<Map.Entry<String, String> > it = parameters.entrySet().iterator();
		boolean first = true;
	    while (it.hasNext()) {
	    	Map.Entry<String, String> pair = it.next();
			if(!first)
				paramstring += "&";
			paramstring += WebSecurity.encodeUri(pair.getKey())+"="+WebSecurity.encodeUri(pair.getValue());			
	    	first = false;
	    }

		new WebClientRequest(this,root+path,paramstring);
	}
	
	public String objAsString(String p, Object o)
	{
		if(o instanceof String)
		{
			return (String)o;
		}
		return "null";
	}
	
	public void post(String path,String p0,Object o0,File f)
	{
		String res = "";
		res += WebSecurity.encodeUri(p0)+"="+WebSecurity.encodeUri(objAsString(p0,o0));
		new WebClientRequest(this,root+path,res,f);
	}

	public void post(String path,String p0,Object o0)
	{
		String res = "";
		res += WebSecurity.encodeUri(p0)+"="+WebSecurity.encodeUri(objAsString(p0,o0));
		new WebClientRequest(this,root+path,res);
	}
	
	public void post(String path,String p0,Object o0,String p1,Object o1,String p2,Object o2)
	{
		String res = "";
		res += WebSecurity.encodeUri(p0)+"="+WebSecurity.encodeUri(objAsString(p0,o0));
		res += "&";
		res += WebSecurity.encodeUri(p1)+"="+WebSecurity.encodeUri(objAsString(p1,o1));
		res += "&";
		res += WebSecurity.encodeUri(p2)+"="+WebSecurity.encodeUri(objAsString(p2,o2));
		new WebClientRequest(this,root+path,res);
	}
	
	public void post(String path)
	{
		new WebClientRequest(this,root+path,"");
	}
	
	public void postSSL(String path)
	{
		new WebClientRequest(this,rootSSL+path,"",true);
	}
	
} public class WebInterface implements Runnable
{
	int port = 8080;
	//the web requests to be processed by the client
	ArrayList<WebRequest> queue = new ArrayList<WebRequest>();
	public String path = null;
	
	public WebInterface(int port)
	{
		this.port = port;
		Thread t = new Thread( this );
		t.setDaemon( true );
		t.start();
	}
	
	public synchronized void getQueue(ArrayList<WebRequest> q)
	{
	    q.addAll(queue);
	    queue.clear();
	}
	public synchronized void queue(WebRequest e)
	{
		queue.add(e);
	}
	
	public void run()
	{
		try
		{
			final ServerSocket ss = new ServerSocket( port );
			while( true )
				new WebRequest( this, ss.accept() );
		}
		catch ( IOException ioe )
		{}
	}

	public void identifyURL()
	{
		try
		{			
            	boolean hasmac = false;
		       System.out.println("Host addr: " + InetAddress.getLocalHost().getHostAddress());  // often returns "127.0.0.1"
               Enumeration<NetworkInterface> n = NetworkInterface.getNetworkInterfaces();
               for (; n.hasMoreElements();)
               {
                       NetworkInterface e = n.nextElement();
                       System.out.println("Interface: " + e.getName());
                       Enumeration<InetAddress> a = e.getInetAddresses();
                       for (; a.hasMoreElements();)
                       {
                               InetAddress addr = a.nextElement();
                               String haddress = addr.getHostAddress();
                        	   if(!haddress.contains("."))
                        	   {
                        		   hasmac = true;
                        	   }
                               if(path==null)
                               {
                            	   if(haddress.contains("."))
                            	   {
                            		   path = haddress+":"+port;
                            	   }
                               }
                               System.out.println("  " + haddress);
                       }
                       if(path!=null)
                       {
                    	   if(hasmac==false)
                    		   path = null;
                       }
               }
		}
		catch(Exception e)
		{
			System.out.println(e.toString());
		}		
	}
} public class WebRequest implements Runnable
{
	WebInterface winterface;
	private Socket socket;
	InputStream in;
	public WebResponse r = null;
	public String path;
	public HashMap<String,String> parms = new HashMap<String,String>();
	public HashMap<String,String> header = new HashMap<String,String>();
	public HashMap<String,String> cookies = new HashMap<String,String>();
	
	
	private static java.text.SimpleDateFormat gmtFrmt;
	static
	{
		gmtFrmt = new java.text.SimpleDateFormat( "E, d MMM yyyy HH:mm:ss 'GMT'", Locale.UK);
		gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT"));
	}
	
	public boolean isSet(String param)
	{
		if(parms.containsKey(param))
		{
			String res = parms.get(param);
			if(res.equalsIgnoreCase("true"))
				return true;
		}
		return false;
	}
	
	public WebRequest(WebInterface i, Socket s)
	{
		winterface = i;
		socket = s;

		Thread t = new Thread( this );
		t.setDaemon( true );
		t.start();
	}
	
	public void run()
	{
		try
		{
			if(r==null)
			{
				InputStream is = socket.getInputStream();
				if ( is == null) return;
				in = is;
				
				// Read the request line
				String inLine = readline(in);
				if (inLine == null) return;
				StringTokenizer st = new StringTokenizer( inLine );
				if ( !st.hasMoreTokens())
					sendError( WebResponse.HTTP_BADREQUEST, "BAD REQUEST: Syntax error. Usage: GET /example/file.html" );
	
				String method = st.nextToken();
	
				if ( !st.hasMoreTokens())
					sendError( WebResponse.HTTP_BADREQUEST, "BAD REQUEST: Missing URI. Usage: GET /example/file.html" );
	
				String uri = st.nextToken();
	
				// Decode parameters from the URI
				int qmi = uri.indexOf( '?' );
				if ( qmi >= 0 )
				{
					decodeParms( uri.substring( qmi+1 ), parms );
					uri = WebSecurity.decodePercent( uri.substring( 0, qmi ));
				}
				else uri = WebSecurity.decodePercent(uri);
	
				path = uri.substring(1);
				//can have hidden params
	
				// If there's another token, it's protocol version,
				// followed by HTTP headers. Ignore version but parse headers.
				// NOTE: this now forces header names uppercase since they are
				// case insensitive and vary by client.
				//HashMap<String,String> header = new HashMap<String,String>();
				if ( st.hasMoreTokens())
				{
					String line = readline(in);
					while ( line.trim().length() > 0 )
					{
						int p = line.indexOf( ':' );
						if(p==-1)
						{
							line = readline(in);
							continue;
						}
						String key = line.substring(0,p).trim().toLowerCase();
						String val = line.substring(p+1).trim();
						if(key.equalsIgnoreCase("Cookie"))
						{
							String[] pairs = val.split(";");
							for(int i=0;i<pairs.length;i++)
							{
								int ind = pairs[i].indexOf("=");
								int find = 0;
								if(i!=0)
									find = 1;
								String ck = pairs[i].substring(find,ind);
								String cv = pairs[i].substring(ind+1);
								cookies.put(ck, cv);
								System.out.println("cookie:"+ck+"="+cv);
							}
						}
						else
						{
							header.put( key, val );
						}
						line = readline(in);
					}
				}
	
				// If the method is POST, there may be parameters
				// in data section, too, read it:
				if ( method.equalsIgnoreCase( "POST" ))
				{
					long size = 0x7FFFFFFFFFFFFFFFl;
					String contentLength = header.get("content-length");
					if (contentLength != null)
					{
						try { size = Integer.parseInt(contentLength); }
						catch (NumberFormatException ex) {}
					}
					int maxSize = 512*1024*1024;
					if(size>maxSize)
					{
						System.out.println("Max upload file size exceeded");
						size = maxSize;
					}
	
					String contentType = header.get("content-type");
					
					if(contentType==null)
					{
						
					}
					else
					if(contentType.startsWith("multipart/form-data;"))
					{						
						System.out.println("multipart/form-data;");
						parms.put("multipart", null);
						
						saveMultipart("tempMultipartFiles");
					}
					else
					if(contentType.startsWith("application/octet-stream;"))
					{
						System.out.println("application/octet-stream;");
						//Don't parse as parameters
					}
					else
					{
						String postLine = "";
						byte buf[] = new byte[(int)size];
						int read = in.read(buf);
						while ( read >= 0 && size > 0 && !postLine.endsWith("\r\n") )
						{
							size -= read;
							postLine += new String(buf, "UTF-8");//String.valueOf(buf, 0, read);
							if ( size > 0 )
								read = in.read(buf);
						}
						postLine = postLine.trim();
						decodeParms( postLine, parms );
					}
				}
				winterface.queue(this);
			}
			else
			{
				sendResponse( r.status, r.location, r.mimeType, r.header, r.data, r.datacallback, r.datacallbackMetadata, r.range );
				in.close();
			}
		}
		catch ( IOException ioe )
		{
			try
			{
				sendError( WebResponse.HTTP_INTERNALERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
			}
			catch ( Throwable t ) {}
		}
		catch ( InterruptedException ie )
		{
			// Thrown by sendError, ignore and exit the thread.
		}
	}


	/**
	 * Decodes parameters in percent-encoded URI-format
	 * ( e.g. "name=Jack%20Daniels&pass=Single%20Malt" ) and
	 * adds them to given Parameters. NOTE: this doesn't support multiple
	 * identical keys due to the simplicity of Parameters -- if you need multiples,
	 * you might want to replace the Parameters with a Hastable of Vectors or such.
	 */
	private void decodeParms( String parms, HashMap<String,String> p )
		throws InterruptedException
	{
		if ( parms == null )
			return;

		StringTokenizer st = new StringTokenizer( parms, "&" );
		while ( st.hasMoreTokens())
		{
			String e = st.nextToken();
			int sep = e.indexOf( '=' );
			if ( sep >= 0 )
				p.put( WebSecurity.decodePercent( e.substring( 0, sep )).trim(),
					   WebSecurity.decodePercent( e.substring( sep+1 )));
		}
	}

	/**
	 * Returns an error message as a HTTP response and
	 * throws InterruptedException to stop further request processing.
	 */
	private void sendError( String status, String msg ) throws InterruptedException
	{
		sendResponse( status, null, WebResponse.MIME_PLAINTEXT, null, new ByteArrayInputStream( msg.getBytes() ), null, null, -1);
		throw new InterruptedException();
	}

	/**
	 * Sends given response to the socket.
	 */
	private void sendResponse( String status, String location, String mime, HashMap<String,String> header, InputStream data, OutputStreamCallback datacallback, Object outputStreamMetadata, long range )
	{
		try
		{
			if ( status == null )
				throw new Error( "sendResponse(): Status can't be null." );

			//System.out.println(status+" "+mime);
			OutputStream out = socket.getOutputStream();
			PrintWriter pw = new PrintWriter( out );
			pw.print("HTTP/1.0 " + status + " \r\n");

			if ( location != null )
				pw.print("Location: " + location + "\r\n");

			if ( mime != null )
				pw.print("Content-Type: " + mime + "\r\n");

			if ( header == null || header.get( "Date" ) == null )
				pw.print( "Date: " + gmtFrmt.format(new Date()) + "\r\n");

			if ( header != null )
			{
				Iterator<Map.Entry<String,String> > it = header.entrySet().iterator();
			    while (it.hasNext()) {
			        Map.Entry<String,String> pair = (Map.Entry<String,String>)it.next();
			        //System.out.println(pair.getKey() + " = " + pair.getValue());
					pw.print( pair.getKey() + ": " + pair.getValue() + "\r\n");
			        //it.remove();
			    }
			}

			pw.print("\r\n");
			pw.flush();

			if( datacallback != null)
			{
				datacallback.out(this,outputStreamMetadata,out);
			}
			else
			if ( data != null )
			{
				byte[] buff = new byte[2048];
				if(range==-1)
				{
					while (true)
					{
						int read = data.read( buff, 0, 2048 );
						if (read <= 0)
							break;
						out.write( buff, 0, read );
					}
				}
				else
				{
					long rem = range;
					while (true)
					{
						int read = data.read( buff, 0, 2048 );
						if (read <= 0)
							break;
						if(rem>=read)
						{
							out.write( buff, 0, read );
							rem -= read;
						}
						else
						{
							out.write( buff, 0, (int)rem );
							break;
						}
					}
					
				}
			}

			out.flush();
			out.close();
			
			if ( data != null )
				data.close();
			
			socket.close();
		}
		catch( IOException ioe )
		{
			// Couldn't write? No can do.
			try 
			{ 
				socket.close(); 
			} 
			catch( Throwable t ) 
			{
				
			}
		}
	}
	
	//*
	public void saveMultipart(String to)
	{
//		String test = TextImportExport.readline(in);
//		while(test!=null)
//		{
//			System.out.println(test);
//			test = TextImportExport.readline(in);
//		}

		String boundary = readline(in);
		if((boundary!=null)&&(boundary.length()>3))
		{
			boundary = boundary.substring(2);
			
			/*
			try
			{
				MultipartStream multipartStream = new MultipartStream(in, boundary.getBytes());
				boolean nextPart = true;//multipartStream.skipPreamble();
				 while(nextPart) 
				 {
					 String header = multipartStream.readHeaders();
					 int ind = header.indexOf("name=");
					 String name = null;
					 if(ind!=-1)
					 {
						 String rest = header.substring(ind+"name=\"".length());
						 ind = rest.indexOf("\"");
						 name = rest.substring(0,ind);
					 }
					 
					 ind = header.indexOf("filename=");
					 if(ind!=-1)
					 {
						 String rest = header.substring(ind+"filename=\"".length());
						 ind = rest.indexOf("\"");
						 String filename = rest.substring(0,ind);
						 
						 File root = new File(to);
						 File filenamepath = new File(to+"/"+filename);
						 //only allow saving into this temp folder
						 if(filenamepath.getAbsolutePath().startsWith(root.getAbsolutePath()))
						 {
							 ind = filename.lastIndexOf('.');
							 
							 String uniquename = filename;
							 long t = System.currentTimeMillis();
							 
							 if(ind!=-1)
							 {
								 String ending = filename.substring(ind+1);
								 
								 uniquename = filename.substring(0,ind);

								 uniquename += "_"+t+"."+ending;
							 }
							 else
							 {
								 uniquename += "_"+t;
							 }
							 // process headers
							 OutputStream output = new FileOutputStream(to+"/"+uniquename);
							 // create some output stream
							 multipartStream.readBodyData(output);
							 output.flush();
							 output.close();
							 
							 parms.put(name, to+"/"+uniquename);
						 }
						 else
						 {
							 multipartStream.discardBodyData();						 
						 }
					 }
					 else
					 {
						 if(name!=null)
						 {
							 ByteArrayOutputStream baos = new ByteArrayOutputStream(10*1024*1024);
							 multipartStream.readBodyData(baos);
							 String asString = new String(baos.toByteArray(), StandardCharsets.UTF_8);
							 parms.put(name, asString);
						 }
						 multipartStream.discardBodyData();						 
					 }
					 nextPart = multipartStream.readBoundary();
				 }
			 } 
			catch(Exception e) 
			 {
				System.out.println(e.toString());
			 // the stream failed to follow required syntax
			 }
			 */
		}
	}

    public static String readline(InputStream bis)
    {
    	boolean empty = true;
    	String result = "";
    	try
    	{
	    	while(true)
	    	{
	    		int c = bis.read();
	    		if(c=='\r')
	    		{
	    			//read a line feed
	    			bis.read();
	    			return result;
	    		}
	    		else
	    		if(c=='\n')
	    		{	    			
		    		return result;
	    		}
	    		else
	    		if(c==-1)
	    		{
	    			
	    		}
	    		else
	    		{
	    			empty = false;
	    			result += (char)c;
	    		}
	    		if(c==-1)
	    		{
	    			if(empty)
	    				return null;
	    			else
	    				return result;
	    		}
	    	}
    	}
    	catch(Exception e)
    	{
    		System.out.println(e.toString());
    	}
    	return null;
    }
	
} public class WebResponse 
{	
	//HTTP status code after processing, e.g. "200 OK", HTTP_OK
	public String status;

	//MIME type of content, e.g. "text/html"
	public String mimeType;

	public String location = null;
	
	//Data of the response, may be null.
	public InputStream data;
	public long range = -1;
	//Function to generate an output stream (if the data is too large to fit in memory)
	public OutputStreamCallback datacallback = null;
	public Object datacallbackMetadata = null;
	
	/**
	 * Headers for the HTTP response. Use addHeader()
	 * to add lines.
	 */
	public HashMap<String,String> header = new HashMap<String,String>();

	public WebResponse()
	{
		this.status = HTTP_OK;
	}

	/**
	 * Some HTTP response status codes
	 */
	public static final String
	    HTTP_OK = "200 OK",
	    HTTP_PARTIAL = "206 Partial Content",
		HTTP_PERMANENT_REDIRECT = "301 Moved Permanently",
	    HTTP_REDIRECT = "307 Temporary Redirect",
		HTTP_FORBIDDEN = "403 Forbidden",
		HTTP_NOTFOUND = "404 Not Found",
		HTTP_BADREQUEST = "400 Bad Request",
		HTTP_INTERNALERROR = "500 Internal Server Error",
		HTTP_NOTIMPLEMENTED = "501 Not Implemented";

	//MIME types of different files
	public static final String
		MIME_PLAINTEXT = "text/plain",
		MIME_HTML = "text/html",
		MIME_JAVASCRIPT = "text/javascript",
		MIME_DEFAULT_BINARY = "application/octet-stream";

	private static Hashtable theMimeTypes = new Hashtable();
	static
	{
		StringTokenizer st = new StringTokenizer(
			"htm		text/html "+
			"html		text/html "+
			"css		text/css "+
			"js			application/javascript "+
			"txt		text/plain "+
			"asc		text/plain "+
			"gif		image/gif "+
			"jpg		image/jpeg "+
			"jpeg		image/jpeg "+
			"..			image/jpeg "+
			"png		image/png "+
			"mp3		audio/mpeg "+
			"m3u		audio/mpeg-url " +
			//"avi		video/mp4 "+
			"mp4		video/mp4 "+
			"m4v		video/mp4 "+
			//"webm		video/webm "+
			"pdf		application/pdf "+
			"doc		application/msword "+
			"ogg		application/x-ogg "+
			"zip		application/octet-stream "+
			"exe		application/octet-stream "+
			"class		application/octet-stream " );
		while ( st.hasMoreTokens())
			theMimeTypes.put( st.nextToken(), st.nextToken());
	}

	public static WebResponse serveFile(HashMap<String, String> header, String path)
	{
		try
		{
			File f = new File(path);
			// Get MIME type from file name extension, if possible
			String mime = null;
			int dot = f.getCanonicalPath().lastIndexOf( '.' );
			if ( dot >= 0 )
				mime = (String)theMimeTypes.get( f.getCanonicalPath().substring( dot + 1 ).toLowerCase());
			if ( mime == null )
				mime = MIME_DEFAULT_BINARY;
	
			FileInputStream fis = new FileInputStream( f );
			return serveInputstream(header, fis, mime, f.length());
		}
		catch(Exception e)
		{
			return null;
		}
	}
	
	public static WebResponse serveInputstream(HashMap<String, String> header,InputStream fis,String mime,long maxlen)
	{
		// Support (simple) skipping:
		long startFrom = 0;
		long endat = -1;
		long rangebytes = -1;
		String resulttype = HTTP_OK;
		String range = header.get( "Range" );
		String rangeend = null;
		if ( range != null )
		{
			System.out.println(range);
			if ( range.startsWith( "bytes=" ))
			{
				resulttype = HTTP_PARTIAL;
				range = range.substring( "bytes=".length());
				int minus = range.indexOf( '-' );
				if ( minus > 0 )
				{
					rangeend = range.substring( minus+1 );
					range = range.substring( 0, minus );
				}
				try	
				{
					startFrom = Long.parseLong( range );
					endat = Long.parseLong(rangeend);
				}
				catch ( NumberFormatException nfe ) 
				{
					rangebytes = 512*1024;
					if(rangebytes>(maxlen-startFrom))
					{
						resulttype = HTTP_OK;
						rangebytes = (maxlen-startFrom);
					}
					else
					{
//						resulttype = HTTP_OK;
						resulttype = HTTP_PARTIAL;						
					}
//					rangebytes = (maxlen-startFrom);
				}
				
			}
			if(endat!=-1)
			{
				rangebytes = endat-startFrom;
			}
			else
			{
//				rangebytes = 512*1024;
//				if(rangebytes>(maxlen-startFrom))
					rangebytes = (maxlen-startFrom);
			}
		}
		else
		{
			rangebytes = maxlen-startFrom;
		}

		try
		{
			fis.skip( startFrom );
		}
		catch(Exception e)
		{
			System.out.println(e.toString());
		}
		WebResponse r = new WebResponse( resulttype, mime, rangebytes, fis );
		r.addHeader( "Content-length", "" + (rangebytes));
		//bytes 0-499/1234
		r.addHeader( "Content-range", "bytes " + startFrom + "-" +
					(startFrom+rangebytes-1) + "/" + maxlen);
		
		return r;
	}

	public WebResponse( String status, String mimeType, long rng, InputStream data )
	{
		this.status = status;
		this.mimeType = mimeType;
		this.data = data;
		this.range = rng;
	}

	public WebResponse( String status, String mimeType, OutputStreamCallback data, Object metadata )
	{
		this.status = status;
		this.mimeType = mimeType;
		this.datacallback = data;
		this.datacallbackMetadata = metadata;
	}
	
	/**
	 * Convenience method that makes an InputStream out of
	 * given text.
	 */
	public WebResponse( String status, String location, String mimeType, String txt )
	{
		this.status = status;
		this.location = location;
		this.mimeType = mimeType;
		this.data = new ByteArrayInputStream(txt.getBytes());
	}

	/**
	 * Convenience method that makes an InputStream out of
	 * given text.
	 */
	public WebResponse( String status, String mimeType, String txt )
	{
		this.status = status;
		this.mimeType = mimeType;
		this.data = new ByteArrayInputStream(txt.getBytes());
	}

	/**
	 * Adds given line to the header.
	 */
	public void addHeader( String name, String value )
	{
		header.put( name, value );
	}

} public abstract class DynamicWebPage implements OutputStreamCallback
{
	public FileStoreInterface fs = null;
	
	public DynamicWebPage(FileStoreInterface fs)
	{
		this.fs = fs;
	}
	
	public abstract boolean process(WebRequest toProcess);

} public class EditMapInstance extends DynamicWebPage
{
	public EditMapInstance(FileStoreInterface fs)
	{
		super(fs);
	}
	public boolean process(WebRequest toProcess)
	{
        if(toProcess.path.startsWith("EditMapInstance.html"))
        {
        	return true;
        }
        else
        if(toProcess.path.startsWith("EditMapInstance.cgi"))
        {
    		String id = toProcess.parms.get("instance");
    		int ind = id.indexOf("/");
    		
    		String concept = id.substring(0,ind);
    		String instance = id.substring(ind+1);

    		String instancepath = "Concepts/"+concept+"/instances/"+instance;    		
    		String datapath = instancepath+"/data.txt";

    		ObjectLanguageInstance o = ObjectLanguageInstance.loadObject(fs, datapath);
    		if(o==null)
    		{
    			o = new ObjectLanguageInstance();
    			o.uniqueID = concept+"/"+instance;
    			o.name = instance;
    			o.type = "map";
    			
    			o.physicalRepresentation = new ObjectPhysicalInstance();
    		}

    		ind = toProcess.path.indexOf("/");
    		String function = toProcess.path.substring(ind+1);
    		if(function.equalsIgnoreCase("setShape"))
    		{
    			String shape = toProcess.parms.get("shape");
    			ParametricShape oldshape = o.physicalRepresentation.shape;
    			o.physicalRepresentation.shape = ParametricShape.get(shape);
    			if(oldshape!=null)
    			{
    				o.physicalRepresentation.shape.makeSimilarSize(oldshape);
    			}
    		}
    		else
    		if(function.equalsIgnoreCase("setShapeParameters"))
    		{
    			ParametricShape shape = o.physicalRepresentation.shape;
    			String[] pnames = shape.getParameterNames();
    			
    			for(int i=0;i<pnames.length;i++)
    			{
    				String v = toProcess.parms.get(pnames[i]);
    				shape.parameters[i] = Double.parseDouble(v);
    			}
    			shape.parametersUpdated();
    		}
    		else
        	if(function.equalsIgnoreCase("addExternalPortal"))
    		{
    			//Create a default instance of a portal
    			ObjectLanguageInstance r = new ObjectLanguageInstance();
//    			name
//    			type
    			r.name = toProcess.parms.get("name");
    			r.name = r.name.replace(' ', '_');
    			r.type = "portal";
    			String roomConcept = toProcess.parms.get("type");
    			String roomInstance = instance+"_"+r.name;
    			r.uniqueID = roomConcept+"/"+roomInstance;
    			
    			//only add the instance if it doesn't already exist
    			int existsind = ArrayUtils.getIndex(o.contains, r.uniqueID);
    			if(existsind==-1)
    			{	
    			
	    			String rpath = "Concepts/"+roomConcept+"/instances/";
	    			fs.createDirectory(rpath);
	    			rpath += roomInstance;
	    			fs.createDirectory(rpath);
	    			
	    			
	    			
	    			r.physicalRepresentation = new ObjectPhysicalInstance();
	    			ParametricShape shape = ParametricShape.get(toProcess.parms.get("shape"));
	    			//ParametricShape shape = new RectangularPortal();
	    			r.physicalRepresentation.shape = shape;
	
	    			shape.setCuboidDimensions(Double.parseDouble(toProcess.parms.get("width")), 
	    					Double.parseDouble(toProcess.parms.get("height")),
	    					Double.parseDouble(toProcess.parms.get("ceiling_height")));
	
	    			double x = Double.parseDouble(toProcess.parms.get("x"));
	    			double y = Double.parseDouble(toProcess.parms.get("y"));
	    			double z = Double.parseDouble(toProcess.parms.get("z"));
	    			o.add(r);
	    			o.physicalRepresentation.add(r.physicalRepresentation, 
	    					x,y,z);
	    			rpath += "/data.txt";
	    			ObjectLanguageInstance.saveObject(r, fs, rpath);
	    			
	    			String connectingRoom = toProcess.parms.get("connectTo");
	    			if(connectingRoom!=null)
	    			{
		    			String crpath = ObjectLanguageInstance.uniqueIdToPath(connectingRoom);
		    			crpath += "/data.txt";
		    			ObjectLanguageInstance cr = ObjectLanguageInstance.loadObject(fs, crpath);
		    			cr.add(r);
		    			int crind = ArrayUtils.getIndex(o.contains,connectingRoom);
		    			double crx = o.physicalRepresentation.contains_metric_transform[crind*12+9];
		    			double cry = o.physicalRepresentation.contains_metric_transform[crind*12+10];
		    			double crz = o.physicalRepresentation.contains_metric_transform[crind*12+11];
		    			//relative position of portal to room
		    			cr.physicalRepresentation.add(r.physicalRepresentation, x-crx, y-cry, z-crz);
		    			ObjectLanguageInstance.saveObject(cr, fs, crpath);
	    			}

	    			connectingRoom = toProcess.parms.get("connectTo2");
	    			if(connectingRoom!=null)
	    			{
		    			String crpath = ObjectLanguageInstance.uniqueIdToPath(connectingRoom);
		    			crpath += "/data.txt";
		    			ObjectLanguageInstance cr = ObjectLanguageInstance.loadObject(fs, crpath);
		    			cr.add(r);
		    			int crind = ArrayUtils.getIndex(o.contains,connectingRoom);
		    			double crx = o.physicalRepresentation.contains_metric_transform[crind*12+9];
		    			double cry = o.physicalRepresentation.contains_metric_transform[crind*12+10];
		    			double crz = o.physicalRepresentation.contains_metric_transform[crind*12+11];
		    			//relative position of portal to room
		    			cr.physicalRepresentation.add(r.physicalRepresentation, x-crx, y-cry, z-crz);
		    			ObjectLanguageInstance.saveObject(cr, fs, crpath);
	    			}
    			}
    		}
    		else
    		if(function.equalsIgnoreCase("setContainedPosition"))
    		{
        		String rid = toProcess.parms.get("cinstance");
        		int rind = rid.indexOf("/");
        		
        		String rconcept = rid.substring(0,rind);
        		String rinstance = rid.substring(rind+1);

        		String rinstancepath = "Concepts/"+rconcept+"/instances/"+rinstance;    		
        		String rdatapath = rinstancepath+"/data.txt";

        		ObjectLanguageInstance r = ObjectLanguageInstance.loadObject(fs, rdatapath);
        		
        		double x = Double.parseDouble(toProcess.parms.get("x"));
        		double y = Double.parseDouble(toProcess.parms.get("y"));
        		double z = Double.parseDouble(toProcess.parms.get("z"));
        		
        		int cind = ArrayUtils.getIndex(o.contains,rid);
        		o.physicalRepresentation.contains_metric_transform[12*cind+ 9] = x;
        		o.physicalRepresentation.contains_metric_transform[12*cind+10] = y;
        		o.physicalRepresentation.contains_metric_transform[12*cind+11] = z;

        		int wind = ArrayUtils.getIndex(r.within,o.uniqueID);
        		r.physicalRepresentation.within_metric_transform[12*wind+ 9] = x;
        		r.physicalRepresentation.within_metric_transform[12*wind+10] = y;
        		r.physicalRepresentation.within_metric_transform[12*wind+11] = z;
        		
    			ObjectLanguageInstance.saveObject(r, fs, rdatapath);
    		}
    		else
    		if(function.equalsIgnoreCase("removeContained"))
    		{
    			//don't modify the room data at this point
        		String rid = toProcess.parms.get("cinstance");
        		int cind = ArrayUtils.getIndex(o.contains,rid);
        		o.physicalRepresentation.contains_metric_transform = ArrayUtils.remove(o.physicalRepresentation.contains_metric_transform,cind*12,12);
        		o.contains = ArrayUtils.remove(o.contains,cind,1);
        		o.contains_type = ArrayUtils.remove(o.contains_type,cind,1);
    		}
    		else
    		if(function.equalsIgnoreCase("addRoom"))
    		{
    			//Create a default instance of a room (using a model)
    			ObjectLanguageInstance r = new ObjectLanguageInstance();
//    			name
//    			type
    			r.name = toProcess.parms.get("name");
    			r.name = r.name.replace(' ', '_');
    			r.type = "room";
    			String roomConcept = toProcess.parms.get("type");
    			String roomInstance = instance+"_"+r.name;
    			r.uniqueID = roomConcept+"/"+roomInstance;
    			
    			//only add the instance if it doesn't already exist
    			int existsind = ArrayUtils.getIndex(o.contains, r.uniqueID);
    			if(existsind==-1)
    			{	
    			
	    			String rpath = "Concepts/"+roomConcept+"/instances/";
	    			fs.createDirectory(rpath);
	    			rpath += roomInstance;
	    			fs.createDirectory(rpath);
	    			
	    			
	    			
	    			r.physicalRepresentation = new ObjectPhysicalInstance();
	    			ParametricShape shape = ParametricShape.get(toProcess.parms.get("shape"));
	    			r.physicalRepresentation.shape = shape;
	
	    			shape.setCuboidDimensions(Double.parseDouble(toProcess.parms.get("width")), 
	    					Double.parseDouble(toProcess.parms.get("height")),
	    					Double.parseDouble(toProcess.parms.get("ceiling_height")));
	
	//    			x
	//    			y
	//    			z
	//    			width
	//    			height
	//    			ceiling_height
	//    			shape
	    			
	    			o.add(r);
	    			o.physicalRepresentation.add(r.physicalRepresentation, 
	    					Double.parseDouble(toProcess.parms.get("x")), 
	    					Double.parseDouble(toProcess.parms.get("y")),
	    					Double.parseDouble(toProcess.parms.get("z")));
	    			rpath += "/data.txt";
	    			ObjectLanguageInstance.saveObject(r, fs, rpath);
    			}
    		}
    		ObjectLanguageInstance.saveObject(o, fs, datapath);
    		
			toProcess.r = new WebResponse(WebResponse.HTTP_OK, WebResponse.MIME_HTML,"Success");
			
        	return true;
        }
        return false;
	}

	
	
	public void printMap(String concept,String instance,String instancepath,PrintWriter pw)
	{
		pw.append("<h1>"+concept+"/"+instance+"</h1>");
		
		
		boolean hasfloorplan = false;
		String floorplanpath = instancepath+"/floorplan.jpg";
		if(fs.exists(floorplanpath))
		{
			pw.append("<img src=\"/"+WebSecurity.encodeUri(floorplanpath)+"\">");
			hasfloorplan = true;
		}
		if(!hasfloorplan)
		{
			floorplanpath = instancepath+"/floorplan.png";
			if(fs.exists(floorplanpath))
			{
				pw.append("<img src=\"/"+WebSecurity.encodeUri(floorplanpath)+"\">");
				hasfloorplan = true;
			}
		}
		
		boolean hassatellite = false;
		String satellitepath = instancepath+"/satellite.jpg";
		if(fs.exists(satellitepath))
		{
			pw.append("<img src=\"/"+WebSecurity.encodeUri(satellitepath)+"\">");
			hassatellite = true;
		}
		if(!hassatellite)
		{
			satellitepath = instancepath+"/satellite.png";
			if(fs.exists(satellitepath))
			{
				pw.append("<img src=\"/"+WebSecurity.encodeUri(satellitepath)+"\">");
				hassatellite = true;
			}
		}
		if(!hassatellite)
		{
			//<div id="map" style="width:400px;height:400px">>My map will go here</div>
		}
		
		if(!hasfloorplan)
		{
			//create a form for uploading a floorplan image
		}
		if(!hassatellite)
		{
			//create a form for uploading a satellite image
		}

	}
	
	public void printShape(ObjectLanguageInstance o,PrintWriter pw)
	{
		//Set the parametric shape of this map
		if(o.physicalRepresentation.shape==null)
		{

		}
		else
		{
			
			StateToSVG.mapToSVG(pw,fs,o);
			//create or edit the shape of the map			

			//Adjust the shape parameters as appropriate			
		}

		pw.append("<h3>Set Shape:</h3>");
		pw.append("<form action='/EditMapInstance.cgi/setShape' method='get' role='form'>\n");
		
		pw.append("<select name=\"shape\">");
		
		for(int i=0;i<ParametricShape.buildingShapes.length;i++)
		{
			if((o.physicalRepresentation.shape!=null)&&(o.physicalRepresentation.shape.getClass().getName().equalsIgnoreCase(ParametricShape.buildingShapes[i])))
				pw.append("<option value=\""+ParametricShape.buildingShapes[i]+"\" selected>"+ParametricShape.buildingShapes[i]+"</option>");
			else
				pw.append("<option value=\""+ParametricShape.buildingShapes[i]+"\">"+ParametricShape.buildingShapes[i]+"</option>");
		}
		
		pw.append("</select>");

		pw.append("<input name=\"instance\" type=\"hidden\" value=\""+o.uniqueID+"\">");
		
		//pw.append("<a class=\"btn btn-primary btn-block\" href='http://www.google.com' onclick='return checkLogin()'>Login</a>");
		pw.append("<button class=\"btn btn-default\" type='submit'>Set</button>");
		pw.append("</form>");
		
		ParametricShape shape = o.physicalRepresentation.shape;
		if(shape!=null)
		{
			pw.append("<form action='/EditMapInstance.cgi/setShapeParameters' method='get' role='form'>\n");
			pw.append("<input name=\"instance\" type=\"hidden\" value=\""+o.uniqueID+"\">");
			String[] pnames = shape.getParameterNames();
			
			pw.append("<table>");
			for(int i=0;i<pnames.length;i++)
			{
				pw.append("<tr>");
				pw.append("<td>");
				pw.append(pnames[i]);
				pw.append("</td>");
				pw.append("<td>");
				pw.append("<input name=\""+pnames[i]+"\" type=\"number\" step=\"0.001\" value=\""+String.format( "%.3f", shape.parameters[i])+"\">");
				
				pw.append("</td>");
				pw.append("</tr>");
			}
			pw.append("</table>");
			pw.append("<button class=\"btn btn-default\" type='submit'>Set</button>");
			pw.append("</form>");
		}		
	}
	
	public void printExternalPortals(ObjectLanguageInstance o,PrintWriter pw)
	{
		//Go through the rooms in the map
		pw.append("<h2>Portals</h2>");

		printType(o, "Portal", "Map", pw, fs);

		pw.append("<h3>Add Internal Portal</h3>");

		//add a (new) external portal instance
		pw.append("<form action='/EditMapInstance.cgi/addExternalPortal' method='get' role='form'>\n");
		
		pw.append("<table>");
		
		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Name");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"name\" type=\"text\" value=\"front_door\">");
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("</table>");

		GeneralMatrixString portalconcepts = new GeneralMatrixString(1);
		EditUtils.getConcepts("portal", portalconcepts);
		
		pw.append("<h3>Portal Type</h3>");
		pw.append("<select name=\"type\">");
		for(int i=0;i<portalconcepts.height;i++)
		{
			pw.append("<option value=\""+portalconcepts.value[i]+"\">"+portalconcepts.value[i]+"</option>");
		}
		pw.append("</select>");

//		ParametricShape shape = o.physicalRepresentation.shape;
//
//		if(shape!=null)
//		{
//			String[] shapesides = shape.getSideNames();
//			
//			pw.append("<h3>Map Side</h3>");
//			pw.append("<select name=\"shapeside\">");
//			for(int i=0;i<shapesides.length;i++)
//			{
//				pw.append("<option value=\""+shapesides[i]+"\">"+shapesides[i]+"</option>");
//			}
//			pw.append("</select>");
//		}

		pw.append("<h4>Room Position and Dimensions</h4>");
		pw.append("<table>");
		
		pw.append("<tr>");
		pw.append("<td>");
		pw.append("X");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"x\" type=\"number\" step=\"0.001\" value=\"0.0\">");		
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Y");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"y\" type=\"number\" step=\"0.001\" value=\"0.36\">");		
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Z");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"z\" type=\"number\" step=\"0.001\" value=\"0.0\">");		
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Plan Width");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"width\" type=\"number\" step=\"0.001\" value=\"0.36\">");		
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Plan Height");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"height\" type=\"number\" step=\"0.001\" value=\"0.823\">");
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Ceiling Height");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"ceiling_height\" type=\"number\" step=\"0.001\" value=\"2.0145\">");
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("</table>");
		
		pw.append("<h4>Portal Shape</h4>");
		pw.append("<select name=\"shape\">");
		for(int i=0;i<ParametricShape.portalShapes.length;i++)
		{
			pw.append("<option value=\""+ParametricShape.portalShapes[i]+"\">"+ParametricShape.portalShapes[i]+"</option>");
		}
		pw.append("</select>");
		
		GeneralMatrixInt roomsOffset = new GeneralMatrixInt(1);
		GeneralMatrixString roomsWithin = new GeneralMatrixString(1);
		EditUtils.getContains(o,"room", roomsWithin,roomsOffset);
		if(roomsWithin.height>0)
		{
			pw.append("<h3>Connect between:</h3>");
			pw.append("<select name=\"connectTo\">");
			for(int i=0;i<roomsWithin.height;i++)
			{
				pw.append("<option value=\""+roomsWithin.value[i]+"\">"+roomsWithin.value[i]+"</option>");
			}
			pw.append("</select>");
			pw.append("<select name=\"connectTo2\">");
			for(int i=0;i<roomsWithin.height;i++)
			{
				pw.append("<option value=\""+roomsWithin.value[i]+"\">"+roomsWithin.value[i]+"</option>");
			}
			pw.append("</select>");
		}
		
		pw.append("<input name=\"instance\" type=\"hidden\" value=\""+o.uniqueID+"\">");
		
		//pw.append("<a class=\"btn btn-primary btn-block\" href='http://www.google.com' onclick='return checkLogin()'>Login</a>");
		pw.append("<button class=\"btn btn-default\" type='submit'>Add</button>");
		pw.append("</form>");
		
		pw.append("<h3>Add External Portal</h3>");
		//add a (new) external portal instance
		pw.append("<form action='/EditMapInstance.cgi/addExternalPortal' method='get' role='form'>\n");
		
		pw.append("<table>");
		
		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Name");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"name\" type=\"text\" value=\"front_door\">");
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("</table>");

		//GeneralMatrixString portalconcepts = new GeneralMatrixString(1);
		EditUtils.getConcepts("portal", portalconcepts);
		
		pw.append("<h3>Portal Type</h3>");
		pw.append("<select name=\"type\">");
		for(int i=0;i<portalconcepts.height;i++)
		{
			pw.append("<option value=\""+portalconcepts.value[i]+"\">"+portalconcepts.value[i]+"</option>");
		}
		pw.append("</select>");

//		ParametricShape shape = o.physicalRepresentation.shape;
//
//		if(shape!=null)
//		{
//			String[] shapesides = shape.getSideNames();
//			
//			pw.append("<h3>Map Side</h3>");
//			pw.append("<select name=\"shapeside\">");
//			for(int i=0;i<shapesides.length;i++)
//			{
//				pw.append("<option value=\""+shapesides[i]+"\">"+shapesides[i]+"</option>");
//			}
//			pw.append("</select>");
//		}

		pw.append("<h4>Room Position and Dimensions</h4>");
		pw.append("<table>");
		
		pw.append("<tr>");
		pw.append("<td>");
		pw.append("X");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"x\" type=\"number\" step=\"0.001\" value=\"0.0\">");		
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Y");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"y\" type=\"number\" step=\"0.001\" value=\"0.36\">");		
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Z");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"z\" type=\"number\" step=\"0.001\" value=\"0.0\">");		
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Plan Width");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"width\" type=\"number\" step=\"0.001\" value=\"0.36\">");		
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Plan Height");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"height\" type=\"number\" step=\"0.001\" value=\"0.823\">");
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Ceiling Height");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"ceiling_height\" type=\"number\" step=\"0.001\" value=\"2.0145\">");
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("</table>");
		
		pw.append("<h4>Portal Shape</h4>");
		pw.append("<select name=\"shape\">");
		for(int i=0;i<ParametricShape.portalShapes.length;i++)
		{
			pw.append("<option value=\""+ParametricShape.portalShapes[i]+"\">"+ParametricShape.portalShapes[i]+"</option>");
		}
		pw.append("</select>");
		
		//GeneralMatrixString roomsWithin = new GeneralMatrixString(1);
//		GeneralMatrixInt roomsOffset = new GeneralMatrixInt(1);
//		GeneralMatrixString roomsWithin = new GeneralMatrixString(1);
//		getContains(o,"room", roomsWithin,roomsOffset);
		if(roomsWithin.height>0)
		{
			pw.append("<h3>Connect to internal room:</h3>");
			pw.append("<select name=\"connectTo\">");
			for(int i=0;i<roomsWithin.height;i++)
			{
				pw.append("<option value=\""+roomsWithin.value[i]+"\">"+roomsWithin.value[i]+"</option>");
			}
			pw.append("</select>");
		}
		
		pw.append("<input name=\"instance\" type=\"hidden\" value=\""+o.uniqueID+"\">");
		
		//pw.append("<a class=\"btn btn-primary btn-block\" href='http://www.google.com' onclick='return checkLogin()'>Login</a>");
		pw.append("<button class=\"btn btn-default\" type='submit'>Add</button>");
		pw.append("</form>");
	}
	

	public static void printType(ObjectLanguageInstance o,String type,String withintype,PrintWriter pw,FileStoreInterface fs)
	{
		if(o.contains!=null)
		{
			double[] ctrans = o.physicalRepresentation.contains_metric_transform;
			GeneralMatrixInt roomsOffset = new GeneralMatrixInt(1);
			GeneralMatrixString roomsWithin = new GeneralMatrixString(1);
//			GeneralMatrixString roomsWithin = new GeneralMatrixString(1);
//			getContains(o,"room", roomsWithin,roomsOffset);
			EditUtils.getContains(o,type, roomsWithin,roomsOffset);
			for(int i=0;i<roomsWithin.height;i++)
			{
				String rpath = ObjectLanguageInstance.uniqueIdToPath(roomsWithin.value[i]);
				ObjectLanguageInstance r = ObjectLanguageInstance.loadObject(fs, rpath+"/data.txt");

				pw.append("<h3><a href=\"/Edit"+type+"Instance.html/"+roomsWithin.value[i]+"\">"+roomsWithin.value[i]+"</a><br></h3>");

				pw.append("<form action='/Edit"+withintype+"Instance.cgi/removeContained' method='get' role='form'>\n");
				pw.append("<input name=\"instance\" type=\"hidden\" value=\""+o.uniqueID+"\">");
				pw.append("<input name=\"cinstance\" type=\"hidden\" value=\""+r.uniqueID+"\">");
				pw.append("<button class=\"btn btn-default\" type='submit'>Remove "+type+"</button>");

				pw.append("</form>");

				//position the room relative to the map
				pw.append("<form action='/Edit"+withintype+"Instance.cgi/setContainedPosition' method='get' role='form'>\n");
				pw.append("<input name=\"instance\" type=\"hidden\" value=\""+o.uniqueID+"\">");
				pw.append("<input name=\"cinstance\" type=\"hidden\" value=\""+r.uniqueID+"\">");
				
				int cind = roomsOffset.value[i];
				double x = ctrans[cind*12+ 9];
				double y = ctrans[cind*12+10];
				double z = ctrans[cind*12+11];
				
				pw.append("<table>");
					pw.append("<tr>");
						pw.append("<td>");
							pw.append("X");
						pw.append("</td>");
						pw.append("<td>");
							pw.append("<input name=\"x\" type=\"number\" step=\"0.001\" value=\""+String.format( "%.3f", x)+"\">");
						pw.append("</td>");
					pw.append("</tr>");
					pw.append("<tr>");
					pw.append("<td>");
						pw.append("Y");
					pw.append("</td>");
					pw.append("<td>");
					pw.append("<input name=\"y\" type=\"number\" step=\"0.001\" value=\""+String.format( "%.3f", y)+"\">");
					pw.append("</td>");
				pw.append("</tr>");
				pw.append("<tr>");
				pw.append("<td>");
					pw.append("Z");
				pw.append("</td>");
				pw.append("<td>");
				pw.append("<input name=\"z\" type=\"number\" step=\"0.001\" value=\""+String.format( "%.3f", z)+"\">");
				pw.append("</td>");
			pw.append("</tr>");

			
			
				pw.append("</table>");
				pw.append("<button class=\"btn btn-default\" type='submit'>Position</button>");

				pw.append("</form>");

				
				pw.append("<h4>Set Shape:</h4>");
				pw.append("<form action='/Edit"+type+"Instance.cgi/setShape' method='get' role='form'>\n");
				
				pw.append("<select name=\"shape\">");
				
				String[] shapes = ParametricShape.getShapesByType(type);
				for(int si=0;si<shapes.length;si++)
				{
					if((r.physicalRepresentation.shape!=null)&&(r.physicalRepresentation.shape.getClass().getName().equalsIgnoreCase(shapes[si])))
						pw.append("<option value=\""+shapes[si]+"\" selected>"+shapes[si]+"</option>");
					else
						pw.append("<option value=\""+shapes[si]+"\">"+shapes[si]+"</option>");
				}
				
				pw.append("</select>");

				pw.append("<input name=\"instance\" type=\"hidden\" value=\""+r.uniqueID+"\">");
				
				//pw.append("<a class=\"btn btn-primary btn-block\" href='http://www.google.com' onclick='return checkLogin()'>Login</a>");
				pw.append("<button class=\"btn btn-default\" type='submit'>Set</button>");
				pw.append("</form>");
				

				pw.append("<h4>Set Shape Dimensions:</h4>");

				pw.append("<form action='/Edit"+type+"Instance.cgi/setShapeParameters' method='get' role='form'>\n");
				pw.append("<input name=\"instance\" type=\"hidden\" value=\""+r.uniqueID+"\">");
				
				ParametricShape rshape = r.physicalRepresentation.shape;

				String[] pnames = rshape.getParameterNames();
				
				pw.append("<table>");
				for(int pi=0;pi<pnames.length;pi++)
				{
					pw.append("<tr>");
					pw.append("<td>");
					pw.append(pnames[pi]);
					pw.append("</td>");
					pw.append("<td>");
					pw.append("<input name=\""+pnames[pi]+"\" type=\"number\" step=\"0.001\" value=\""+String.format( "%.3f", rshape.parameters[pi])+"\">");
					
					pw.append("</td>");
					pw.append("</tr>");
				}
				pw.append("</table>");
				pw.append("<button class=\"btn btn-default\" type='submit'>Set</button>");
				pw.append("</form>");
				//remove a room
			}
		}
	}
	
	public void printRooms(ObjectLanguageInstance o,PrintWriter pw)
	{
		pw.append("<h2>Rooms</h2>");
		
		printType(o, "Room", "Map", pw, fs);
		
		pw.append("<h3>Add New Room</h3>");

		//add a (new) room instance to the map
		pw.append("<form action='/EditMapInstance.cgi/addRoom' method='get' role='form'>\n");
		
		pw.append("<table>");
		
		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Name");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"name\" type=\"text\" value=\"bedroom\">");
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("</table>");

		GeneralMatrixString roomconcepts = new GeneralMatrixString(1);
		EditUtils.getConcepts("room", roomconcepts);
		
		pw.append("<h4>Room Type</h4>");
		pw.append("<select name=\"type\">");
		for(int i=0;i<roomconcepts.height;i++)
		{
			pw.append("<option value=\""+roomconcepts.value[i]+"\">"+roomconcepts.value[i]+"</option>");
		}
		pw.append("</select>");

		pw.append("<h4>Room Position and Dimensions</h4>");
		pw.append("<table>");
		
		pw.append("<tr>");
		pw.append("<td>");
		pw.append("X");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"x\" type=\"number\" step=\"0.001\" value=\"0.36\">");		
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Y");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"y\" type=\"number\" step=\"0.001\" value=\"0.36\">");		
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Z");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"z\" type=\"number\" step=\"0.001\" value=\"0.0\">");		
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Plan Width");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"width\" type=\"number\" step=\"0.001\" value=\"3.0\">");		
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Plan Height");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"height\" type=\"number\" step=\"0.001\" value=\"3.0\">");
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Ceiling Height");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"ceiling_height\" type=\"number\" step=\"0.001\" value=\"2.4\">");
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("</table>");
		
		pw.append("<h4>Room Shape</h4>");
		pw.append("<select name=\"shape\">");
		for(int i=0;i<ParametricShape.roomShapes.length;i++)
		{
			pw.append("<option value=\""+ParametricShape.roomShapes[i]+"\">"+ParametricShape.roomShapes[i]+"</option>");
		}
		pw.append("</select>");

		pw.append("<input name=\"instance\" type=\"hidden\" value=\""+o.uniqueID+"\">");
		
		//pw.append("<a class=\"btn btn-primary btn-block\" href='http://www.google.com' onclick='return checkLogin()'>Login</a>");
		pw.append("<button class=\"btn btn-default\" type='submit'>Add</button>");
		pw.append("</form>");
	}
	
	public void out(WebRequest r,Object metadata,OutputStream s)
	{
		PrintWriter pw = new PrintWriter( s );
		int ind = r.path.indexOf("/");
		String concept = r.path.substring(ind+1);
		ind = concept.indexOf("/");
		String instance = concept.substring(ind+1);
		concept = concept.substring(0,ind);
		//identify the path to the instance of this map
		
		/*
		<script>
		function myMap() {
		    var mapOptions = {
		        center: new google.maps.LatLng(51.5, -0.12),
		        zoom: 10,
		        mapTypeId: google.maps.MapTypeId.HYBRID
		    }
		var map = new google.maps.Map(document.getElementById("map"), mapOptions);
		}
		</script>
		
		<script src="https://maps.googleapis.com/maps/api/js?callback=myMap"></script>
		*/

		String instancepath = "Concepts/"+concept+"/instances/"+instance;
		
		String datapath = instancepath+"/data.txt";
		
		ObjectLanguageInstance o = ObjectLanguageInstance.loadObject(fs, datapath);
		if(o==null)
		{
			o = new ObjectLanguageInstance();
			o.uniqueID = concept+"/"+instance;
			o.name = instance;
			o.type = "map";
			
			o.physicalRepresentation = new ObjectPhysicalInstance();
			
			ObjectLanguageInstance.saveObject(o, fs, datapath);
		}

		SpatialValidation.validateMap(o, fs);
		
		printMap(concept,instance,instancepath,pw);

		printShape(o, pw);

		printExternalPortals(o, pw);
		
		printRooms(o, pw);
		
		pw.flush();
	}
} public class EditObjectInstance extends DynamicWebPage
{
	public EditObjectInstance(FileStoreInterface fs)
	{
		super(fs);
	}
	public boolean process(WebRequest toProcess)
	{
        if(toProcess.path.startsWith("EditObjectInstance.html"))
        {
        	return true;
        }
        else
            if(
            		(toProcess.path.startsWith("EditObjectInstance.cgi"))||
            		(toProcess.path.startsWith("EditobjectInstance.cgi"))
            		)
        {
    		String id = toProcess.parms.get("instance");
    		int ind = id.indexOf("/");
    		
    		String concept = id.substring(0,ind);
    		String instance = id.substring(ind+1);

    		String instancepath = "Concepts/"+concept+"/instances/"+instance;    		
    		String datapath = instancepath+"/data.txt";

    		ObjectLanguageInstance o = ObjectLanguageInstance.loadObject(fs, datapath);
    		if(o==null)
    		{
    			o = new ObjectLanguageInstance();
    			o.uniqueID = concept+"/"+instance;
    			o.name = instance;
    			o.type = "map";
    			
    			o.physicalRepresentation = new ObjectPhysicalInstance();
    		}

    		ind = toProcess.path.indexOf("/");
    		String function = toProcess.path.substring(ind+1);
//    		if(function.equalsIgnoreCase("setShape"))
//    		{
//    			String shape = toProcess.parms.get("shape");
//    			ParametricShape oldshape = o.physicalRepresentation.shape;
//    			o.physicalRepresentation.shape = ParametricShape.get(shape);
//    			if(oldshape!=null)
//    			{
//    				o.physicalRepresentation.shape.makeSimilarSize(oldshape);
//    			}
//    		}
//    		else
//    		if(function.equalsIgnoreCase("setShapeParameters"))
//    		{
//    			ParametricShape shape = o.physicalRepresentation.shape;
//    			String[] pnames = shape.getParameterNames();
//    			
//    			for(int i=0;i<pnames.length;i++)
//    			{
//    				String v = toProcess.parms.get(pnames[i]);
//    				shape.parameters[i] = Double.parseDouble(v);
//    			}
//    			shape.parametersUpdated();
//
//    		}
//    		else
//    		if(function.equalsIgnoreCase("addObject"))
//    		{
//    			//Create a default instance of a room (using a model)
//    			ObjectLanguageInstance r = new ObjectLanguageInstance();
////    			name
////    			type
//    			r.name = toProcess.parms.get("name");
//    			r.name = r.name.replace(' ', '_');
//    			r.type = "object";
//    			String roomConcept = toProcess.parms.get("type");
//    			String roomInstance = instance+"_"+r.name;
//    			r.uniqueID = roomConcept+"/"+roomInstance;
//    			
//    			//only add the instance if it doesn't already exist
//    			int existsind = ArrayUtils.getIndex(o.contains, r.uniqueID);
//    			if(existsind==-1)
//    			{	
//    			
//	    			String rpath = "Concepts/"+roomConcept+"/instances/";
//	    			fs.createDirectory(rpath);
//	    			rpath += roomInstance;
//	    			fs.createDirectory(rpath);
//	    			
//	    			
//	    			
//	    			r.physicalRepresentation = new ObjectPhysicalInstance();
//	    			ParametricShape shape = ParametricShape.get(toProcess.parms.get("shape"));
//	    			r.physicalRepresentation.shape = shape;
//	
//	    			shape.setCuboidDimensions(Double.parseDouble(toProcess.parms.get("width")), 
//	    					Double.parseDouble(toProcess.parms.get("height")),
//	    					Double.parseDouble(toProcess.parms.get("ceiling_height")));
//	
//	//    			x
//	//    			y
//	//    			z
//	//    			width
//	//    			height
//	//    			ceiling_height
//	//    			shape
//	    			
//	    			o.add(r);
//	    			o.physicalRepresentation.add(r.physicalRepresentation, 
//	    					Double.parseDouble(toProcess.parms.get("x")), 
//	    					Double.parseDouble(toProcess.parms.get("y")),
//	    					Double.parseDouble(toProcess.parms.get("z")));
//	    			rpath += "/data.txt";
//	    			ObjectLanguageInstance.saveObject(r, fs, rpath);
//    			}
//    		}
//    		else
//    		if(function.equalsIgnoreCase("setContainedPosition"))
//    		{
//        		String rid = toProcess.parms.get("cinstance");
//        		int rind = rid.indexOf("/");
//        		
//        		String rconcept = rid.substring(0,rind);
//        		String rinstance = rid.substring(rind+1);
//
//        		String rinstancepath = "Concepts/"+rconcept+"/instances/"+rinstance;    		
//        		String rdatapath = rinstancepath+"/data.txt";
//
//        		ObjectLanguageInstance r = ObjectLanguageInstance.loadObject(fs, rdatapath);
//        		
//        		double x = Double.parseDouble(toProcess.parms.get("x"));
//        		double y = Double.parseDouble(toProcess.parms.get("y"));
//        		double z = Double.parseDouble(toProcess.parms.get("z"));
//        		
//        		int cind = ArrayUtils.getIndex(o.contains,rid);
//        		o.physicalRepresentation.contains_metric_transform[12*cind+ 9] = x;
//        		o.physicalRepresentation.contains_metric_transform[12*cind+10] = y;
//        		o.physicalRepresentation.contains_metric_transform[12*cind+11] = z;
//
//        		int wind = ArrayUtils.getIndex(r.within,o.uniqueID);
//        		r.physicalRepresentation.within_metric_transform[12*wind+ 9] = x;
//        		r.physicalRepresentation.within_metric_transform[12*wind+10] = y;
//        		r.physicalRepresentation.within_metric_transform[12*wind+11] = z;
//        		
//    			ObjectLanguageInstance.saveObject(r, fs, rdatapath);
//    		}
    		EditUtils.processFunction(toProcess, function, o, instance, fs);

			toProcess.r = new WebResponse(WebResponse.HTTP_OK, WebResponse.MIME_HTML,"Success");

    		ObjectLanguageInstance.saveObject(o, fs, datapath);

    		return true;
        }
       
        return false;
	}
	
	public void printObject(ObjectLanguageInstance o,PrintWriter pw)
	{
		//Set the parametric shape of this map
		if(o.physicalRepresentation.shape==null)
		{

		}
		else
		{
			
			double ceiling_height = 2.4;
						
			StateToSVG.objectToSVG(pw,fs,o,ceiling_height);
			//create or edit the shape of the map			

			//Adjust the shape parameters as appropriate			
		}
		
		EditUtils.printShape(o, pw, fs);
	}
	public void out(WebRequest r,Object metadata,OutputStream s)
	{
		PrintWriter pw = new PrintWriter( s );
		int ind = r.path.indexOf("/");
		String concept = r.path.substring(ind+1);
		ind = concept.indexOf("/");
		String instance = concept.substring(ind+1);
		concept = concept.substring(0,ind);

		String instancepath = "Concepts/"+concept+"/instances/"+instance;
		
		String datapath = instancepath+"/data.txt";
		
		ObjectLanguageInstance o = ObjectLanguageInstance.loadObject(fs, datapath);
		if(o==null)
		{
			o = new ObjectLanguageInstance();
			o.uniqueID = concept+"/"+instance;
			o.name = instance;
			o.type = "map";
			
			o.physicalRepresentation = new ObjectPhysicalInstance();
			
			ObjectLanguageInstance.saveObject(o, fs, datapath);
		}
		
//		if(o.physicalRepresentation.shape instanceof ModifiedCopyShape)
//		{
//			ModifiedCopyShape mcs = (ModifiedCopyShape)o.physicalRepresentation.shape;
//			mcs.buildParametersAndSides();
//			ObjectLanguageInstance.saveObject(o, fs, datapath);
//		}

		pw.append("<h1>"+concept+"/"+instance+"</h1>");

		printObject(o,pw);
		
		EditUtils.printObjects(o, pw, fs);
		
		pw.flush();
	}
	
	
} public class EditPortalInstance extends DynamicWebPage
{
	public EditPortalInstance(FileStoreInterface fs)
	{
		super(fs);
	}
	public boolean process(WebRequest toProcess)
	{
        if(toProcess.path.startsWith("EditPortalInstance.html"))
        {
        	return true;
        }
        else
            if(
            		(toProcess.path.startsWith("EditPortalInstance.cgi"))||
            		(toProcess.path.startsWith("EditportalInstance.cgi"))
            		)
        {
    		String id = toProcess.parms.get("instance");
    		int ind = id.indexOf("/");
    		
    		String concept = id.substring(0,ind);
    		String instance = id.substring(ind+1);

    		String instancepath = "Concepts/"+concept+"/instances/"+instance;    		
    		String datapath = instancepath+"/data.txt";

    		ObjectLanguageInstance o = ObjectLanguageInstance.loadObject(fs, datapath);
    		if(o==null)
    		{
    			o = new ObjectLanguageInstance();
    			o.uniqueID = concept+"/"+instance;
    			o.name = instance;
    			o.type = "map";
    			
    			o.physicalRepresentation = new ObjectPhysicalInstance();
    		}

    		ind = toProcess.path.indexOf("/");
    		String function = toProcess.path.substring(ind+1);
//    		if(function.equalsIgnoreCase("setShape"))
//    		{
//    			String shape = toProcess.parms.get("shape");
//    			ParametricShape oldshape = o.physicalRepresentation.shape;
//    			o.physicalRepresentation.shape = ParametricShape.get(shape);
//    			if(oldshape!=null)
//    			{
//    				o.physicalRepresentation.shape.makeSimilarSize(oldshape);
//    			}
//    		}
//    		else
//    		if(function.equalsIgnoreCase("setShapeParameters"))
//    		{
//    			ParametricShape shape = o.physicalRepresentation.shape;
//    			String[] pnames = shape.getParameterNames();
//    			
//    			for(int i=0;i<pnames.length;i++)
//    			{
//    				String v = toProcess.parms.get(pnames[i]);
//    				shape.parameters[i] = Double.parseDouble(v);
//    			}
//    		}
//    		else
//    		if(function.equalsIgnoreCase("addObject"))
//    		{
//    			//Create a default instance of a room (using a model)
//    			ObjectLanguageInstance r = new ObjectLanguageInstance();
////    			name
////    			type
//    			r.name = toProcess.parms.get("name");
//    			r.name = r.name.replace(' ', '_');
//    			r.type = "object";
//    			String roomConcept = toProcess.parms.get("type");
//    			String roomInstance = instance+"_"+r.name;
//    			r.uniqueID = roomConcept+"/"+roomInstance;
//    			
//    			//only add the instance if it doesn't already exist
//    			int existsind = ArrayUtils.getIndex(o.contains, r.uniqueID);
//    			if(existsind==-1)
//    			{	
//    			
//	    			String rpath = "Concepts/"+roomConcept+"/instances/";
//	    			fs.createDirectory(rpath);
//	    			rpath += roomInstance;
//	    			fs.createDirectory(rpath);
//	    			
//	    			
//	    			
//	    			r.physicalRepresentation = new ObjectPhysicalInstance();
//	    			ParametricShape shape = ParametricShape.get(toProcess.parms.get("shape"));
//	    			r.physicalRepresentation.shape = shape;
//	
//	    			shape.setCuboidDimensions(Double.parseDouble(toProcess.parms.get("width")), 
//	    					Double.parseDouble(toProcess.parms.get("height")),
//	    					Double.parseDouble(toProcess.parms.get("ceiling_height")));
//	
//	//    			x
//	//    			y
//	//    			z
//	//    			width
//	//    			height
//	//    			ceiling_height
//	//    			shape
//	    			
//	    			o.add(r);
//	    			o.physicalRepresentation.add(r.physicalRepresentation, 
//	    					Double.parseDouble(toProcess.parms.get("x")), 
//	    					Double.parseDouble(toProcess.parms.get("y")),
//	    					Double.parseDouble(toProcess.parms.get("z")),
//	    					Double.parseDouble(toProcess.parms.get("zr"))
//	    					);
//	    			rpath += "/data.txt";
//	    			ObjectLanguageInstance.saveObject(r, fs, rpath);
//    			}
//    		}
//    		else
//    		if(function.equalsIgnoreCase("setContainedPosition"))
//    		{
//        		String rid = toProcess.parms.get("cinstance");
//        		int rind = rid.indexOf("/");
//        		
//        		String rconcept = rid.substring(0,rind);
//        		String rinstance = rid.substring(rind+1);
//
//        		String rinstancepath = "Concepts/"+rconcept+"/instances/"+rinstance;    		
//        		String rdatapath = rinstancepath+"/data.txt";
//
//        		ObjectLanguageInstance r = ObjectLanguageInstance.loadObject(fs, rdatapath);
//        		
//        		double x = Double.parseDouble(toProcess.parms.get("X"));
//        		double y = Double.parseDouble(toProcess.parms.get("Y"));
//        		double z = Double.parseDouble(toProcess.parms.get("Z"));
//        		double zr = Double.parseDouble(toProcess.parms.get("Zr"));
//        		
//        		int cind = ArrayUtils.getIndex(o.contains,rid);
//        		
//        		GeneralMatrixDouble zrot = new GeneralMatrixDouble(4,4);
//        		zrot.set3DTransformRotation(0.0, 0.0, zr*Math.PI/180.0);
//
//        		o.physicalRepresentation.contains_metric_transform[12*cind+ 0] = zrot.value[4*0+0];
//        		o.physicalRepresentation.contains_metric_transform[12*cind+ 1] = zrot.value[4*0+1];
//        		o.physicalRepresentation.contains_metric_transform[12*cind+ 2] = zrot.value[4*0+2];
//
//        		o.physicalRepresentation.contains_metric_transform[12*cind+ 3] = zrot.value[4*1+0];
//        		o.physicalRepresentation.contains_metric_transform[12*cind+ 4] = zrot.value[4*1+1];
//        		o.physicalRepresentation.contains_metric_transform[12*cind+ 5] = zrot.value[4*1+2];
//
//        		o.physicalRepresentation.contains_metric_transform[12*cind+ 6] = zrot.value[4*2+0];
//        		o.physicalRepresentation.contains_metric_transform[12*cind+ 7] = zrot.value[4*2+1];
//        		o.physicalRepresentation.contains_metric_transform[12*cind+ 8] = zrot.value[4*2+2];
//
//        		o.physicalRepresentation.contains_metric_transform[12*cind+ 9] = x;
//        		o.physicalRepresentation.contains_metric_transform[12*cind+10] = y;
//        		o.physicalRepresentation.contains_metric_transform[12*cind+11] = z;
//
//        		int wind = ArrayUtils.getIndex(r.within,o.uniqueID);
//        		
//        		System.arraycopy(o.physicalRepresentation.contains_metric_transform, 12*cind, 
//        				r.physicalRepresentation.within_metric_transform, 12*wind, 
//        				12);
////        		r.physicalRepresentation.within_metric_transform[12*wind+ 9] = x;
////        		r.physicalRepresentation.within_metric_transform[12*wind+10] = y;
////        		r.physicalRepresentation.within_metric_transform[12*wind+11] = z;
//        		
//    			ObjectLanguageInstance.saveObject(r, fs, rdatapath);
//    		}
    		EditUtils.processFunction(toProcess, function, o, instance, fs);
    		
			toProcess.r = new WebResponse(WebResponse.HTTP_OK, WebResponse.MIME_HTML,"Success");

    		ObjectLanguageInstance.saveObject(o, fs, datapath);

    		return true;
        }
       
        return false;
	}
	
	public void printPortal(ObjectLanguageInstance o,PrintWriter pw)
	{
		//Set the parametric shape of this map
		if(o.physicalRepresentation.shape==null)
		{

		}
		else
		{
			
			double ceiling_height = 2.4;
			
			StateToSVG.portalToSVG(pw,fs,o,ceiling_height);
			//create or edit the shape of the map			

			//Adjust the shape parameters as appropriate			
		}
		
		EditUtils.printShape(o, pw, fs);
	}
	
	public void out(WebRequest r,Object metadata,OutputStream s)
	{
		PrintWriter pw = new PrintWriter( s );
		int ind = r.path.indexOf("/");
		String concept = r.path.substring(ind+1);
		ind = concept.indexOf("/");
		String instance = concept.substring(ind+1);
		concept = concept.substring(0,ind);

		String instancepath = "Concepts/"+concept+"/instances/"+instance;
		
		String datapath = instancepath+"/data.txt";
		
		ObjectLanguageInstance o = ObjectLanguageInstance.loadObject(fs, datapath);
		if(o==null)
		{
			o = new ObjectLanguageInstance();
			o.uniqueID = concept+"/"+instance;
			o.name = instance;
			o.type = "map";
			
			o.physicalRepresentation = new ObjectPhysicalInstance();
			
			ObjectLanguageInstance.saveObject(o, fs, datapath);
		}
		
		pw.append("<h1>"+concept+"/"+instance+"</h1>");

		printPortal(o,pw);
		
		EditUtils.printObjects(o, pw, fs);
		
		pw.flush();
	}
} public class EditPoseModel extends DynamicWebPage
{
	public EditPoseModel(FileStoreInterface fs)
	{
		super(fs);
	}
	public boolean process(WebRequest toProcess)
	{
        if(toProcess.path.startsWith("EditPoseModel.html"))
        {
        	return true;
        }
        else
            if(
            		(toProcess.path.startsWith("EditPoseModel.scgi"))||
            		(toProcess.path.startsWith("EditposeModel.scgi"))
            		)
        {
        		int ind = toProcess.path.indexOf("/");
        		String function = toProcess.path.substring(ind+1);

        		PoseLanguageModel o = null;
        		if(function.equalsIgnoreCase("createModel"))
        		{
        			String concept = toProcess.parms.get("concept");
        			String instance = toProcess.parms.get("name");
        			o = new PoseLanguageModel();
        			o.uniqueID = concept+"/"+instance;
        			o.name = instance;
        			o.type = "pose";
        			
        			o.physicalRepresentation = new PosePhysicalModel();
        			
            		String instancepath = "Concepts/"+concept+"/models/"+instance;
        			fs.createDirectory("Concepts/"+concept+"/models/");
        			fs.createDirectory("Concepts/"+concept+"/models/"+instance);
            		String datapath = instancepath+"/data.txt";
        			PoseLanguageModel.saveObject(o, fs, datapath);
        		}
        		
        		String result = "<a href='/EditPoseModel.html/"+o.uniqueID+"'>"+o.uniqueID+"</a>";
        		
    			toProcess.r = new WebResponse(WebResponse.HTTP_OK, WebResponse.MIME_HTML,result);
    			return true;
        }
        else
            if(
            		(toProcess.path.startsWith("EditPoseModel.cgi"))||
            		(toProcess.path.startsWith("EditposeModel.cgi"))
            		)
        {
    		String id = toProcess.parms.get("instance");
    		
    		String concept = id;
    		int ind = concept.indexOf("/");
    		String instance = concept.substring(ind+1);
    		concept = concept.substring(0,ind);

    		String instancepath = "Concepts/"+concept+"/models/"+instance;
    		
    		String datapath = instancepath+"/data.txt";
    		
    		PoseLanguageModel o = PoseLanguageModel.loadObject(fs, datapath);

    		ind = toProcess.path.indexOf("/");
    		String function = toProcess.path.substring(ind+1);

    		if(function.equalsIgnoreCase("addCharacter"))
    		{
    			String cconcept = toProcess.parms.get("concept");
    			o.character_concepts = ArrayUtils.add(o.character_concepts, cconcept);
    			PoseLanguageModel.saveObject(o, fs, datapath);
    		}
    		else
    		if(function.equalsIgnoreCase("addObject"))
    		{
    			String cconcept = toProcess.parms.get("concept");
    			o.object_concepts = ArrayUtils.add(o.object_concepts, cconcept);
    		}
    		else
    		if(function.equalsIgnoreCase("addConstraint"))
    		{
    			String type = toProcess.parms.get("type");
    			GeneralMatrixInt constrainees = new GeneralMatrixInt(1);
    			int cind = 0;
    			while(true)
    			{
    				String constrainee = toProcess.parms.get("constrainee"+cind);
    				if(constrainee==null)
    					break;
    				if(!constrainee.equalsIgnoreCase("unused"))
    				{
    					int ival = Integer.parseInt(constrainee);
    					constrainees.push_back(ival);
    				}
    				cind++;
    			}
    			PhysicalConstraint constraint = PhysicalConstraint.get(type);
    			constraint.constrainees = new int[constrainees.height];
    			System.arraycopy(constrainees.value,0, constraint.constrainees, 0, constraint.constrainees.length);

    			o.physicalRepresentation.constraints = PhysicalConstraint.add(o.physicalRepresentation.constraints, constraint);
    		}
    		else
    		if(function.equalsIgnoreCase("removeConstraint"))
    		{
    			String cinds = toProcess.parms.get("constraint");
    			int cind = Integer.parseInt(cinds);
    			o.physicalRepresentation.constraints = PhysicalConstraint.remove(o.physicalRepresentation.constraints, cind, 1);
    		}
    		else
    		if(function.equalsIgnoreCase("moveConstraintUp"))
    		{
    			String cinds = toProcess.parms.get("constraint");
    			int cind = Integer.parseInt(cinds);
    			if(cind>0)
    			{
    				PhysicalConstraint p = o.physicalRepresentation.constraints[cind-1];
    				PhysicalConstraint n = o.physicalRepresentation.constraints[cind];
    				o.physicalRepresentation.constraints[cind-1] = n;
    				o.physicalRepresentation.constraints[cind] = p;
    			}
    		}
    		else
    		if(function.equalsIgnoreCase("moveConstraintFirst"))
    		{
    			String cinds = toProcess.parms.get("constraint");
    			int cind = Integer.parseInt(cinds);
    			if(cind>0)
    			{
    				PhysicalConstraint p = o.physicalRepresentation.constraints[0];
    				PhysicalConstraint n = o.physicalRepresentation.constraints[cind];
    				o.physicalRepresentation.constraints[0] = n;
    				o.physicalRepresentation.constraints[cind] = p;
    			}
    		}
    		else
    		if(function.equalsIgnoreCase("moveConstraintDown"))
    		{
    			String cinds = toProcess.parms.get("constraint");
    			int cind = Integer.parseInt(cinds);
    			if((cind+1)<o.physicalRepresentation.constraints.length)
    			{
    				PhysicalConstraint p = o.physicalRepresentation.constraints[cind];
    				PhysicalConstraint n = o.physicalRepresentation.constraints[cind+1];
    				o.physicalRepresentation.constraints[cind] = n;
    				o.physicalRepresentation.constraints[cind+1] = p;
    			}
    		}
    		else
    		if(function.equalsIgnoreCase("moveConstraintLast"))
    		{
    			String cinds = toProcess.parms.get("constraint");
    			int cind = Integer.parseInt(cinds);
    			if((cind+1)<o.physicalRepresentation.constraints.length)
    			{
    				PhysicalConstraint p = o.physicalRepresentation.constraints[cind];
    				PhysicalConstraint n = o.physicalRepresentation.constraints[o.physicalRepresentation.constraints.length-1];
    				o.physicalRepresentation.constraints[cind] = n;
    				o.physicalRepresentation.constraints[o.physicalRepresentation.constraints.length-1] = p;
    			}
    		}
    		else
    		if(function.equalsIgnoreCase("setConstraintParameters"))
    		{
    			String cinds = toProcess.parms.get("constraint");
    			int cind = Integer.parseInt(cinds);
    			
    			PhysicalConstraint constraint = o.physicalRepresentation.constraints[cind];
    			
    			String[] pnames = constraint.getParameterNames();    			
    			for(int i=0;i<pnames.length;i++)
    			{
    				String v = toProcess.parms.get(pnames[i]);
    				constraint.parameters[i] = Double.parseDouble(v);
    			}
    			String[] bnames = constraint.getBoneNames();    			
    			for(int i=0;i<bnames.length;i++)
    			{
    				String v = toProcess.parms.get(bnames[i]);
    				constraint.bones[i] = Integer.parseInt(v);
    			}
    			String[] snames = constraint.getShapeNames();    			
    			for(int i=0;i<snames.length;i++)
    			{
    				String v = toProcess.parms.get(snames[i]);
    				constraint.shapes[i] = v.split("/");
    			}
    			String[] ssides = constraint.getShapeSideNames();    			
    			for(int i=0;i<ssides.length;i++)
    			{
    				String v = toProcess.parms.get(ssides[i]);
    				constraint.shape_side[i] = v;
    			}
    			
    			constraint.parametersUpdated();
    		}
    		
    		String result = "<a href='/EditPoseModel.html/"+o.uniqueID+"'>"+o.uniqueID+"</a>";
    		
			PoseLanguageModel.saveObject(o, fs, datapath);

			toProcess.r = new WebResponse(WebResponse.HTTP_OK, WebResponse.MIME_HTML,result);
			return true;
        }
		return false;
	} 
	
	public void out(WebRequest r,Object metadata,OutputStream s)
	{
		PrintWriter pw = new PrintWriter( s );
		
		//load up a model
		
		int ind = r.path.indexOf("/");
		
		if(ind==-1)
		{
			//be able to create a new instance of a pose model
			pw.append("<form action='/EditPoseModel.scgi/createModel' method='get' role='form'>\n");
		
			GeneralMatrixString pconcepts = new GeneralMatrixString(1);
			EditUtils.findConcepts("pose", pconcepts);
			pw.append("<select name=\"concept\">");
			for(int ci=0;ci<pconcepts.height;ci++)
			{
				pw.append("<option value=\""+pconcepts.value[ci]+"\">"+pconcepts.value[ci]+"</option>");
			}
			pw.append("</select>");
			
			pw.append("<input name=\"name\" type=\"txt\" value=\"model_name\">");
			
			pw.append("<button class=\"btn btn-default\" type='submit'>Add</button>");

			pw.append("</form>");

			pw.flush();
			return;
		}
		
		String concept = r.path.substring(ind+1);
		ind = concept.indexOf("/");
		String instance = concept.substring(ind+1);
		concept = concept.substring(0,ind);

		String instancepath = "Concepts/"+concept+"/models/"+instance;
		
		String datapath = instancepath+"/data.txt";
		
		PoseLanguageModel o = PoseLanguageModel.loadObject(fs, datapath);
//		
//		ObjectLanguageInstance o = ObjectLanguageInstance.loadObject(fs, datapath);
		if(o==null)
		{
			o = new PoseLanguageModel();
			o.uniqueID = concept+"/"+instance;
			o.name = instance;
			o.type = "pose";
			
			o.physicalRepresentation = new PosePhysicalModel();
			
			PoseLanguageModel.saveObject(o, fs, datapath);
		}

//		if(o.object_concepts[0]==null)
//		{
//			o.object_concepts = new String[]{};
//			PoseLanguageModel.saveObject(o, fs, datapath);			
//		}
		
//		o.physicalRepresentation.constraints = new PhysicalConstraint[0];
//		PoseLanguageModel.saveObject(o, fs, datapath);
		
		//one or more character concepts
		//none or more object concepts
		
		//a model is evaluated with a specific collection of instances
		//which can be selected through get parameters

		if((o.character_concepts.length>0)||(o.object_concepts.length>0))
		{
			pw.append("<form action='/EditPoseModel.html/"+o.uniqueID+"' method='get' role='form'>\n");
	
			for(int i=0;i<o.character_concepts.length;i++)
			{
				GeneralMatrixString instances = new GeneralMatrixString(1);
				EditUtils.findInstances(o.character_concepts[i], instances);
				pw.append("<h4>Character Instance"+i);
				pw.append("<select name=\"cinst"+i+"\">");
				String selected = r.parms.get("cinst"+i);
				for(int ci=0;ci<instances.height;ci++)
				{
					if((selected!=null)&&(instances.value[i].equalsIgnoreCase(selected)))					
						pw.append("<option selected=\"selected\" value=\""+instances.value[ci]+"\">"+instances.value[ci]+"</option>");
					else
						pw.append("<option value=\""+instances.value[ci]+"\">"+instances.value[ci]+"</option>");
				}
				pw.append("</select>");
				pw.append("</h4>");
			}		
	
			for(int i=0;i<o.object_concepts.length;i++)
			{
				GeneralMatrixString instances = new GeneralMatrixString(1);
				EditUtils.findInstances(o.object_concepts[i], instances);
				pw.append("<h4>Object Instance"+i);
				pw.append("<select name=\"oinst"+i+"\">");
				String selected = r.parms.get("oinst"+i);
				for(int ci=0;ci<instances.height;ci++)
				{
					if((selected!=null)&&(instances.value[i].equalsIgnoreCase(selected)))					
						pw.append("<option selected=\"selected\" value=\""+instances.value[ci]+"\">"+instances.value[ci]+"</option>");
					else
						pw.append("<option value=\""+instances.value[ci]+"\">"+instances.value[ci]+"</option>");
				}
				pw.append("</select>");
				pw.append("</h4>");
			}		
	
			pw.append("<button class=\"btn btn-default\" type='submit'>Test</button>");
	
			pw.append("</form>");
		}
		
		//add
		{
			pw.append("<h4>Add Character</h4>");
			pw.append("<form action='/EditPoseModel.cgi/addCharacter' method='get' role='form'>\n");
			pw.append("<input name=\"instance\" type=\"hidden\" value=\""+o.uniqueID+"\">");
			GeneralMatrixString cconcepts = new GeneralMatrixString(1);
			EditUtils.findConcepts("character", cconcepts);
			pw.append("<select name=\"concept\">");
			for(int ci=0;ci<cconcepts.height;ci++)
			{
				pw.append("<option value=\""+cconcepts.value[ci]+"\">"+cconcepts.value[ci]+"</option>");
			}
			pw.append("</select>");

			pw.append("<button class=\"btn btn-default\" type='submit'>Add</button>");	
			pw.append("</form>");
		}
		
		{
			pw.append("<h4>Add Object</h4>");
			pw.append("<form action='/EditPoseModel.cgi/addObject' method='get' role='form'>\n");
			pw.append("<input name=\"instance\" type=\"hidden\" value=\""+o.uniqueID+"\">");
			GeneralMatrixString cconcepts = new GeneralMatrixString(1);
			EditUtils.findConcepts("object", cconcepts);
			pw.append("<select name=\"concept\">");
			for(int ci=0;ci<cconcepts.height;ci++)
			{
				pw.append("<option value=\""+cconcepts.value[ci]+"\">"+cconcepts.value[ci]+"</option>");
			}
			pw.append("</select>");
			pw.append("<button class=\"btn btn-default\" type='submit'>Add</button>");	
			pw.append("</form>");
		}


		{
			pw.append("<h4>Add Constraint</h4>");
			pw.append("<form action='/EditPoseModel.cgi/addConstraint' method='get' role='form'>\n");
			pw.append("<input name=\"instance\" type=\"hidden\" value=\""+o.uniqueID+"\">");
			
			pw.append("<select name=\"type\">");
			for(int ci=0;ci<PhysicalConstraint.constraintnames.length;ci++)
			{
				pw.append("<option value=\""+PhysicalConstraint.constraintnames[ci]+"\">"+PhysicalConstraint.constraintnames[ci]+"</option>");
			}
			pw.append("</select>");
			
			//can constrain with self so +1
			for(int i=0;i<(o.object_concepts.length+o.character_concepts.length+1);i++)
			{
				pw.append("<select name=\"constrainee"+i+"\">");
				pw.append("<option value=\"unused\">unused</option>");
				for(int ci=0;ci<o.object_concepts.length;ci++)
				{
					pw.append("<option value=\""+(-(ci+1))+"\">"+o.object_concepts[ci]+"</option>");
				}
				for(int ci=0;ci<o.character_concepts.length;ci++)
				{
					pw.append("<option value=\""+ci+"\">"+o.character_concepts[ci]+"</option>");
				}
				pw.append("</select>");
			}
			
			
			pw.append("<button class=\"btn btn-default\" type='submit'>Add</button>");	
			pw.append("</form>");
		}

		//given the selected instances, and the constraints 
		//produce a model instance
		GeneralMatrixObject allObjects = new GeneralMatrixObject(1,o.character_concepts.length+o.object_concepts.length);
		GeneralMatrixDouble oTransforms = new GeneralMatrixDouble(6,allObjects.height);
		oTransforms.clear(0.0);
		
		//when rendering the pose, show instance examples of the characters and objects associated with the pose
		GeneralMatrixObject characters = new GeneralMatrixObject(1,o.character_concepts.length);
		for(int i=0;i<characters.height;i++)
		{
			String iuid = r.parms.get("cinst"+i);
			if(iuid==null)
			{
				GeneralMatrixString instances = new GeneralMatrixString(1);
				EditUtils.findInstances(o.character_concepts[i], instances);
				iuid = instances.value[0];
			}
			String path = ObjectLanguageInstance.uniqueIdToPath(iuid);
			ObjectLanguageInstance c = ObjectLanguageInstance.loadObject(fs, path+"/data.txt");
			characters.value[i] = c;
			allObjects.value[i] = c;
		}

		GeneralMatrixObject objects = new GeneralMatrixObject(1,o.object_concepts.length);
		for(int i=0;i<objects.height;i++)
		{
			String iuid = r.parms.get("oinst"+i);
			if(iuid==null)
			{
				GeneralMatrixString instances = new GeneralMatrixString(1);
				EditUtils.findInstances(o.object_concepts[i], instances);
				iuid = instances.value[0];
			}
			String path = ObjectLanguageInstance.uniqueIdToPath(iuid);
			ObjectLanguageInstance c = ObjectLanguageInstance.loadObject(fs, path+"/data.txt");
			objects.value[i] = c;
			allObjects.value[i+characters.height] = c;
		}

		
		//some of the constraints are parameterised so a pose instance is created 
		//by selecting a set of instances and some parameters
		//the value of these parameters can be selected just like the instances
		for(int i=0;i<o.physicalRepresentation.constraints.length;i++)
		{
			PhysicalConstraint constraint = o.physicalRepresentation.constraints[i];

			if(constraint instanceof IKRelativeToShapeSurface)
			{
				IKRelativeToShapeSurface ik = (IKRelativeToShapeSurface)constraint;
				if(ik.parameters.length<ik.getParameterNames().length)
				{
					double[] np = new double[ik.getParameterNames().length];
					System.arraycopy(ik.parameters, 0, np, 0, ik.parameters.length);
					np[ik.parameters.length+0] = 1.0;
					np[ik.parameters.length+1] = 0.0;
					np[ik.parameters.length+2] = 0.0;
					ik.parameters = np;
					PoseLanguageModel.saveObject(o, fs, datapath);
				}
			}
			
			pw.append("<h2>"+constraint.getClass().getName()+"</h2>");

			pw.append("<form action='/EditPoseModel.cgi/removeConstraint' method='get' role='form'>\n");
			pw.append("<input name=\"instance\" type=\"hidden\" value=\""+o.uniqueID+"\">");
			pw.append("<input name=\"constraint\" type=\"hidden\" value=\""+i+"\">");
			pw.append("<button class=\"btn btn-default\" type='submit'>Remove</button>");
			pw.append("</form>");

			if(i!=0)
			{
				pw.append("<form action='/EditPoseModel.cgi/moveConstraintUp' method='get' role='form'>\n");
				pw.append("<input name=\"instance\" type=\"hidden\" value=\""+o.uniqueID+"\">");
				pw.append("<input name=\"constraint\" type=\"hidden\" value=\""+i+"\">");
				pw.append("<button class=\"btn btn-default\" type='submit'>Move Up</button>");
				pw.append("</form>");

				pw.append("<form action='/EditPoseModel.cgi/moveConstraintFirst' method='get' role='form'>\n");
				pw.append("<input name=\"instance\" type=\"hidden\" value=\""+o.uniqueID+"\">");
				pw.append("<input name=\"constraint\" type=\"hidden\" value=\""+i+"\">");
				pw.append("<button class=\"btn btn-default\" type='submit'>Move First</button>");
				pw.append("</form>");
			}
			if(i!=(o.physicalRepresentation.constraints.length-1))
			{
				pw.append("<form action='/EditPoseModel.cgi/moveConstraintDown' method='get' role='form'>\n");
				pw.append("<input name=\"instance\" type=\"hidden\" value=\""+o.uniqueID+"\">");
				pw.append("<input name=\"constraint\" type=\"hidden\" value=\""+i+"\">");
				pw.append("<button class=\"btn btn-default\" type='submit'>Move Down</button>");
				pw.append("</form>");
				pw.append("<form action='/EditPoseModel.cgi/moveConstraintLast' method='get' role='form'>\n");
				pw.append("<input name=\"instance\" type=\"hidden\" value=\""+o.uniqueID+"\">");
				pw.append("<input name=\"constraint\" type=\"hidden\" value=\""+i+"\">");
				pw.append("<button class=\"btn btn-default\" type='submit'>Move Last</button>");
				pw.append("</form>");
			}
			
			pw.append("<h3>Set Parameters</h3>");
			pw.append("<form action='/EditPoseModel.cgi/setConstraintParameters' method='get' role='form'>\n");
			pw.append("<input name=\"instance\" type=\"hidden\" value=\""+o.uniqueID+"\">");
			pw.append("<input name=\"constraint\" type=\"hidden\" value=\""+i+"\">");
			String[] pnames = constraint.getParameterNames();
			pw.append("<table>");
			for(int pi=0;pi<pnames.length;pi++)
			{
				pw.append("<tr>");
				pw.append("<td>");
				pw.append(pnames[pi]);
				pw.append("</td>");
				pw.append("<td>");
				pw.append("<input name=\""+pnames[pi]+"\" type=\"number\" step=\"0.001\" value=\""+String.format( "%.3f", constraint.parameters[pi])+"\">");
				
				pw.append("</td>");
				pw.append("</tr>");
			}
			pw.append("</table>");

			
			String[] bnames = constraint.getBoneNames();
			if(bnames.length!=0)
			{
				pw.append("<h3>Bones</h3>");
				pw.append("<table>");
				for(int pi=0;pi<bnames.length;pi++)
				{
//					if(constraint.bones==null)
//					{
//						constraint.bones = new int[1];
//						PoseLanguageModel.saveObject(o, fs, datapath);
//					}
					pw.append("<tr>");
					pw.append("<td>");
					pw.append(bnames[pi]);
					pw.append("</td>");
					pw.append("<td>");
					
					pw.append("<select name=\""+bnames[pi]+"\">");
					for(int ci=0;ci<HumanBones.names.length;ci++)
					{
						if(ci==constraint.bones[pi])
							pw.append("<option selected=\"selected\" value=\""+ci+"\">"+HumanBones.names[ci]+"</option>");
						else
							pw.append("<option value=\""+ci+"\">"+HumanBones.names[ci]+"</option>");
					}
					pw.append("</select>");

					pw.append("</td>");
					pw.append("</tr>");
				}
				pw.append("</table>");
			}
			
			String[] snames = constraint.getShapeNames();
			if(snames.length!=0)
			{
				GeneralMatrixString possibleShapes = new GeneralMatrixString(1);
				for(int oi=0;oi<allObjects.height;oi++)
				{
					ObjectLanguageInstance oo = (ObjectLanguageInstance)allObjects.value[oi];
					oo.physicalRepresentation.shape.getShapePaths(""+oi+"/"+oo.name, possibleShapes);
					for(int ci=0;ci<oo.contains.length;ci++)
					{
						String cid = oo.contains[ci];
						
						String cpath = ObjectLanguageInstance.uniqueIdToPath(cid);
						ObjectLanguageInstance c = ObjectLanguageInstance.loadObject(fs, cpath+"/data.txt");
						
						ParametricShape cshape = c.physicalRepresentation.shape;
						cshape.getShapePaths(""+oi+"/"+oo.name+"/contains/"+c.name+"/", possibleShapes);
					}
				}

				pw.append("<h3>Shapes</h3>");
				pw.append("<table>");
				for(int pi=0;pi<snames.length;pi++)
				{
					String currentshapepath = "";
					String[] currentshapestrings = constraint.shapes[pi];
					for(int si=0;si<currentshapestrings.length;si++)
					{
						if(si!=0)
							currentshapepath += "/";
						currentshapepath += currentshapestrings[si];
					}
					pw.append("<tr>");
					pw.append("<td>");
					pw.append(snames[pi]);
					pw.append("</td>");
					pw.append("<td>");
					
					pw.append("<select name=\""+snames[pi]+"\">");
					
					for(int si=0;si<possibleShapes.height;si++)
					{
						if(possibleShapes.value[si].equalsIgnoreCase(currentshapepath))
							pw.append("<option selected=\"selected\" value=\""+possibleShapes.value[si]+"\">"+possibleShapes.value[si]+"</option>");
						else
							pw.append("<option value=\""+possibleShapes.value[si]+"\">"+possibleShapes.value[si]+"</option>");
					}
					//
					
					pw.append("</select>");
					
					pw.append("</td>");
					pw.append("</tr>");

				}
				pw.append("</table>");
			}
			
			String[] sinames = constraint.getShapeSideNames();
			if(sinames.length!=0)
			{
				pw.append("<h3>Side</h3>");
				pw.append("<table>");

				for(int pi=0;pi<sinames.length;pi++)
				{
					pw.append("<tr>");
					pw.append("<td>");
					pw.append(sinames[pi]);
					pw.append("</td>");
					pw.append("<td>");
					
					pw.append("<select name=\""+sinames[pi]+"\">");

					for(int si=0;si<ParametricShape.allsidenames.length;si++)
					{
						if(constraint.shape_side[pi].equalsIgnoreCase(ParametricShape.allsidenames[si]))
							pw.append("<option selected=\"selected\" value=\""+ParametricShape.allsidenames[si]+"\">"+ParametricShape.allsidenames[si]+"</option>");
						else
							pw.append("<option value=\""+ParametricShape.allsidenames[si]+"\">"+ParametricShape.allsidenames[si]+"</option>");
					}

					pw.append("</select>");
					
					pw.append("</td>");
					pw.append("</tr>");
				}
				pw.append("</table>");
			}

			pw.append("<button class=\"btn btn-default\" type='submit'>Set</button>");
			pw.append("</form>");

		}
		
		//initial conditions can also be set
		//these will then be used to see if it helps convergence of the constraints
		//if they are effective they can be saved
		
		//
		
		//for each initial state
		
		//first try to solve the constraints in turn
		for(int itr=0;itr<2;itr++)
		{
			for(int i=0;i<o.physicalRepresentation.constraints.length;i++)
			{
				PhysicalConstraint pc = (PhysicalConstraint)o.physicalRepresentation.constraints[i];
				pc.minimiseErrors(fs,characters, objects, oTransforms);
			}
		}
		
		for(int i=0;i<allObjects.height;i++)
		{
			ObjectLanguageInstance oo = (ObjectLanguageInstance)allObjects.value[i];
			if(oo!=null)
				oo.physicalRepresentation.shape.parametersUpdated();
		}
		//in some cases this may be sufficient to solve them

		//this is the initial state
		
		//iterate through the constraints calculating their cost
		
		//potentially attempt to solve the constraints e.g. using levenberg marquart
		
		
		//visualise the result
		
		//
		int[] origins = new int[3*2];
		int inset = 40;
		double scale = 400;

		StateToSVG.objectsToSVG(pw, fs, allObjects, oTransforms, 2.4,
				origins,inset,scale);
		
		StateToSVG.addConstraints(fs, o, characters, objects, oTransforms, scale, origins, pw);
		
		pw.append("</svg>");

		//list the parameters of the posed characters
		
		for(int i=0;i<characters.height;i++)
		{
			ObjectLanguageInstance c = (ObjectLanguageInstance)characters.value[i];
			pw.append("<h2>"+c.name+"</h2>");
			String[] pnames = c.physicalRepresentation.shape.getParameterNames();
			double[] parameters = c.physicalRepresentation.shape.parameters;
			pw.append("<table>");

			for(int pi=0;pi<parameters.length;pi++)
			{
				pw.append("<tr>");
				pw.append("<td>");
				pw.append(pnames[pi]);
				pw.append("</td>");
				pw.append("<td>"+parameters[pi]);
				pw.append("</td>");
				pw.append("</tr>");
			}
			pw.append("</table>");
		}
		pw.flush();
	}
} public class EditRoomInstance extends DynamicWebPage
{
	public EditRoomInstance(FileStoreInterface fs)
	{
		super(fs);
	}

	public boolean process(WebRequest toProcess)
	{
        if(toProcess.path.startsWith("EditRoomInstance.html"))
        {
        	return true;
        }
        else
            if(
            		(toProcess.path.startsWith("EditRoomInstance.cgi"))||
            		(toProcess.path.startsWith("EditroomInstance.cgi"))
            		)
        {
    		String id = toProcess.parms.get("instance");
    		int ind = id.indexOf("/");
    		
    		String concept = id.substring(0,ind);
    		String instance = id.substring(ind+1);

    		String instancepath = "Concepts/"+concept+"/instances/"+instance;    		
    		String datapath = instancepath+"/data.txt";

    		ObjectLanguageInstance o = ObjectLanguageInstance.loadObject(fs, datapath);
    		if(o==null)
    		{
    			o = new ObjectLanguageInstance();
    			o.uniqueID = concept+"/"+instance;
    			o.name = instance;
    			o.type = "map";
    			
    			o.physicalRepresentation = new ObjectPhysicalInstance();
    		}

    		ind = toProcess.path.indexOf("/");
    		String function = toProcess.path.substring(ind+1);
    		
    		EditUtils.processFunction(toProcess, function, o, instance, fs);

//    		if(function.equalsIgnoreCase("setShape"))
//    		{
//    			String shape = toProcess.parms.get("shape");
//    			ParametricShape oldshape = o.physicalRepresentation.shape;
//    			o.physicalRepresentation.shape = ParametricShape.get(shape);
//    			if(oldshape!=null)
//    			{
//    				o.physicalRepresentation.shape.makeSimilarSize(oldshape);
//    			}
//    		}
//    		else
//    		if(function.equalsIgnoreCase("setShapeParameters"))
//    		{
//    			ParametricShape shape = o.physicalRepresentation.shape;
//    			String[] pnames = shape.getParameterNames();
//    			
//    			for(int i=0;i<pnames.length;i++)
//    			{
//    				String v = toProcess.parms.get(pnames[i]);
//    				shape.parameters[i] = Double.parseDouble(v);
//    			}
//    		}
    		
    		ObjectLanguageInstance.saveObject(o, fs, datapath);

			toProcess.r = new WebResponse(WebResponse.HTTP_OK, WebResponse.MIME_HTML,"Success");

    		return true;
        }
        
        return false;
	}
	
	public void printRoom(ObjectLanguageInstance o,PrintWriter pw)
	{
		//Set the parametric shape of this map
		if(o.physicalRepresentation.shape==null)
		{

		}
		else
		{
			
			StateToSVG.roomToSVG(pw,fs,o);
			//create or edit the shape of the map			

			//Adjust the shape parameters as appropriate			
			
			EditUtils.printShape(o, pw, fs);
		}
	}

	public void out(WebRequest r,Object metadata,OutputStream s)
	{
		PrintWriter pw = new PrintWriter( s );
		int ind = r.path.indexOf("/");
		String concept = r.path.substring(ind+1);
		ind = concept.indexOf("/");
		String instance = concept.substring(ind+1);
		concept = concept.substring(0,ind);

		String instancepath = "Concepts/"+concept+"/instances/"+instance;
		
		String datapath = instancepath+"/data.txt";
		
		ObjectLanguageInstance o = ObjectLanguageInstance.loadObject(fs, datapath);
		if(o==null)
		{
			o = new ObjectLanguageInstance();
			o.uniqueID = concept+"/"+instance;
			o.name = instance;
			o.type = "map";
			
			o.physicalRepresentation = new ObjectPhysicalInstance();
			
			ObjectLanguageInstance.saveObject(o, fs, datapath);
		}
		
//		for(int i=0;i<o.contains.length;i++)
//		{
//			String cid = o.contains[i];
//			
//			String cpath = ObjectLanguageInstance.uniqueIdToPath(cid);
//			ObjectLanguageInstance c = ObjectLanguageInstance.loadObject(fs, cpath+"/data.txt");
//			
//			if(c==null)
//			{
//				int cind = i;
//        		o.physicalRepresentation.contains_metric_transform = ArrayUtils.remove(o.physicalRepresentation.contains_metric_transform,cind*12,12);
//        		o.contains = ArrayUtils.remove(o.contains,cind,1);
//        		o.contains_type = ArrayUtils.remove(o.contains_type,cind,1);
//
//        		ObjectLanguageInstance.saveObject(o, fs, datapath);
//				break;
//			}
//		}
		
		pw.append("<h1>"+concept+"/"+instance+"</h1>");

		printRoom(o,pw);
		
		EditUtils.printCharacters(o, pw, fs);
		EditUtils.printObjects(o, pw, fs);
		
		pw.flush();	}
} public class EditStoryInstance extends DynamicWebPage
{
	public EditStoryInstance(FileStoreInterface fs)
	{
		super(fs);
	}
	public boolean process(WebRequest toProcess)
	{
        if(toProcess.path.startsWith("EditStoryInstance.html"))
        {
        	return true;
        }
        else
        if(
        		(toProcess.path.startsWith("EditStoryInstance.scgi"))||
        		(toProcess.path.startsWith("EditStoryInstance.scgi"))
        		)
    {
    		int ind = toProcess.path.indexOf("/");
    		String function = toProcess.path.substring(ind+1);

    		StoryInstance o = null;
    		if(function.equalsIgnoreCase("createInstance"))
    		{
    			String concept = toProcess.parms.get("concept");
    			String instance = toProcess.parms.get("name");
    			o = new StoryInstance();
    			o.uniqueID = concept+"/"+instance;
    			o.name = instance;
    			o.type = "pose";
    			
        		String instancepath = "Concepts/"+concept+"/instances/"+instance;
    			fs.createDirectory("Concepts/"+concept+"/instances/");
    			fs.createDirectory("Concepts/"+concept+"/instances/"+instance);
        		String datapath = instancepath+"/data.txt";
        		StoryInstance.saveObject(o, fs, datapath);
    		}
    		
    		String result = "<a href='/EditStoryInstance.html/"+o.uniqueID+"'>"+o.uniqueID+"</a>";
    		
			toProcess.r = new WebResponse(WebResponse.HTTP_OK, WebResponse.MIME_HTML,result);
			return true;
    }
        else
            if(
            		(toProcess.path.startsWith("EditStoryInstance.cgi"))||
            		(toProcess.path.startsWith("EditStoryInstance.cgi"))
            		)
        {
    		String id = toProcess.parms.get("instance");
    		int ind = id.indexOf("/");
    		
    		String concept = id.substring(0,ind);
    		String instance = id.substring(ind+1);

    		String instancepath = "Concepts/"+concept+"/instances/"+instance;    		
    		String datapath = instancepath+"/data.txt";

    		StoryInstance o = StoryInstance.loadObject(fs, datapath);
    		if(o==null)
    		{
    			o = new StoryInstance();
    			o.uniqueID = concept+"/"+instance;
    			o.name = instance;
    			o.type = "activity";
       		}

    		ind = toProcess.path.indexOf("/");
    		String function = toProcess.path.substring(ind+1);

    		if(function.equalsIgnoreCase("addMap"))
    		{
    			String mapname = toProcess.parms.get("map");
    			o.objects = ArrayUtils.add(o.objects, mapname);
    			if(o.initialRootObject==null)
    				o.initialRootObject = mapname;
    		}
    		else
    		if(function.equalsIgnoreCase("addCharacter"))
    		{
    			String charname = toProcess.parms.get("character");
    			o.objects = ArrayUtils.add(o.objects, charname);
    		}
    		else
    		if(function.equalsIgnoreCase("addPose"))
    		{
    			String posename = toProcess.parms.get("pose");
    			o.poses = ArrayUtils.add(o.poses, posename);
    		}
    		else
    		if(function.equalsIgnoreCase("addChange"))
    		{
    			String time = toProcess.parms.get("time");
    			String change = toProcess.parms.get("change");
    			
    			ChangeState cs = ChangeState.get(change);
    			double t = Double.parseDouble(time);
    			o.changes = ChangeState.add(o.changes, cs);
    			o.timeOfChange = ArrayUtils.add(o.timeOfChange, t);
    		}
    		else
    		if(function.equalsIgnoreCase("setChangeParameters"))
    		{
    			String cinds = toProcess.parms.get("cinstance");
    			int ci = Integer.parseInt(cinds);
    			
    			ChangeState cs = o.changes[ci];
    			
    			String[] pnames = cs.getParameterNames();
    			String[] pcatnames = cs.getCategoryParameterNames();
    			String[] changeenames = cs.getChangeeNames();
    			String[] posenames = cs.getPoseNames();
    			
    			for(int i=0;i<pnames.length;i++)
    			{
    				String pval = toProcess.parms.get(pnames[i]);
    				double v = Double.parseDouble(pval);
    				cs.parameters[i] = v;
    			}
    			for(int i=0;i<pcatnames.length;i++)
    			{
    				String pval = toProcess.parms.get(pcatnames[i]);
    				int v = Integer.parseInt(pval);
    				cs.category_parameters[i] = v;
    			}    			
    			for(int i=0;i<changeenames.length;i++)
    			{
    				String pval = toProcess.parms.get(changeenames[i]);
    				cs.changeePaths[i] = pval;
    			}
    			for(int i=0;i<posenames.length;i++)
    			{
    				String pval = toProcess.parms.get(posenames[i]);
    				cs.posePaths[i] = pval;
    			}
    		}
    		
    		//EditUtils.processFunction(toProcess, function, o, instance, fs);

			toProcess.r = new WebResponse(WebResponse.HTTP_OK, WebResponse.MIME_HTML,"Success");

			StoryInstance.saveObject(o, fs, datapath);

    		return true;
        }
       
        return false;
	}
	

	public void loadContainedObjects(ObjectLanguageInstance o,GeneralMatrixObject objects, GeneralMatrixString objectpaths)
	{
		for(int i=0;i<o.contains.length;i++)
		{
			String cid = o.contains[i];
			String cpath = ObjectLanguageInstance.uniqueIdToPath(cid);
			ObjectLanguageInstance c = ObjectLanguageInstance.loadObject(fs, cpath+"/data.txt");
			objects.push_back(c);
			objectpaths.push_back(cid);
			loadContainedObjects(c, objects, objectpaths);
		}
	}
	
	public void printChange(PrintWriter pw,String oid,ChangeState cs,int cind,
			GeneralMatrixString root,GeneralMatrixString objectpaths,GeneralMatrixObject objects,GeneralMatrixString posepaths,GeneralMatrixObject poses)
	{
		pw.append(cs.getClass().getSimpleName()+"<br>");

		pw.append("<form action='/EditStoryInstance.cgi/setChangeParameters' method='get' role='form'>\n");
		pw.append("<input name=\"instance\" type=\"hidden\" value=\""+oid+"\">");
		pw.append("<input name=\"cinstance\" type=\"hidden\" value=\""+cind+"\">");
		String[] pnames = cs.getParameterNames();
		String[] pcatnames = cs.getCategoryParameterNames();
		String[][] pcatvalues = cs.getCategoryParameterCatgegories();
		String[] changeenames = cs.getChangeeNames();
		String[] posenames = cs.getPoseNames();
		
		pw.append("<table>");
		for(int i=0;i<pnames.length;i++)
		{
			pw.append("<tr>");
			pw.append("<td>");
			pw.append(pnames[i]);
			pw.append("</td>");
			pw.append("<td>");
			pw.append("<input name=\""+pnames[i]+"\" type=\"number\" step=\"0.001\" value=\""+String.format( "%.3f", cs.parameters[i])+"\">");
			
			pw.append("</td>");
			pw.append("</tr>");
		}
		for(int i=0;i<pcatnames.length;i++)
		{
			pw.append("<tr>");
			pw.append("<td>");
			pw.append(pcatnames[i]);
			pw.append("</td>");
			pw.append("<td>");

			pw.append("<select name=\""+pcatnames[i]+"\">");

			String[] cvals = pcatvalues[i];
			for(int si=0;si<cvals.length;si++)
			{
				if(cs.category_parameters[i]==si)
					pw.append("<option value=\""+si+"\" selected>"+cvals[si]+"</option>");
				else
					pw.append("<option value=\""+si+"\">"+cvals[si]+"</option>");
			}
			
			pw.append("</select>");
			
			pw.append("</td>");
			pw.append("</tr>");
		}

		for(int i=0;i<changeenames.length;i++)
		{
			pw.append("<tr>");
			pw.append("<td>");
			pw.append(changeenames[i]);
			pw.append("</td>");
			pw.append("<td>");

			pw.append("<select name=\""+changeenames[i]+"\">");

			for(int si=0;si<objectpaths.height;si++)
			{
				if((cs.changeePaths[i]!=null)&&(objectpaths.value[si].equalsIgnoreCase(cs.changeePaths[i])))
					pw.append("<option value=\""+objectpaths.value[si]+"\" selected>"+objectpaths.value[si]+"</option>");
				else
					pw.append("<option value=\""+objectpaths.value[si]+"\">"+objectpaths.value[si]+"</option>");
			}
			
			pw.append("</select>");
			
			pw.append("</td>");
			pw.append("</tr>");
		}

		for(int i=0;i<posenames.length;i++)
		{
			pw.append("<tr>");
			pw.append("<td>");
			pw.append(posenames[i]);
			pw.append("</td>");
			pw.append("<td>");

			pw.append("<select name=\""+posenames[i]+"\">");

			for(int si=0;si<posepaths.height;si++)
			{
				if((cs.posePaths[i]!=null)&&(posepaths.value[si].equalsIgnoreCase(cs.posePaths[i])))
					pw.append("<option value=\""+posepaths.value[si]+"\" selected>"+posepaths.value[si]+"</option>");
				else
					pw.append("<option value=\""+posepaths.value[si]+"\">"+posepaths.value[si]+"</option>");
			}
			
			pw.append("</select>");
			
			pw.append("</td>");
			pw.append("</tr>");
		}
		pw.append("</table>");
		pw.append("<button class=\"btn btn-default\" type='submit'>Set</button>");
		pw.append("</form>");
	}
	
	public void out(WebRequest r,Object metadata,OutputStream s)
	{
		PrintWriter pw = new PrintWriter( s );
		int ind = r.path.indexOf("/");
		String concept = r.path.substring(ind+1);
		String thisid = ""+concept;
		ind = concept.indexOf("/");
		String instance = concept.substring(ind+1);
		concept = concept.substring(0,ind);

		String instancepath = "Concepts/"+concept+"/instances/"+instance;
		
		String datapath = instancepath+"/data.txt";
		
		StoryInstance o = StoryInstance.loadObject(fs, datapath);
		if(o==null)
		{
			o = new StoryInstance();
			o.uniqueID = concept+"/"+instance;
			o.name = instance;
			o.type = "map";
			
			StoryInstance.saveObject(o, fs, datapath);
		}
		
//		{
//			o.changes = ChangeState.remove(o.changes,o.changes.length-1,1);
//			o.timeOfChange = ArrayUtils.remove(o.timeOfChange, o.timeOfChange.length-1, 1);
//			StoryInstance.saveObject(o, fs, datapath);
//		}
		
		//load up the objects and pose models
		//go through the changes until the target time

		GeneralMatrixObject objects = new GeneralMatrixObject(1);
		GeneralMatrixString objectpaths = new GeneralMatrixString(1);
		GeneralMatrixObject poses = new GeneralMatrixObject(1);
		GeneralMatrixString posepaths = new GeneralMatrixString(1);

		//load up all the objects and poses and put in the lookup table
		for(int i=0;i<o.objects.length;i++)
		{
			String oid = o.objects[i];
			String opath = ObjectLanguageInstance.uniqueIdToPath(oid);
			ObjectLanguageInstance oi = ObjectLanguageInstance.loadObject(fs, opath+"/data.txt");
			objects.push_back(oi);
			objectpaths.push_back(oid);
			
			//remove the within information for the object (so that it can be placed)
			oi.within = new String[]{};
			oi.within_type = new String[]{};
			oi.physicalRepresentation.within_metric_transform = new double[]{};
			
			loadContainedObjects(oi,objects,objectpaths);
		}
		
		for(int i=0;i<o.poses.length;i++)
		{
			String pid = o.poses[i];
			String ppath = PoseLanguageModel.uniqueIdToPath(pid);
			PoseLanguageModel pi = PoseLanguageModel.loadObject(fs, ppath+"/data.txt");
			poses.push_back(pi);
			posepaths.push_back(pid);
		}
		
		pw.append("<h1>"+concept+"/"+instance+"</h1>");

		pw.append("<h2>Maps</h2>");
		for(int i=0;i<o.objects.length;i++)
		{
			String obid = o.objects[i];
			if(obid.startsWith("map"))
				pw.append(o.objects[i]+"<br>");
		}

		pw.append("<h3>Add Map</h3>");
		GeneralMatrixString mapconcepts = new GeneralMatrixString(1);
		EditUtils.getConcepts("map", mapconcepts);

		for(int mi=0;mi<mapconcepts.height;mi++)
		{
			GeneralMatrixString instances = new GeneralMatrixString(1);
			EditUtils.findInstances(mapconcepts.value[mi], instances);

			if(instances.height==0)
				continue;
			
			pw.append("<h4>"+mapconcepts.value[mi]+"</h4>");

			pw.append("<form action='/EditStoryInstance.cgi/addMap' method='get' role='form'>\n");
			pw.append("<input name=\"instance\" type=\"hidden\" value=\""+o.uniqueID+"\">");
			
			pw.append("<select name=\"map\">");


			for(int si=0;si<instances.height;si++)
			{
				if((o.initialRootObject!=null)&&(o.initialRootObject.equalsIgnoreCase(instances.value[si])))
					pw.append("<option value=\""+instances.value[si]+"\" selected>"+instances.value[si]+"</option>");
				else
					pw.append("<option value=\""+instances.value[si]+"\">"+instances.value[si]+"</option>");
			}
			
			pw.append("</select>");
			
			pw.append("<button class=\"btn btn-default\" type='submit'>Add Map</button>");

			pw.append("</form>");

		}

		pw.append("<h2>Characters</h2>");
		for(int i=0;i<o.objects.length;i++)
		{
			String obid = o.objects[i];
			if(obid.startsWith("character"))
				pw.append(o.objects[i]+"<br>");
		}

		pw.append("<h3>Add Character</h3>");
		GeneralMatrixString charconcepts = new GeneralMatrixString(1);
		EditUtils.getConcepts("character", charconcepts);

		for(int mi=0;mi<charconcepts.height;mi++)
		{
			GeneralMatrixString instances = new GeneralMatrixString(1);
			EditUtils.findInstances(charconcepts.value[mi], instances);

			if(instances.height==0)
				continue;
			
			pw.append("<h4>"+charconcepts.value[mi]+"</h4>");

			pw.append("<form action='/EditStoryInstance.cgi/addCharacter' method='get' role='form'>\n");
			pw.append("<input name=\"instance\" type=\"hidden\" value=\""+o.uniqueID+"\">");
			
			pw.append("<select name=\"character\">");


			for(int si=0;si<instances.height;si++)
			{
//				int charind = GeneralMatrixString.find(o.objects,instances.value[si]);
//				if(charind!=-1)
//					pw.append("<option value=\""+instances.value[si]+"\" selected>"+instances.value[si]+"</option>");
//				else
					pw.append("<option value=\""+instances.value[si]+"\">"+instances.value[si]+"</option>");
			}
			
			pw.append("</select>");
			
			pw.append("<button class=\"btn btn-default\" type='submit'>Add Character</button>");

			pw.append("</form>");

		}

		pw.append("<h2>Poses</h2>");
		for(int i=0;i<o.poses.length;i++)
		{
			pw.append(o.poses[i]+"<br>");
		}
		
		pw.append("<h3>Add Pose</h3>");
		GeneralMatrixString poseconcepts = new GeneralMatrixString(1);
		EditUtils.getConcepts("pose", poseconcepts);

		for(int mi=0;mi<poseconcepts.height;mi++)
		{
			GeneralMatrixString models = new GeneralMatrixString(1);
			EditUtils.findModels(poseconcepts.value[mi], models);

			if(models.height==0)
				continue;
			
			pw.append("<h4>"+poseconcepts.value[mi]+"</h4>");

			pw.append("<form action='/EditStoryInstance.cgi/addPose' method='get' role='form'>\n");
			pw.append("<input name=\"instance\" type=\"hidden\" value=\""+o.uniqueID+"\">");
			
			pw.append("<select name=\"pose\">");


			for(int si=0;si<models.height;si++)
			{
//				int charind = GeneralMatrixString.find(o.objects,instances.value[si]);
//				if(charind!=-1)
//					pw.append("<option value=\""+instances.value[si]+"\" selected>"+instances.value[si]+"</option>");
//				else
					pw.append("<option value=\""+models.value[si]+"\">"+models.value[si]+"</option>");
			}
			
			pw.append("</select>");
			
			pw.append("<button class=\"btn btn-default\" type='submit'>Add Pose</button>");

			pw.append("</form>");

		}
		
		pw.append("<h2>Changes</h2>");
		
		GeneralMatrixString root = new GeneralMatrixString(o.initialRootObject);

		double time = 0.0;
		pw.append("<h3>"+time+"</h3>");
		for(int i=0;i<o.changes.length;i++)
		{
			ChangeState cid = o.changes[i];
			double ctime = o.timeOfChange[i];
			
			if(ctime>time)
			{
				time = ctime;
				pw.append("<h3>"+time+"</h3>");
			}
			printChange(pw,o.uniqueID,cid,i,
					root, objectpaths, objects, posepaths, poses);
		}

		//add change
		pw.append("<h3>Add Change</h3>");

		pw.append("<form action='/EditStoryInstance.cgi/addChange' method='get' role='form'>\n");
		pw.append("<input name=\"instance\" type=\"hidden\" value=\""+o.uniqueID+"\">");
		
		pw.append("<table>");
		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Time:");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"time\" type=\"number\" step=\"0.001\" value=\""+String.format( "%.3f", time)+"\">");
		pw.append("</td>");
		pw.append("</tr>");
		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Change:");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<select name=\"change\">");

		String[] changeNames = ChangeState.changeNames;
		for(int si=0;si<changeNames.length;si++)
		{
//			int charind = GeneralMatrixString.find(o.objects,instances.value[si]);
//			if(charind!=-1)
//				pw.append("<option value=\""+instances.value[si]+"\" selected>"+instances.value[si]+"</option>");
//			else
				pw.append("<option value=\""+changeNames[si]+"\">"+changeNames[si]+"</option>");
		}
		
		pw.append("</select>");
		pw.append("</td>");
		pw.append("</tr>");
		pw.append("</table>");
		
		pw.append("<button class=\"btn btn-default\" type='submit'>Add Change</button>");

		pw.append("</form>");		
		if(o.initialRootObject==null)
		{
		}
		else
		//if(o.initialRootObject!=null)
		{
		
		String times = r.parms.get("time");
		time = 0.0;
		if(times!=null)
		{
			time = Double.parseDouble(times);
		}
		for(int i=0;i<o.changes.length;i++)
		{
			double ct = o.timeOfChange[i];
			if(ct>time)
			{
				break;
			}
			
			ChangeState cs = o.changes[i];
//			if(cs instanceof ChangePose)
//			{
//				cs.posePaths = new String[]{null};
//				StoryInstance.saveObject(o, fs, datapath);
//			}
			cs.change(root, objectpaths, objects, posepaths, poses);
		}
		
		pw.append("<form action='/EditStoryInstance.html/"+thisid+"' method='get' role='form'>\n");

		pw.append("<table>");
		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Time:");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"time\" type=\"number\" step=\"0.001\" value=\""+String.format( "%.3f", time)+"\">");
		pw.append("</td>");
		pw.append("</tr>");
		pw.append("</table>");
		
		pw.append("<button class=\"btn btn-default\" type='submit'>At time</button>");

		pw.append("</form>");		

		
		int rootind = objectpaths.find(root.value[0]);

		ObjectLanguageInstance rooto = (ObjectLanguageInstance)objects.value[rootind];
		//printObject(o,pw);
		
		//EditUtils.printObjects(o, pw, fs);
		//Set the parametric shape of this map
		if(rooto.physicalRepresentation.shape==null)
		{

		}
		else
		{
			
			double ceiling_height = 2.4;
						
			StateToSVG.mapToSVG(pw,
					objectpaths,objects,posepaths,poses,rooto);
			//create or edit the shape of the map			

			//Adjust the shape parameters as appropriate			
		}
		
		//EditUtils.printShape(rooto, pw, fs);
		}
		
		pw.flush();
	}
	
	
} public class EditUtils 
{
	public static GeneralMatrixString shapesToCopy = new GeneralMatrixString(1);
	public static GeneralMatrixString shapesToPaths = new GeneralMatrixString(1);

	public static void findConcepts(String type,GeneralMatrixString cs)
	{
		String tofind = type+"_";
		File conceptsdir = new File("Concepts");
		String[] concepts = conceptsdir.list();
		for(int i=0;i<concepts.length;i++)
		{
			String c = concepts[i];
			if(c.startsWith(tofind))
			{
				cs.push_back(c);
			}
		}
	}
	
	public static void findInstances(String concept,GeneralMatrixString instances)
	{
		File conceptdir = new File("Concepts/"+concept+"/instances/");
		String[] concepts = conceptdir.list();
		if(concepts==null)
			return;
		for(int i=0;i<concepts.length;i++)
		{
			String c = concepts[i];
			File instf = new File("Concepts/"+concept+"/instances/"+c);
			if(instf.isDirectory())
				instances.push_back(concept+"/"+c);
		}
	}
	
	public static void findModels(String concept,GeneralMatrixString instances)
	{
		File conceptdir = new File("Concepts/"+concept+"/models/");
		String[] concepts = conceptdir.list();
		if(concepts==null)
			return;
		for(int i=0;i<concepts.length;i++)
		{
			String c = concepts[i];
			File instf = new File("Concepts/"+concept+"/models/"+c);
			if(instf.isDirectory())
				instances.push_back(concept+"/"+c);
		}
	}
	
	public static void findShapesToCopy(FileStoreInterface fs)
	{
		File conceptsdir = new File("Concepts");
		String[] concepts = conceptsdir.list();
		for(int i=0;i<concepts.length;i++)
		{
			String c = concepts[i];
			
			System.out.println(c);
			
			String cInstPath = "Concepts/"+c+"/instances";
			File cInstdir = new File(cInstPath);
			if(!cInstdir.exists())
			{
				continue;
			}
			
			if(c.startsWith("story"))
				continue;
			if(c.startsWith("pose"))
				continue;
			
			String[] insts = cInstdir.list();
			for(int ii=0;ii<insts.length;ii++)
			{
				String instdata = cInstPath+"/"+insts[ii]+"/data.txt";
				File instf = new File(instdata);
				if(instf.exists())
				{
					ObjectLanguageInstance o = ObjectLanguageInstance.loadObject(fs, instdata);
					if(o.physicalRepresentation.shape instanceof ModifiedCopyShape)
						continue;
					
					System.out.println(insts[ii]);
					
					shapesToPaths.push_back(instdata);
					shapesToCopy.push_back(c+"/"+insts[ii]);
				}
			}
		}
	}
	
	public static void getContains(ObjectLanguageInstance o,String type,GeneralMatrixString result,GeneralMatrixInt offset)
	{
		for(int i=0;i<o.contains_type.length;i++)
		{
			if(o.contains_type[i].equalsIgnoreCase(type))
			{
				result.push_back(o.contains[i]);
				offset.push_back(i);
			}
		}
	}
	
	public static void getConcepts(String type,GeneralMatrixString result)
	{
		File conceptsdir = new File("Concepts");
		String[] concepts = conceptsdir.list();
		for(int i=0;i<concepts.length;i++)
		{
			String c = concepts[i];
			if(c.startsWith(".")||c.endsWith(".md"))
			{
				continue;
			}

			if(c.startsWith(type+"_"))
			{
				result.push_back(c);
			}
		}
		
	}

	public static void printType(ObjectLanguageInstance o,String type,String withintype,PrintWriter pw,FileStoreInterface fs)
	{
		if(o.contains!=null)
		{
			double[] ctrans = o.physicalRepresentation.contains_metric_transform;
			GeneralMatrixInt roomsOffset = new GeneralMatrixInt(1);
			GeneralMatrixString roomsWithin = new GeneralMatrixString(1);
//			GeneralMatrixString roomsWithin = new GeneralMatrixString(1);
//			getContains(o,"room", roomsWithin,roomsOffset);
			getContains(o,type, roomsWithin,roomsOffset);
			for(int i=0;i<roomsWithin.height;i++)
			{
				String rpath = ObjectLanguageInstance.uniqueIdToPath(roomsWithin.value[i]);
				ObjectLanguageInstance r = ObjectLanguageInstance.loadObject(fs, rpath+"/data.txt");

				pw.append("<h3><a href=\"/Edit"+type+"Instance.html/"+roomsWithin.value[i]+"\">"+roomsWithin.value[i]+"</a><br></h3>");

				pw.append("<form action='/Edit"+withintype+"Instance.cgi/removeContained' method='get' role='form'>\n");
				pw.append("<input name=\"instance\" type=\"hidden\" value=\""+o.uniqueID+"\">");
				pw.append("<input name=\"cinstance\" type=\"hidden\" value=\""+r.uniqueID+"\">");
				pw.append("<button class=\"btn btn-default\" type='submit'>Remove "+type+"</button>");

				pw.append("</form>");

				//position the room relative to the map
				pw.append("<form action='/Edit"+withintype+"Instance.cgi/setContainedPosition' method='get' role='form'>\n");
				pw.append("<input name=\"instance\" type=\"hidden\" value=\""+o.uniqueID+"\">");
				pw.append("<input name=\"cinstance\" type=\"hidden\" value=\""+r.uniqueID+"\">");
				
				int cind = roomsOffset.value[i];
				double x = ctrans[cind*12+ 9];
				double y = ctrans[cind*12+10];
				double z = ctrans[cind*12+11];
				
				int coff = cind*12;
				GeneralMatrixDouble containedRelativeToParenttran = new GeneralMatrixDouble(4,4);
				containedRelativeToParenttran.setIdentity();
				for(int ti=0;ti<3;ti++)
					containedRelativeToParenttran.value[ti] = ctrans[coff+ti];
				for(int ti=0;ti<3;ti++)
					containedRelativeToParenttran.value[4+ti] = ctrans[coff+3+ti];
				for(int ti=0;ti<3;ti++)
					containedRelativeToParenttran.value[8+ti] = ctrans[coff+6+ti];
				containedRelativeToParenttran.set3DTransformPosition(ctrans[coff+9], ctrans[coff+10], ctrans[coff+11]);

				GeneralMatrixDouble euler = new GeneralMatrixDouble(3,1);
				GeneralMatrixDouble.getEuler(containedRelativeToParenttran, euler);
				double xr = 180.0*(euler.value[0]/Math.PI);
				double yr = 180.0*(euler.value[1]/Math.PI);
				double zr = 180.0*(euler.value[2]/Math.PI);
				
				pw.append("<table>");
					pw.append("<tr>");
						pw.append("<td>");
							pw.append("X");
						pw.append("</td>");
						pw.append("<td>");
							pw.append("<input name=\"x\" type=\"number\" step=\"0.001\" value=\""+String.format( "%.3f", x)+"\">");
						pw.append("</td>");
					pw.append("</tr>");
					pw.append("<tr>");
					pw.append("<td>");
						pw.append("Y");
					pw.append("</td>");
					pw.append("<td>");
					pw.append("<input name=\"y\" type=\"number\" step=\"0.001\" value=\""+String.format( "%.3f", y)+"\">");
					pw.append("</td>");
				pw.append("</tr>");
				pw.append("<tr>");
				pw.append("<td>");
					pw.append("Z");
				pw.append("</td>");
				pw.append("<td>");
				pw.append("<input name=\"z\" type=\"number\" step=\"0.001\" value=\""+String.format( "%.3f", z)+"\">");
				pw.append("</td>");
			pw.append("</tr>");
			
			
			pw.append("<tr>");
			pw.append("<td>");
				pw.append("Xr");
			pw.append("</td>");
			pw.append("<td>");
			pw.append("<input name=\"xr\" type=\"number\" step=\"1.0\" value=\""+String.format( "%.1f", xr)+"\">");
			pw.append("</td>");
		pw.append("</tr>");
		pw.append("<tr>");
		pw.append("<td>");
			pw.append("Yr");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"yr\" type=\"number\" step=\"1.0\" value=\""+String.format( "%.1f", yr)+"\">");
		pw.append("</td>");
	pw.append("</tr>");
	pw.append("<tr>");
	pw.append("<td>");
		pw.append("Zr");
	pw.append("</td>");
	pw.append("<td>");
	pw.append("<input name=\"zr\" type=\"number\" step=\"1.0\" value=\""+String.format( "%.1f", zr)+"\">");
	pw.append("</td>");
pw.append("</tr>");

				pw.append("</table>");
				pw.append("<button class=\"btn btn-default\" type='submit'>Position</button>");

				pw.append("</form>");

				
				pw.append("<h4>Set Shape:</h4>");
				pw.append("<form action='/Edit"+type+"Instance.cgi/setShape' method='get' role='form'>\n");
				
				pw.append("<select name=\"shape\">");
				
				String[] shapes = ParametricShape.getShapesByType(type);
				for(int si=0;si<shapes.length;si++)
				{
					if((r.physicalRepresentation.shape!=null)&&(r.physicalRepresentation.shape.getClass().getName().equalsIgnoreCase(shapes[si])))
						pw.append("<option value=\""+shapes[si]+"\" selected>"+shapes[si]+"</option>");
					else
						pw.append("<option value=\""+shapes[si]+"\">"+shapes[si]+"</option>");
				}
				
				pw.append("</select>");

				pw.append("<input name=\"instance\" type=\"hidden\" value=\""+r.uniqueID+"\">");
				
				//pw.append("<a class=\"btn btn-primary btn-block\" href='http://www.google.com' onclick='return checkLogin()'>Login</a>");
				pw.append("<button class=\"btn btn-default\" type='submit'>Set</button>");
				pw.append("</form>");
				

				pw.append("<h4>Set Shape Dimensions:</h4>");

				pw.append("<form action='/Edit"+type+"Instance.cgi/setShapeParameters' method='get' role='form'>\n");
				pw.append("<input name=\"instance\" type=\"hidden\" value=\""+r.uniqueID+"\">");
				
				ParametricShape rshape = r.physicalRepresentation.shape;

				String[] pnames = rshape.getParameterNames();
				
				if(rshape.parameters==null)
				{
					rshape.parametersUpdated();
					ObjectLanguageInstance.saveObject(r, fs, rpath+"/data.txt");
				}

				pw.append("<table>");
				for(int pi=0;pi<pnames.length;pi++)
				{
					pw.append("<tr>");
					pw.append("<td>");
					pw.append(pnames[pi]);
					pw.append("</td>");
					pw.append("<td>");
					pw.append("<input name=\""+pnames[pi]+"\" type=\"number\" step=\"0.001\" value=\""+String.format( "%.3f", rshape.parameters[pi])+"\">");
					
					pw.append("</td>");
					pw.append("</tr>");
				}
				pw.append("</table>");
				pw.append("<button class=\"btn btn-default\" type='submit'>Set</button>");
				pw.append("</form>");
				
				if(rshape instanceof CSGShape)
				{
					addSubShapeForm(pw, type, r, shapes);
//					pw.append("<h4>Add Sub Shape:</h4>");
//
//					pw.append("<form action='/Edit"+type+"Instance.cgi/addSubShape' method='get' role='form'>\n");
//					pw.append("<input name=\"instance\" type=\"hidden\" value=\""+r.uniqueID+"\">");
//
//					pw.append("<table>");
//					
//					pw.append("<tr>");
//					pw.append("<td>");
//					pw.append("Name");
//					pw.append("</td>");
//					pw.append("<td>");
//					pw.append("<input name=\"name\" type=\"text\" value=\"objectname\">");
//					pw.append("</td>");
//					pw.append("</tr>");
//
//					pw.append("</table>");
//
//					pw.append("<select name=\"shape\">");
//					
//					for(int si=0;si<shapes.length;si++)
//					{
//						if(shapes[si].equalsIgnoreCase("CSGShape"))
//							continue;
//						
//						if((r.physicalRepresentation.shape!=null)&&(r.physicalRepresentation.shape.getClass().getName().equalsIgnoreCase(shapes[si])))
//							pw.append("<option value=\""+shapes[si]+"\" selected>"+shapes[si]+"</option>");
//						else
//							pw.append("<option value=\""+shapes[si]+"\">"+shapes[si]+"</option>");
//					}
//					
//					pw.append("</select>");
//					
//					pw.append("<h5>Sub Shape Position and Dimensions</h5>");
//					pw.append("<table>");
//					
//					pw.append("<tr>");
//					pw.append("<td>");
//					pw.append("X");
//					pw.append("</td>");
//					pw.append("<td>");
//					pw.append("<input name=\"x\" type=\"number\" step=\"0.001\" value=\"0.0\">");		
//					pw.append("</td>");
//					pw.append("</tr>");
//
//					pw.append("<tr>");
//					pw.append("<td>");
//					pw.append("Y");
//					pw.append("</td>");
//					pw.append("<td>");
//					pw.append("<input name=\"y\" type=\"number\" step=\"0.001\" value=\"0.0\">");		
//					pw.append("</td>");
//					pw.append("</tr>");
//
//					pw.append("<tr>");
//					pw.append("<td>");
//					pw.append("Z");
//					pw.append("</td>");
//					pw.append("<td>");
//					pw.append("<input name=\"z\" type=\"number\" step=\"0.001\" value=\"0.0\">");		
//					pw.append("</td>");
//					pw.append("</tr>");
//
//
//					pw.append("<tr>");
//					pw.append("<td>");
//					pw.append("X Rotation");
//					pw.append("</td>");
//					pw.append("<td>");
//					pw.append("<input name=\"xr\" type=\"number\" step=\"1.0\" value=\"0.0\">");		
//					pw.append("</td>");
//					pw.append("</tr>");
//
//					pw.append("<tr>");
//					pw.append("<td>");
//					pw.append("Y Rotation");
//					pw.append("</td>");
//					pw.append("<td>");
//					pw.append("<input name=\"yr\" type=\"number\" step=\"1.0\" value=\"0.0\">");		
//					pw.append("</td>");
//					pw.append("</tr>");
//
//					pw.append("<tr>");
//					pw.append("<td>");
//					pw.append("Z Rotation");
//					pw.append("</td>");
//					pw.append("<td>");
//					pw.append("<input name=\"zr\" type=\"number\" step=\"1.0\" value=\"0.0\">");		
//					pw.append("</td>");
//					pw.append("</tr>");
//
//					pw.append("<tr>");
//					pw.append("<td>");
//					pw.append("Plan Width");
//					pw.append("</td>");
//					pw.append("<td>");
//					pw.append("<input name=\"width\" type=\"number\" step=\"0.001\" value=\"1.0\">");		
//					pw.append("</td>");
//					pw.append("</tr>");
//
//					pw.append("<tr>");
//					pw.append("<td>");
//					pw.append("Plan Height");
//					pw.append("</td>");
//					pw.append("<td>");
//					pw.append("<input name=\"height\" type=\"number\" step=\"0.001\" value=\"1.0\">");
//					pw.append("</td>");
//					pw.append("</tr>");
//
//					pw.append("<tr>");
//					pw.append("<td>");
//					pw.append("Ceiling Height");
//					pw.append("</td>");
//					pw.append("<td>");
//					pw.append("<input name=\"ceiling_height\" type=\"number\" step=\"0.001\" value=\"1.0\">");
//					pw.append("</td>");
//					pw.append("</tr>");
//
//					pw.append("</table>");					
//					pw.append("<button class=\"btn btn-default\" type='submit'>Set</button>");
//					pw.append("</form>");

				}
				//remove a room
			}
		}
	}
	
	public static void addSubShapeForm(PrintWriter pw, String type,ObjectLanguageInstance r,String[] shapes)
	{
		CSGShape csg = (CSGShape)r.physicalRepresentation.shape;
		if(csg.shapes.length>0)
		{
			pw.append("<h4>Remove Sub Shape:</h4>");
			
			pw.append("<form action='/Edit"+type+"Instance.cgi/removeSubShape' method='get' role='form'>\n");
			pw.append("<input name=\"instance\" type=\"hidden\" value=\""+r.uniqueID+"\">");
			
			pw.append("<select name=\"shape\">");
			
			for(int si=0;si<csg.shapes.length;si++)
			{
				pw.append("<option value=\""+si+"\">"+csg.shapeNames[si]+"</option>");
			}
			
			pw.append("</select>");
			
			pw.append("<button class=\"btn btn-default\" type='submit'>Remove</button>");
			pw.append("</form>");
		}
		
		pw.append("<h4>Add Sub Shape:</h4>");

	pw.append("<form action='/Edit"+type+"Instance.cgi/addSubShape' method='get' role='form'>\n");
	pw.append("<input name=\"instance\" type=\"hidden\" value=\""+r.uniqueID+"\">");

	pw.append("<table>");
	
	pw.append("<tr>");
	pw.append("<td>");
	pw.append("Name");
	pw.append("</td>");
	pw.append("<td>");
	pw.append("<input name=\"name\" type=\"text\" value=\"objectname\">");
	pw.append("</td>");
	pw.append("</tr>");

	pw.append("</table>");

	pw.append("<select name=\"shape\">");
	
	for(int si=0;si<shapes.length;si++)
	{
		if(shapes[si].equalsIgnoreCase("CSGShape"))
			continue;
		
		if((r.physicalRepresentation.shape!=null)&&(r.physicalRepresentation.shape.getClass().getName().equalsIgnoreCase(shapes[si])))
			pw.append("<option value=\""+shapes[si]+"\" selected>"+shapes[si]+"</option>");
		else
			pw.append("<option value=\""+shapes[si]+"\">"+shapes[si]+"</option>");
	}
	
	pw.append("</select>");
	
	pw.append("<h5>Sub Shape Position and Dimensions</h5>");
	pw.append("<table>");
	
	pw.append("<tr>");
	pw.append("<td>");
	pw.append("X");
	pw.append("</td>");
	pw.append("<td>");
	pw.append("<input name=\"x\" type=\"number\" step=\"0.001\" value=\"0.0\">");		
	pw.append("</td>");
	pw.append("</tr>");

	pw.append("<tr>");
	pw.append("<td>");
	pw.append("Y");
	pw.append("</td>");
	pw.append("<td>");
	pw.append("<input name=\"y\" type=\"number\" step=\"0.001\" value=\"0.0\">");		
	pw.append("</td>");
	pw.append("</tr>");

	pw.append("<tr>");
	pw.append("<td>");
	pw.append("Z");
	pw.append("</td>");
	pw.append("<td>");
	pw.append("<input name=\"z\" type=\"number\" step=\"0.001\" value=\"0.0\">");		
	pw.append("</td>");
	pw.append("</tr>");


	pw.append("<tr>");
	pw.append("<td>");
	pw.append("X Rotation");
	pw.append("</td>");
	pw.append("<td>");
	pw.append("<input name=\"xr\" type=\"number\" step=\"1.0\" value=\"0.0\">");		
	pw.append("</td>");
	pw.append("</tr>");

	pw.append("<tr>");
	pw.append("<td>");
	pw.append("Y Rotation");
	pw.append("</td>");
	pw.append("<td>");
	pw.append("<input name=\"yr\" type=\"number\" step=\"1.0\" value=\"0.0\">");		
	pw.append("</td>");
	pw.append("</tr>");

	pw.append("<tr>");
	pw.append("<td>");
	pw.append("Z Rotation");
	pw.append("</td>");
	pw.append("<td>");
	pw.append("<input name=\"zr\" type=\"number\" step=\"1.0\" value=\"0.0\">");		
	pw.append("</td>");
	pw.append("</tr>");

	pw.append("<tr>");
	pw.append("<td>");
	pw.append("Plan Width");
	pw.append("</td>");
	pw.append("<td>");
	pw.append("<input name=\"width\" type=\"number\" step=\"0.001\" value=\"1.0\">");		
	pw.append("</td>");
	pw.append("</tr>");

	pw.append("<tr>");
	pw.append("<td>");
	pw.append("Plan Height");
	pw.append("</td>");
	pw.append("<td>");
	pw.append("<input name=\"height\" type=\"number\" step=\"0.001\" value=\"1.0\">");
	pw.append("</td>");
	pw.append("</tr>");

	pw.append("<tr>");
	pw.append("<td>");
	pw.append("Ceiling Height");
	pw.append("</td>");
	pw.append("<td>");
	pw.append("<input name=\"ceiling_height\" type=\"number\" step=\"0.001\" value=\"1.0\">");
	pw.append("</td>");
	pw.append("</tr>");

	pw.append("</table>");					
	pw.append("<button class=\"btn btn-default\" type='submit'>Add Sub Shape</button>");
	pw.append("</form>");
}
	
	public static void printCharacters(ObjectLanguageInstance o,PrintWriter pw,FileStoreInterface fs)
	{
		pw.append("<h2>Characters</h2>");
		
		printType(o, "character", o.type, pw, fs);
		
		pw.append("<h3>Add New Character</h3>");

		//add a (new) room instance to the map
		pw.append("<form action='/Edit"+o.type+"Instance.cgi/addCharacter' method='get' role='form'>\n");
		
		pw.append("<table>");
		
		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Name");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"name\" type=\"text\" value=\"charactername\">");
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("</table>");

		GeneralMatrixString roomconcepts = new GeneralMatrixString(1);
		EditUtils.getConcepts("character", roomconcepts);
		
		pw.append("<h4>Character Type</h4>");
		pw.append("<select name=\"type\">");
		for(int i=0;i<roomconcepts.height;i++)
		{
			pw.append("<option value=\""+roomconcepts.value[i]+"\">"+roomconcepts.value[i]+"</option>");
		}
		pw.append("</select>");

		pw.append("<h4>Character Position</h4>");
		pw.append("<table>");
		
		pw.append("<tr>");
		pw.append("<td>");
		pw.append("X");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"x\" type=\"number\" step=\"0.001\" value=\"0.36\">");		
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Y");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"y\" type=\"number\" step=\"0.001\" value=\"0.36\">");		
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Z");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"z\" type=\"number\" step=\"0.001\" value=\"0.0\">");		
		pw.append("</td>");
		pw.append("</tr>");


		pw.append("<tr>");
		pw.append("<td>");
		pw.append("X Rotation");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"xr\" type=\"number\" step=\"1.0\" value=\"0.0\">");		
		pw.append("</td>");
		pw.append("</tr>");
		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Y Rotation");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"yr\" type=\"number\" step=\"1.0\" value=\"0.0\">");		
		pw.append("</td>");
		pw.append("</tr>");
		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Z Rotation");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"zr\" type=\"number\" step=\"1.0\" value=\"0.0\">");		
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("</table>");
		
		pw.append("<input name=\"instance\" type=\"hidden\" value=\""+o.uniqueID+"\">");
		
		pw.append("<button class=\"btn btn-default\" type='submit'>Add</button>");
		pw.append("</form>");

	}
	public static void printObjects(ObjectLanguageInstance o,PrintWriter pw,FileStoreInterface fs)
	{
		pw.append("<h2>Object</h2>");
		
		printType(o, "Object", o.type, pw, fs);
		
		pw.append("<h3>Add New Object</h3>");

		//add a (new) room instance to the map
		pw.append("<form action='/Edit"+o.type+"Instance.cgi/addObject' method='get' role='form'>\n");
		
		pw.append("<table>");
		
		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Name");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"name\" type=\"text\" value=\"objectname\">");
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("</table>");

		GeneralMatrixString roomconcepts = new GeneralMatrixString(1);
		EditUtils.getConcepts("object", roomconcepts);
		
		pw.append("<h4>Object Type</h4>");
		pw.append("<select name=\"type\">");
		for(int i=0;i<roomconcepts.height;i++)
		{
			pw.append("<option value=\""+roomconcepts.value[i]+"\">"+roomconcepts.value[i]+"</option>");
		}
		pw.append("</select>");

		pw.append("<h4>Object Position and Dimensions</h4>");
		pw.append("<table>");
		
		pw.append("<tr>");
		pw.append("<td>");
		pw.append("X");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"x\" type=\"number\" step=\"0.001\" value=\"0.36\">");		
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Y");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"y\" type=\"number\" step=\"0.001\" value=\"0.36\">");		
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Z");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"z\" type=\"number\" step=\"0.001\" value=\"0.0\">");		
		pw.append("</td>");
		pw.append("</tr>");


		pw.append("<tr>");
		pw.append("<td>");
		pw.append("X Rotation");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"xr\" type=\"number\" step=\"1.0\" value=\"0.0\">");		
		pw.append("</td>");
		pw.append("</tr>");
		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Y Rotation");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"yr\" type=\"number\" step=\"1.0\" value=\"0.0\">");		
		pw.append("</td>");
		pw.append("</tr>");
		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Z Rotation");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"zr\" type=\"number\" step=\"1.0\" value=\"0.0\">");		
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Plan Width");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"width\" type=\"number\" step=\"0.001\" value=\"3.0\">");		
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Plan Height");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"height\" type=\"number\" step=\"0.001\" value=\"3.0\">");
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("<tr>");
		pw.append("<td>");
		pw.append("Ceiling Height");
		pw.append("</td>");
		pw.append("<td>");
		pw.append("<input name=\"ceiling_height\" type=\"number\" step=\"0.001\" value=\"2.4\">");
		pw.append("</td>");
		pw.append("</tr>");

		pw.append("</table>");
		
		pw.append("<h4>Object Shape</h4>");
		pw.append("<select name=\"shape\">");
		for(int i=0;i<ParametricShape.objectShapes.length;i++)
		{
			pw.append("<option value=\""+ParametricShape.objectShapes[i]+"\">"+ParametricShape.objectShapes[i]+"</option>");
		}
		pw.append("</select>");

		pw.append("<select name=\"shapeToCopyFrom\">");
		for(int i=0;i<shapesToCopy.height;i++)
		{
			pw.append("<option value=\""+shapesToPaths.value[i]+"\">"+shapesToCopy.value[i]+"</option>");
		}
		pw.append("</select>");

		pw.append("<input name=\"instance\" type=\"hidden\" value=\""+o.uniqueID+"\">");
		
		//pw.append("<a class=\"btn btn-primary btn-block\" href='http://www.google.com' onclick='return checkLogin()'>Login</a>");
		pw.append("<button class=\"btn btn-default\" type='submit'>Add</button>");
		pw.append("</form>");
	}

	public static void printShape(ObjectLanguageInstance o,PrintWriter pw,FileStoreInterface fs)
	{
//		//Set the parametric shape of this map
//		if(o.physicalRepresentation.shape==null)
//		{
//
//		}
//		else
//		{
//			
//			StateToSVG.mapToSVG(pw,fs,o);
//			//create or edit the shape of the map			
//
//			//Adjust the shape parameters as appropriate			
//		}

		pw.append("<h3>Set Shape:</h3>");
		pw.append("<form action='/Edit"+o.type+"Instance.cgi/setShape' method='get' role='form'>\n");
		
		pw.append("<select name=\"shape\">");
		
		String[] shapes = ParametricShape.getShapesByType(o.type);
		for(int i=0;i<shapes.length;i++)
		{
			if((o.physicalRepresentation.shape!=null)&&(o.physicalRepresentation.shape.getClass().getName().equalsIgnoreCase(shapes[i])))
				pw.append("<option value=\""+shapes[i]+"\" selected>"+shapes[i]+"</option>");
			else
				pw.append("<option value=\""+shapes[i]+"\">"+shapes[i]+"</option>");
		}
		
		pw.append("</select>");

		pw.append("<input name=\"instance\" type=\"hidden\" value=\""+o.uniqueID+"\">");
		
		//pw.append("<a class=\"btn btn-primary btn-block\" href='http://www.google.com' onclick='return checkLogin()'>Login</a>");
		pw.append("<button class=\"btn btn-default\" type='submit'>Set</button>");
		pw.append("</form>");
		
		ParametricShape shape = o.physicalRepresentation.shape;
		if(shape!=null)
		{
			pw.append("<form action='/Edit"+o.type+"Instance.cgi/setShapeParameters' method='get' role='form'>\n");
			pw.append("<input name=\"instance\" type=\"hidden\" value=\""+o.uniqueID+"\">");
			String[] pnames = shape.getParameterNames();
			
			pw.append("<table>");
			for(int i=0;i<pnames.length;i++)
			{
				pw.append("<tr>");
				pw.append("<td>");
				pw.append(pnames[i]);
				pw.append("</td>");
				pw.append("<td>");
				pw.append("<input name=\""+pnames[i]+"\" type=\"number\" step=\"0.001\" value=\""+String.format( "%.3f", shape.parameters[i])+"\">");
				
				pw.append("</td>");
				pw.append("</tr>");
			}
			pw.append("</table>");
			pw.append("<button class=\"btn btn-default\" type='submit'>Set</button>");
			pw.append("</form>");
			
			if(shape instanceof CSGShape)
			{
				addSubShapeForm(pw, o.type, o, shapes);
			}
		}		
	}
	
	public static void processFunction(WebRequest toProcess,String function,ObjectLanguageInstance o,String instance,FileStoreInterface fs)
	{
		if(function.equalsIgnoreCase("setShape"))
		{
			String shape = toProcess.parms.get("shape");
			ParametricShape oldshape = o.physicalRepresentation.shape;
			o.physicalRepresentation.shape = ParametricShape.get(shape);
			if(oldshape!=null)
			{
				o.physicalRepresentation.shape.makeSimilarSize(oldshape);
			}
		}
		else
		if(function.equalsIgnoreCase("setShapeParameters"))
		{
			ParametricShape shape = o.physicalRepresentation.shape;
			String[] pnames = shape.getParameterNames();
			
			for(int i=0;i<pnames.length;i++)
			{
				String v = toProcess.parms.get(pnames[i]);
				shape.parameters[i] = Double.parseDouble(v);
			}

			shape.parametersUpdated();
		}
		else
		if(function.equalsIgnoreCase("addCharacter"))
		{
			//Create a default instance of a room (using a model)
			ObjectLanguageInstance r = new ObjectLanguageInstance();
//			name
//			type
			r.name = toProcess.parms.get("name");
			r.name = r.name.replace(' ', '_');
			r.type = "character";
			String roomConcept = toProcess.parms.get("type");
			String roomInstance = instance+"_"+r.name;
			r.uniqueID = roomConcept+"/"+roomInstance;
			
			//only add the instance if it doesn't already exist
			int existsind = ArrayUtils.getIndex(o.contains, r.uniqueID);
			if(existsind==-1)
			{	
			
    			String rpath = "Concepts/"+roomConcept+"/instances/";
    			fs.createDirectory(rpath);
    			rpath += roomInstance;
    			fs.createDirectory(rpath);
    			
    			
    			
    			r.physicalRepresentation = new ObjectPhysicalInstance();
    			ParametricShape shape = new HumanShape();
    			r.physicalRepresentation.shape = shape;

//    			x
//    			y
//    			z
//    			width
//    			height
//    			ceiling_height
//    			shape
    			
    			o.add(r);
    			o.physicalRepresentation.add(r.physicalRepresentation, 
    					Double.parseDouble(toProcess.parms.get("x")), 
    					Double.parseDouble(toProcess.parms.get("y")),
    					Double.parseDouble(toProcess.parms.get("z")),
    					Double.parseDouble(toProcess.parms.get("xr")),
    					Double.parseDouble(toProcess.parms.get("yr")),
    					Double.parseDouble(toProcess.parms.get("zr"))
    					);
    			rpath += "/data.txt";
    			ObjectLanguageInstance.saveObject(r, fs, rpath);
			}
		}
		else
		if(function.equalsIgnoreCase("addObject"))
		{
			//Create a default instance of a room (using a model)
			ObjectLanguageInstance r = new ObjectLanguageInstance();
//			name
//			type
			r.name = toProcess.parms.get("name");
			r.name = r.name.replace(' ', '_');
			r.type = "object";
			String roomConcept = toProcess.parms.get("type");
			String roomInstance = instance+"_"+r.name;
			r.uniqueID = roomConcept+"/"+roomInstance;
			
			//only add the instance if it doesn't already exist
			int existsind = ArrayUtils.getIndex(o.contains, r.uniqueID);
			if(existsind==-1)
			{	
			
    			String rpath = "Concepts/"+roomConcept+"/instances/";
    			fs.createDirectory(rpath);
    			rpath += roomInstance;
    			fs.createDirectory(rpath);
    			
    			
    			
    			r.physicalRepresentation = new ObjectPhysicalInstance();
    			ParametricShape shape = null;
    			if(toProcess.parms.get("shape").equalsIgnoreCase("ModifiedCopyShape"))
    			{
    				String ocopypath = toProcess.parms.get("shapeToCopyFrom");
    				ObjectLanguageInstance ocopy = ObjectLanguageInstance.loadObject(fs, ocopypath);
    				ParametricShape shapeToCopy = ocopy.physicalRepresentation.shape;
    				ModifiedCopyShape mshape = new ModifiedCopyShape();
    				mshape.baseCopy = shapeToCopy;
    				mshape.baseUpdated();
    				shape = mshape;
    			}
    			else
    			{
    				shape = ParametricShape.get(toProcess.parms.get("shape"));
    			}
    			r.physicalRepresentation.shape = shape;

    			shape.setCuboidDimensions(Double.parseDouble(toProcess.parms.get("width")), 
    					Double.parseDouble(toProcess.parms.get("height")),
    					Double.parseDouble(toProcess.parms.get("ceiling_height")));

//    			x
//    			y
//    			z
//    			width
//    			height
//    			ceiling_height
//    			shape
    			
    			o.add(r);
    			o.physicalRepresentation.add(r.physicalRepresentation, 
    					Double.parseDouble(toProcess.parms.get("x")), 
    					Double.parseDouble(toProcess.parms.get("y")),
    					Double.parseDouble(toProcess.parms.get("z")),
    					Double.parseDouble(toProcess.parms.get("xr")),
    					Double.parseDouble(toProcess.parms.get("yr")),
    					Double.parseDouble(toProcess.parms.get("zr"))
    					);
    			rpath += "/data.txt";
    			ObjectLanguageInstance.saveObject(r, fs, rpath);
			}
		}
		else
		if(function.equalsIgnoreCase("removeContained"))
		{
			//don't modify the room data at this point
    		String rid = toProcess.parms.get("cinstance");
    		int cind = ArrayUtils.getIndex(o.contains,rid);
    		o.physicalRepresentation.contains_metric_transform = ArrayUtils.remove(o.physicalRepresentation.contains_metric_transform,cind*12,12);
    		o.contains = ArrayUtils.remove(o.contains,cind,1);
    		o.contains_type = ArrayUtils.remove(o.contains_type,cind,1);
		}
		else
		if(function.equalsIgnoreCase("setContainedPosition"))
		{
    		String rid = toProcess.parms.get("cinstance");
    		int rind = rid.indexOf("/");
    		
    		String rconcept = rid.substring(0,rind);
    		String rinstance = rid.substring(rind+1);

    		String rinstancepath = "Concepts/"+rconcept+"/instances/"+rinstance;    		
    		String rdatapath = rinstancepath+"/data.txt";

    		ObjectLanguageInstance r = ObjectLanguageInstance.loadObject(fs, rdatapath);
    		
    		double x = Double.parseDouble(toProcess.parms.get("x"));
    		double y = Double.parseDouble(toProcess.parms.get("y"));
    		double z = Double.parseDouble(toProcess.parms.get("z"));
    		double xr = Double.parseDouble(toProcess.parms.get("xr"));
    		double yr = Double.parseDouble(toProcess.parms.get("yr"));
    		double zr = Double.parseDouble(toProcess.parms.get("zr"));
    		
    		int cind = ArrayUtils.getIndex(o.contains,rid);
    		
    		GeneralMatrixDouble zrot = new GeneralMatrixDouble(4,4);
    		zrot.set3DTransformRotation(xr*Math.PI/180.0, yr*Math.PI/180.0, zr*Math.PI/180.0);

    		o.physicalRepresentation.contains_metric_transform[12*cind+ 0] = zrot.value[4*0+0];
    		o.physicalRepresentation.contains_metric_transform[12*cind+ 1] = zrot.value[4*0+1];
    		o.physicalRepresentation.contains_metric_transform[12*cind+ 2] = zrot.value[4*0+2];

    		o.physicalRepresentation.contains_metric_transform[12*cind+ 3] = zrot.value[4*1+0];
    		o.physicalRepresentation.contains_metric_transform[12*cind+ 4] = zrot.value[4*1+1];
    		o.physicalRepresentation.contains_metric_transform[12*cind+ 5] = zrot.value[4*1+2];

    		o.physicalRepresentation.contains_metric_transform[12*cind+ 6] = zrot.value[4*2+0];
    		o.physicalRepresentation.contains_metric_transform[12*cind+ 7] = zrot.value[4*2+1];
    		o.physicalRepresentation.contains_metric_transform[12*cind+ 8] = zrot.value[4*2+2];

    		o.physicalRepresentation.contains_metric_transform[12*cind+ 9] = x;
    		o.physicalRepresentation.contains_metric_transform[12*cind+10] = y;
    		o.physicalRepresentation.contains_metric_transform[12*cind+11] = z;

    		int wind = ArrayUtils.getIndex(r.within,o.uniqueID);
    		
    		System.arraycopy(o.physicalRepresentation.contains_metric_transform, 12*cind, 
    				r.physicalRepresentation.within_metric_transform, 12*wind, 
    				12);
//    		r.physicalRepresentation.within_metric_transform[12*wind+ 9] = x;
//    		r.physicalRepresentation.within_metric_transform[12*wind+10] = y;
//    		r.physicalRepresentation.within_metric_transform[12*wind+11] = z;
    		
			ObjectLanguageInstance.saveObject(r, fs, rdatapath);
		}
		else
		if(function.equalsIgnoreCase("removeSubShape"))
		{
			int shapeind = Integer.parseInt(toProcess.parms.get("shape"));
			CSGShape csg = (CSGShape)o.physicalRepresentation.shape;
			csg.removeSubShape(shapeind);
		}
		else
		if(function.equalsIgnoreCase("addSubShape"))
		{
			CSGShape csg = (CSGShape)o.physicalRepresentation.shape;

			ParametricShape shape = ParametricShape.get(toProcess.parms.get("shape"));
			
			shape.setCuboidDimensions(Double.parseDouble(toProcess.parms.get("width")), 
					Double.parseDouble(toProcess.parms.get("height")),
					Double.parseDouble(toProcess.parms.get("ceiling_height")));

    		double x = Double.parseDouble(toProcess.parms.get("x"));
    		double y = Double.parseDouble(toProcess.parms.get("y"));
    		double z = Double.parseDouble(toProcess.parms.get("z"));
    		double xr = Double.parseDouble(toProcess.parms.get("xr"));
    		double yr = Double.parseDouble(toProcess.parms.get("yr"));
    		double zr = Double.parseDouble(toProcess.parms.get("zr"));

    		String name = toProcess.parms.get("name");
			
    		csg.addShape(shape, name, CSGShape.ADD, x, y, z, xr, yr, zr);
		}
	}
} public class InternalIndex extends DynamicWebPage
{
	public InternalIndex(FileStoreInterface fs)
	{
		super(fs);
	}

	public boolean process(WebRequest toProcess)
	{
        if(toProcess.path.equalsIgnoreCase("Index.html"))
        {
        	return true;
        }
        return false;
	}
	
	public static void printTaxonomy(PrintWriter pw,String path,int depth)
	{
		File taxdir = new File(path);
		String[] taxs = taxdir.list();
		for(int i=0;i<taxs.length;i++)
		{
			String t = taxs[i];
			if(t.startsWith(".")||t.endsWith(".md"))
			{
				continue;
			}
			
			if(t.startsWith("map_"))
			{
				pw.append("<a href=\"/ViewMapConcept.html/"+t+"\">"+t+"</a><br>");
			}
			else
			if(t.startsWith("room_"))
			{
				pw.append("<a href=\"/ViewRoomConcept.html/"+t+"\">"+t+"</a><br>");
			}
			else
			if(t.startsWith("object_"))
			{
				pw.append("<a href=\"/ViewObjectConcept.html/"+t+"\">"+t+"</a><br>");
			}
			else
			if(t.startsWith("pose_"))
			{
				pw.append("<a href=\"/ViewPoseConcept.html/"+t+"\">"+t+"</a><br>");
			}
			else
			if(t.startsWith("story_"))
			{
				pw.append("<a href=\"/ViewStoryConcept.html/"+t+"\">"+t+"</a><br>");
			}
			else
			{
				pw.append("<h"+depth+">"+t+"</h"+depth+">");

				printTaxonomy(pw, path+"/"+t, depth+1);
			}
		}
	}
	
	public void out(WebRequest r,Object metadata,OutputStream s)
	{
		PrintWriter pw = new PrintWriter( s );

		//list the concepts
		pw.append("<h1>Rough World</h1>");

		pw.append("<h2><a href='EditPoseModel.html'>Create A Pose Model</a></h2>");
		
		
		//the default taxonomy
		//view all the maps, objects etc. as lists
		File taxdir = new File("Taxonomies");
		String[] taxs = taxdir.list();
		for(int i=0;i<taxs.length;i++)
		{
			String t = taxs[i];
			if(t.startsWith(".")||t.endsWith(".md"))
			{
				continue;
			}
			
			pw.append("<h2>"+t+"</h2>");
			printTaxonomy(pw, "Taxonomies/"+t,3);
		}
		
		pw.flush();
	}
} public class ViewMapConcept extends DynamicWebPage
{
	public ViewMapConcept(FileStoreInterface fs)
	{
		super(fs);
	}
	public boolean process(WebRequest toProcess)
	{
        if(toProcess.path.startsWith("ViewMapConcept.html"))
        {
        	return true;
        }
        return false;
	}
	
	public void out(WebRequest r,Object metadata,OutputStream s)
	{
		PrintWriter pw = new PrintWriter( s );
		int ind = r.path.indexOf("/");
		String concept = r.path.substring(ind+1);
		//list the concepts
		pw.append("<h1>"+concept+"</h1>");

		pw.append("<h2>Instances</h2>");
		//the default taxonomy
		//view all the maps, objects etc. as lists
		File instsdir = new File("Concepts/"+concept+"/Instances");
		String[] insts = instsdir.list();
		for(int i=0;i<insts.length;i++)
		{
			String inst = insts[i];
			if(inst.startsWith(".")||inst.endsWith(".md"))
			{
				continue;
			}
			
			pw.append("<a href=\"/EditMapInstance.html/"+concept+"/"+inst+"\">"+inst+"</a><br>");
		}
		pw.flush();
	}
} public class ViewObjectConcept extends DynamicWebPage
{
	public ViewObjectConcept(FileStoreInterface fs)
	{
		super(fs);
	}
	
	public boolean process(WebRequest toProcess)
	{
        if(toProcess.path.startsWith("ViewObjectConcept.html"))
        {
        	return true;
        }
        return false;
	}
	
	public void out(WebRequest r,Object metadata,OutputStream s)
	{
		PrintWriter pw = new PrintWriter( s );
		int ind = r.path.indexOf("/");
		String concept = r.path.substring(ind+1);
		//list the concepts
		pw.append("<h1>"+concept+"</h1>");

		pw.append("<h2>Instances</h2>");
		//the default taxonomy
		//view all the maps, objects etc. as lists
		File instsdir = new File("Concepts/"+concept+"/Instances");
		String[] insts = instsdir.list();
		for(int i=0;i<insts.length;i++)
		{
			String inst = insts[i];
			if(inst.startsWith(".")||inst.endsWith(".md"))
			{
				continue;
			}
			
			pw.append("<a href=\"/EditObjectInstance.html/"+concept+"/"+inst+"\">"+inst+"</a><br>");
		}
		pw.flush();
	}
} public class ViewPoseConcept extends DynamicWebPage
{
	public ViewPoseConcept(FileStoreInterface fs)
	{
		super(fs);
	}
	
	public boolean process(WebRequest toProcess)
	{
        if(toProcess.path.startsWith("ViewPoseConcept.html"))
        {
        	return true;
        }
        return false;
	}
	
	public void out(WebRequest r,Object metadata,OutputStream s)
	{
		PrintWriter pw = new PrintWriter( s );
		int ind = r.path.indexOf("/");
		String concept = r.path.substring(ind+1);
		//list the concepts
		pw.append("<h1>"+concept+"</h1>");

		pw.append("<h2>Instances</h2>");
		//the default taxonomy
		//view all the maps, objects etc. as lists
		File instsdir = new File("Concepts/"+concept+"/instances");
		if(instsdir.exists())
		{
			String[] insts = instsdir.list();
			for(int i=0;i<insts.length;i++)
			{
				String inst = insts[i];
				if(inst.startsWith(".")||inst.endsWith(".md"))
				{
					continue;
				}
				
				pw.append("<a href=\"/EditPoseInstance.html/"+concept+"/"+inst+"\">"+inst+"</a><br>");
			}
		}
		
		pw.append("<h2>Models</h2>");
		//the default taxonomy
		//view all the maps, objects etc. as lists
		File modelsdir = new File("Concepts/"+concept+"/models");
		if(modelsdir.exists())
		{
			String[] insts = modelsdir.list();
			for(int i=0;i<insts.length;i++)
			{
				String inst = insts[i];
				if(inst.startsWith(".")||inst.endsWith(".md"))
				{
					continue;
				}
				
				pw.append("<a href=\"/EditPoseModel.html/"+concept+"/"+inst+"\">"+inst+"</a><br>");
			}
		}
		pw.flush();
	}
} public class ViewRoomConcept extends DynamicWebPage
{
	public ViewRoomConcept(FileStoreInterface fs)
	{
		super(fs);
	}
	public boolean process(WebRequest toProcess)
	{
        if(toProcess.path.startsWith("ViewRoomConcept.html"))
        {
        	return true;
        }
        return false;
	}
	
	public void out(WebRequest r,Object metadata,OutputStream s)
	{
		PrintWriter pw = new PrintWriter( s );
		int ind = r.path.indexOf("/");
		String concept = r.path.substring(ind+1);
		//list the concepts
		pw.append("<h1>"+concept+"</h1>");

		pw.append("<h2>Instances</h2>");
		//the default taxonomy
		//view all the maps, objects etc. as lists
		File instsdir = new File("Concepts/"+concept+"/Instances");
		String[] insts = instsdir.list();
		for(int i=0;i<insts.length;i++)
		{
			String inst = insts[i];
			if(inst.startsWith(".")||inst.endsWith(".md"))
			{
				continue;
			}
			
			pw.append("<a href=\"/EditRoomInstance.html/"+concept+"/"+inst+"\">"+inst+"</a><br>");
		}
		pw.flush();
	}
} public class ViewStoryConcept extends DynamicWebPage
{
	public ViewStoryConcept(FileStoreInterface fs)
	{
		super(fs);
	}
	public boolean process(WebRequest toProcess)
	{
        if(toProcess.path.startsWith("ViewStoryConcept.html"))
        {
        	return true;
        }
        return false;
	}
	
	public void out(WebRequest r,Object metadata,OutputStream s)
	{
		PrintWriter pw = new PrintWriter( s );
		int ind = r.path.indexOf("/");
		String concept = r.path.substring(ind+1);
		//list the concepts
		pw.append("<h1>"+concept+"</h1>");

		pw.append("<h2>Create Instance</h2>");

		//be able to create a new instance of a pose model
		pw.append("<form action='/EditStoryInstance.scgi/createInstance' method='get' role='form'>\n");
		pw.append("<input name=\"concept\" type=\"hidden\" value=\""+concept+"\">");

		pw.append("<input name=\"name\" type=\"txt\" value=\"instance_name\">");
		
		pw.append("<button class=\"btn btn-default\" type='submit'>Create</button>");

		pw.append("</form>");

		pw.append("<h2>Instances</h2>");
		//the default taxonomy
		//view all the maps, objects etc. as lists
		File instsdir = new File("Concepts/"+concept+"/Instances");
		String[] insts = instsdir.list();
		if(insts!=null)
		{
			for(int i=0;i<insts.length;i++)
			{
				String inst = insts[i];
				if(inst.startsWith(".")||inst.endsWith(".md"))
				{
					continue;
				}
				
				pw.append("<a href=\"/EditStoryInstance.html/"+concept+"/"+inst+"\">"+inst+"</a><br>");
			}
		}
		pw.flush();
	}
}
import java.util.ArrayList;
import utils.FileStoreInterface;
import web.WebClient;
import web.WebInterface;
import web.WebRequest;
import web.WebResponse;
import website.views.DynamicWebPage;
import website.views.EditMapInstance;
import website.views.EditObjectInstance;
import website.views.EditPortalInstance;
import website.views.EditPoseModel;
import website.views.EditRoomInstance;
import website.views.EditStoryInstance;
import website.views.EditUtils;
import website.views.InternalIndex;
import website.views.ViewMapConcept;
import website.views.ViewObjectConcept;
import website.views.ViewPoseConcept;
import website.views.ViewRoomConcept;
import website.views.ViewStoryConcept;
package website;



//An internal website for experimentation and curation of the dataset
public class Internal 
{
	String externalip = null;
	WebInterface webInterface = null;
	FileStoreInterface fs = new FileStoreInterface("");
	
	ArrayList<DynamicWebPage> pages = new ArrayList<DynamicWebPage>();
	
	boolean keepRunning = true;
	
	//This is a main function, it is the first function that is called when a java program starts
	//The array of strings named commandLineParameters are extra information that can be
	//passed to a program when it is run using the command line
	//this is just one of many ways of varying a programs behaviour by passing information to it
		//for example some programs have config files, which are usually simple text files with name=value settings on each line
		//programs with databases (like this one) can store their settings in the database
		//some programs even read properties stored by the operating system in system variables
	public static void main(String[] commandLineParameters) throws Exception 
	{
		Internal rw = new Internal();
		rw.init();
		rw.run();
	}
	
	public void init()
	{
	
		try
		{
			if(externalip==null)
			{
				//get the world ip address
				//login to otidae
				//upload the ip address
				WebClient checkIP = new WebClient("http://icanhazip.com/");