package daruma.server;

import java.io.OutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.FileOutputStream;
import java.io.File;
import java.io.UnsupportedEncodingException;

import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import daruma.storage_manager.DBMSStorageManager;
import daruma.storage_manager.StorageException;

import daruma.sql.DatabaseConnectionException;
import daruma.sql.MySQLDatabaseConnectionFactory;
import daruma.wfs.SOAPWFSProcessor;
import daruma.util.EncodingConversionOutputStream;
import daruma.util.LogWriter;
import daruma.util.TeeInputStream;
import daruma.util.TeeOutputStream;
import daruma.util.FatalException;

import daruma.util.Itk;


public class ConnectionHandler implements Runnable
{
	private	static Object		lock = new Object();

	private InputStream		inputStream;
	private OutputStream		outputStream;

	private	DBMSStorageManager	storage;
	private	String			debugTag;
	private	boolean			debug = false;
	private	boolean			ioLog = false;
	private	File			ioLogDirectory = null;
	private	boolean			compressStreamByGzip = false;
	private	long			connectionID = -1;

	public	ConnectionHandler( InputStream  in ,
				   OutputStream  out ,
				   String  databaseName ,
				   String  user ,
				   String  passwd ,
				   String  debugTag ,
				   boolean  debug ,
				   boolean  ioLog ,
				   File  ioLogDirectory ,
				   long  connectionID )
					throws DatabaseConnectionException
	{
		this.inputStream = in;
		this.outputStream = out;

		this.debugTag = debugTag;
		this.debug = debug;
		this.ioLog = ioLog;
		this.ioLogDirectory = ioLogDirectory;
		this.connectionID = connectionID;

		this.storage = new DBMSStorageManager
				   ( (new MySQLDatabaseConnectionFactory())
								  .create() );
		try
		{
			this.storage.connect( databaseName , user , passwd );
		}
		catch( DatabaseConnectionException e )
		{
			throw e;
		}
	}

	public	void	setCompressStreamByGzip( boolean  flag )
	{
		this.compressStreamByGzip = flag;
	}



	public	void	run()
	{
		//
		// exclusive control for processing by single thread
		//
		synchronized( ConnectionHandler.lock )
		{
		    try
		    {
			LogWriter.qwrite("INFO",
					 "[" + Itk.getCurrentTimeStr() + "] ",
					 "Open  :", this.debugTag) ;
			this.runImplement();
		    } 
		    finally
		    {
			LogWriter.qwrite("INFO",
					 "[" + Itk.getCurrentTimeStr() + "] ",
					 "Close :", this.debugTag) ;
		    }
		}
	}

	public	void	runImplement()
	{
		try
		{
			//
			// prepare I/O
			//
			InputStream	in  = this.inputStream;
			OutputStream	out = get_ostream(outputStream);

			if ( this.compressStreamByGzip )
			{
				in  = new GZIPInputStream ( in );
				out = new GZIPOutputStream( out );
			}

			if ( this.debug )
			{
				in  = new TeeInputStream
					  ( in  , System.out , false );

				out = new TeeOutputStream
					  ( out , System.out , false );
			}

			if ( this.ioLog )
			{
				String	fileNameBase
					= "log-" + this.connectionID;

				OutputStream	inputLogFile;
				inputLogFile = new FileOutputStream
						( new File
						  ( this.ioLogDirectory ,
						    fileNameBase
						    + "-from-client" ) );

				OutputStream	outputLogFile;
				outputLogFile = new FileOutputStream

						( new File
						  ( this.ioLogDirectory ,
						    fileNameBase
						    + "-to-client" ) );

				in  = new TeeInputStream
					  ( in  , inputLogFile , false );

				out = new TeeOutputStream
					  ( out , outputLogFile , false );
			}


			//
			// process document
			//
			SOAPWFSProcessor	p = new SOAPWFSProcessor();

			p.setDebugTag( this.debugTag );

			p.process( in , out , storage );

			//
			// close database connection
			//
			try
			{
				this.storage.close();
			}
			catch( DatabaseConnectionException  e )
			{
				throw e;
			}


			//
			// close I/O connection
			//
			out.flush();
			out.close();
			in.close();

			/*
			this.socket.close();
			*/
			this.inputStream.close();
			this.outputStream.close();
		}
		catch( IOException  e )
		{
			e.printStackTrace();
		}
		catch( DatabaseConnectionException dce)
		{
			dce.printStackTrace();
		}
		catch( FatalException  e )
		{
			e.printStackTrace();
		}
		finally
		{
			try
			{
				this.inputStream.close();
			}
			catch( IOException  e )
			{
				e.printStackTrace();
			}

			try
			{
				this.outputStream.close();
			}
			catch( IOException  e )
			{
				e.printStackTrace();
			}
		}
	}


	private static Boolean cEncodingWarned = false;

	/** XXX:
	 *
	 * InputStream/OutputStream are now obsolete for text I/O.
	 * Use Reader/Writer for these purpose.
	 *
	 * These lines are some trivial fix.
	 * We must fix it.
	 */
	private OutputStream get_ostream(OutputStream raw) {
		String       encoding = System.getProperty("file.encoding");
		OutputStream enc;

		if ( ! (encoding.equalsIgnoreCase("utf-8") ||
			encoding.equalsIgnoreCase("utf8"))) {
			if (!cEncodingWarned) {
				LogWriter.qwrite("WARN", "I/O encoding is not UTF-8. Applies trivial encoding conversion.");
				cEncodingWarned = true;
			}
			try {
				enc = new EncodingConversionOutputStream(raw, "utf-8");
			} catch(UnsupportedEncodingException uee) {
				LogWriter.qwrite("FATAL", "Encoding name error. Message: ", uee.getMessage());
				enc = null;

				throw new RuntimeException();
			}
		} else {
			enc = raw;
		}

		return enc;
	}
}
