package com.planet_ink.fakedb;
import java.sql.*;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
import java.lang.ref.WeakReference;


@SuppressWarnings({"unchecked","rawtypes"})
public class Connection implements java.sql.Connection
{
   static private java.util.Map databases = new java.util.HashMap();
   static private java.util.Map references = new java.util.HashMap();
   private Backend backend;
   private boolean closed=false;
   private String oldPath="";

   static private void log(String x)
   {
	  System.err.println("Connection: "+x);
   }

   Backend getBackend() { return backend; }

   public Connection(String path) throws java.sql.SQLException
   {
	   connect(path);
   }

   public String getOldPath()
   {
	   return oldPath;
   }

   private void connect(String path) throws java.sql.SQLException
   {
	  try
	  {
		 path=(new java.io.File(path)).getCanonicalPath();
	  }
	  catch (final java.io.IOException e) {}

	  oldPath=path;
	  if(!closed)
	  {
		  synchronized(references)
		  {
			  Integer conCount=(Integer)references.get(path);
			  if(conCount==null)
				  conCount=Integer.valueOf(0);
			  references.remove(path);
			  references.put(path,Integer.valueOf(conCount.intValue()+1));
		  }
	  }

	  synchronized (databases)
	  {
		  final WeakReference ref=(WeakReference)databases.get(path);
		  Backend   	backend=null;
		  if (ref!=null)
			 backend=(Backend)ref.get();
		  if (backend==null)
		  {
			 backend=new Backend();
			 if (!backend.open(new java.io.File(path)))
				throw new java.sql.SQLException("unable to open database");
			 databases.put(path,new WeakReference(backend));
		  }
		  this.backend=backend;
	  }
   }

   @Override
public java.sql.Statement createStatement() throws java.sql.SQLException
   {
	  return new Statement(this);
   }

   @Override
public java.sql.Statement createStatement(int a, int b) throws java.sql.SQLException
   {
	   return createStatement();
   }

   @Override
public java.sql.Statement createStatement(int a, int b, int c) throws java.sql.SQLException
   {
	   return createStatement();
   }

   @Override
public java.sql.PreparedStatement prepareStatement(String sql) throws java.sql.SQLException
   {
	  final PreparedStatement p = new PreparedStatement(this);
	  p.prepare(sql);
	  return p;
   }

   @Override
public java.sql.PreparedStatement prepareStatement(String sql,int a) throws java.sql.SQLException
   {
	   return prepareStatement(sql);
   }

   @Override
public java.sql.PreparedStatement prepareStatement(String sql,int[] a) throws java.sql.SQLException
   {
	   return prepareStatement(sql);
   }

   @Override
public java.sql.PreparedStatement prepareStatement(String sql,String[] a) throws java.sql.SQLException
   {
	   return prepareStatement(sql);
   }

   @Override
public java.sql.PreparedStatement prepareStatement(String sql,int a,int b) throws java.sql.SQLException
   {
	   return prepareStatement(sql);
   }

   public java.sql.PreparedStatement prepareStatement(String sql,int a,int[] b) throws java.sql.SQLException
   {
	   return prepareStatement(sql);
   }

   @Override
public java.sql.PreparedStatement prepareStatement(String sql,int a,int b,int c) throws java.sql.SQLException
   {
	   return prepareStatement(sql);
   }

   @Override
public java.sql.CallableStatement prepareCall(String sql) throws java.sql.SQLException
   {
	  log("prepareCall");
	  throw new java.sql.SQLException("Callable statments not suppoted.", "S1C00");
   }

   @Override
public java.sql.CallableStatement prepareCall(String sql,int a,int b) throws java.sql.SQLException
   {
	   return prepareCall(sql);
   }

   @Override
public java.sql.CallableStatement prepareCall(String sql,int a,int b,int c) throws java.sql.SQLException
   {
	   return prepareCall(sql);
   }

   @Override
public int getHoldability()
   {
	   return java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT;
   }

   @Override
public java.sql.Savepoint setSavepoint()
	   throws java.sql.SQLException
   {
	   throw new java.sql.SQLException("Savepoints not supported");
   }

   @Override
public java.sql.Savepoint setSavepoint(String S)
	   throws java.sql.SQLException
   {
	   throw new java.sql.SQLException("Savepoints not supported");
   }

   @Override
public void rollback(java.sql.Savepoint saved)
	   throws java.sql.SQLException
   {
	   throw new java.sql.SQLException("Savepoints not supported");
   }

   @Override
public void releaseSavepoint(java.sql.Savepoint saved)
	   throws java.sql.SQLException
   {
	   throw new java.sql.SQLException("Savepoints not supported");
   }

   @Override
public String nativeSQL(String sql) throws java.sql.SQLException
   {
	  return sql;
   }

   @Override
public void setAutoCommit(boolean autoCommit) throws java.sql.SQLException
   {
	  log("setAutoCommit");
	  if (!autoCommit)
		  throw new java.sql.SQLException("Cannot disable AUTO_COMMIT", "08003");
	  return;
   }

   @Override
public boolean getAutoCommit() throws java.sql.SQLException
   {
	  return true;
   }

   @Override
public void commit() throws java.sql.SQLException
   {
		//log("commit");
   }

   @Override
public void rollback() throws java.sql.SQLException
   {
		//log("rollback");
   }

   @Override
public void close() throws java.sql.SQLException
   {
		if(!closed)
		{
			closed=true;
			synchronized(references)
			{
				final Integer conCount=(Integer)references.get(oldPath);
				if(conCount!=null)
				{
					if(conCount.intValue()==1)
					{
						if(backend!=null)
							backend.clearFakeTables();
						backend=null;
						references.remove(oldPath);
						databases.remove(oldPath);
					}
					else
					{
						references.remove(oldPath);
						references.put(oldPath,Integer.valueOf(conCount.intValue()-1));
					}
				}
			}
		}
   }

   @Override
public boolean isClosed() throws java.sql.SQLException
   {
		return closed;
   }

   @Override
public java.sql.DatabaseMetaData getMetaData() throws java.sql.SQLException
   {
	  log("getMetaData");
	  return null;
   }

   @Override
public void setReadOnly (boolean readOnly) throws java.sql.SQLException
   {
	  log("setReadOnly");
   }

   @Override
public boolean isReadOnly() throws java.sql.SQLException
   {
	  return false;
   }

   @Override
public void setCatalog(String Catalog) throws java.sql.SQLException
   {
	  log("setCatalog");
   }

   @Override
public String getCatalog() throws java.sql.SQLException
   {
	   return "FAKEDB";
   }

   @Override
public void setTransactionIsolation(int level) throws java.sql.SQLException
   {
	  log("setTransactionIsolation");
	  throw new java.sql.SQLException("Transaction Isolation Levels are not supported.", "S1C00");
   }

   @Override
public int getTransactionIsolation() throws java.sql.SQLException
   {
	  return java.sql.Connection.TRANSACTION_NONE;
   }

   @Override
public java.sql.SQLWarning getWarnings() throws java.sql.SQLException
   {
	  log("getWarnings");
	  return null;
   }

   @Override
public void clearWarnings() throws java.sql.SQLException
   {
	  log("clearWarnings");
   }

   @Override
public void setHoldability(int holdability) throws java.sql.SQLException
   {}

   @Override
public java.util.Map getTypeMap() throws java.sql.SQLException
   {
	   return new java.util.HashMap();
   }

   @Override
public Array createArrayOf(String arg0, Object[] arg1) throws SQLException
   {
	   return null;
   }

   @Override
public Blob createBlob() throws SQLException
   {
	   return null;
   }

   @Override
public Clob createClob() throws SQLException
   {
	   return null;
   }

   @Override
public NClob createNClob() throws SQLException
   {
	   return null;
   }

   @Override
public SQLXML createSQLXML() throws SQLException
   {
	   return null;
   }

   @Override
public Struct createStruct(String arg0, Object[] arg1) throws SQLException
   {
	   return null;
   }

   @Override
public Properties getClientInfo() throws SQLException
   {
	   return null;
   }

   @Override
public String getClientInfo(String arg0) throws SQLException
   {
	   return null;
   }

   @Override
public boolean isValid(int arg0) throws SQLException
   {
	   return false;
   }

   @Override
public void setClientInfo(Properties arg0) throws SQLClientInfoException
   {
   }

   @Override
public void setClientInfo(String arg0, String arg1) throws SQLClientInfoException
   {
   }

   //public void setTypeMap(Map arg0) throws SQLException { }
   @Override
public void setTypeMap(Map<String, Class<?>> arg0) throws SQLException
   {
   }

   @Override
public boolean isWrapperFor(Class<?> iface) throws SQLException
   {
	   return false;
   }

   @Override
public <T> T unwrap(Class<T> iface) throws SQLException
   {
	   return null;
   }

	public void setSchema(String schema) throws SQLException { connect(schema); }
	public String getSchema() throws SQLException { return oldPath; }
	public void abort(Executor executor) throws SQLException { this.close(); }
	public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { }
	public int getNetworkTimeout() throws SQLException { return 0; }

   // JDK 1.4 stuff
/*
   public int getHoldability() throws java.sql.SQLException
	  { return java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT; }
   public void releaseSavepoint(java.sql.Savepoint savepoint) throws java.sql.SQLException
	  {}
   public java.sql.Savepoint setSavepoint() throws java.sql.SQLException
	  { return null; }
   public java.sql.Savepoint setSavepoint(String name) throws java.sql.SQLException
	  { return null; }
   public void rollback(java.sql.Savepoint p) throws java.sql.SQLException
   { rollback(); }
*/
}
