package nl.idgis.publisher.provider.database;

import java.sql.Blob;
import java.sql.Clob;
import java.sql.ResultSet;
import java.util.concurrent.ExecutorService;

import org.apache.commons.io.IOUtils;

import nl.idgis.publisher.domain.service.Type;
import nl.idgis.publisher.provider.database.messages.AbstractDatabaseColumnInfo;
import nl.idgis.publisher.provider.database.messages.FetchTable;
import nl.idgis.publisher.provider.protocol.WKBGeometry;

import akka.actor.Props;
import akka.event.Logging;
import akka.event.LoggingAdapter;

import oracle.sql.STRUCT;
import oracle.sql.TIMESTAMP;

import org.deegree.geometry.Geometry;
import org.deegree.geometry.io.WKBWriter;
import org.deegree.sqldialect.oracle.sdo.SDOGeometryConverter;

public class OracleDatabaseCursor extends AbstractDatabaseCursor {
	
	private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);
	
	private final SDOGeometryConverter converter = new SDOGeometryConverter();

	public OracleDatabaseCursor(ResultSet t, FetchTable fetchTable, ExecutorService executorService) {
		super(t, fetchTable, executorService);
	}
	
	public static Props props(ResultSet t, FetchTable fetchTable, ExecutorService executorService) {
		return Props.create(OracleDatabaseCursor.class, t, fetchTable, executorService);
	}

	@Override
	Object convert(AbstractDatabaseColumnInfo columnInfo, Object value) throws Exception {
		if(value == null) {
			return null;
		}
		
		log.debug("database column value: " + value);
		
		if(columnInfo.getType() == Type.DATE) {
			String typeName = columnInfo.getTypeName();
			if("TIMESTAMP(6)".equals(typeName)) {
				log.debug("database column value is of type timestamp(6)");
				if(value instanceof TIMESTAMP) {
					log.debug("database column value is of type oracle.sql.timestamp");
					
					TIMESTAMP timestamp = (TIMESTAMP)value;
					return timestamp.timestampValue();
				} else {
					log.error("unsupported value class: {}", value.getClass().getCanonicalName());
					return null;
				}
			} else {
				return value;
			}
		} else if(columnInfo.getType() == Type.GEOMETRY) {
			String typeName = columnInfo.getTypeName();
			if("SDO_GEOMETRY".equals(typeName)) {
				log.debug("database column value is of type sdo_geometry");
				
				if(value instanceof STRUCT) {
					log.debug("database column value is of type struct");
					
					STRUCT struct = (STRUCT)value;
					log.debug("struct object: " + struct);
					
					Geometry geom = converter.toGeometry(struct, null);
					log.debug("geom object: " + geom);
					
					return new WKBGeometry(WKBWriter.write(geom));
				} else {
					log.error("unsupported value class: {}", value.getClass().getCanonicalName());
					return null;
				}
			} else if("ST_GEOMETRY".equals(typeName)) {
				log.debug("database column value is of type st_geometry");
				
				if(value instanceof Blob) {
					Blob blob = (Blob)value;
					long blobLength = blob.length();
					if(blobLength > Integer.MAX_VALUE) {
						log.error("blob value too large: {}", blobLength);
						return null;
					} if(blobLength == 0) { // known to be returned by SDE.ST_ASBINARY on empty geometries
						log.error("empty blob");
						return null;
					} else {
						return new WKBGeometry(blob.getBytes(1l, (int)blobLength));
					}
				} else {
					log.error("unsupported value class: {}", value.getClass().getCanonicalName());
					return null;
				}
			} else {
				log.error("unsupported geometry type: {}", typeName);
				return null;
			}
		} else {
			if(value instanceof Clob) {
				Clob clob = (Clob)value;
				if(clob.length() > Integer.MAX_VALUE) {
					log.error("clob value too large: {}", clob.length());
					return null;
				}
				
				return IOUtils.toString(clob.getCharacterStream());
			}
			
			return value;
		}
	}
}

@@ -1482,18 +1482,75 @@
 ;%0D%0A%09%09%7D%0D%0A
-%09%09
+%0D%0A%09%09String className = value.getClass().getCanonicalName();
 %0D%0A%09%09log.
@@ -1583,16 +1583,39 @@
 ue: 
-%22 + valu
+%7B%7D, class: %7B%7D%22, value, classNam
 e);%0D
@@ -1657,173 +1657,22 @@
 ype.
-DATE) %7B%0D%0A%09%09%09String typeName = columnInfo.getTypeName();%0D%0A%09%09%09if(%22TIMESTAMP(6)%22.equals(typeName)) %7B%0D%0A%09%09%09%09log.debug(%22database column value is of type timestamp(6)%22);%0D%0A%09
+TIMESTAMP) %7B%0D%0A
 %09%09%09i
@@ -1700,33 +1700,32 @@
 MESTAMP) %7B%0D%0A%09%09%09%09
-%09
 log.debug(%22datab
@@ -1767,30 +1767,24 @@
 sql.
-timestamp%22);%0D%0A%09%09%09%09%09
+TIMESTAMP%22);%0D%0A
 %0D%0A
-%09
 %09%09%09%09
@@ -1824,25 +1824,24 @@
 value;%0D%0A%09%09%09%09
-%09
 return times
@@ -1860,25 +1860,24 @@
 mpValue();%0D%0A
-%09
 %09%09%09%7D else %7B%0D
@@ -1873,33 +1873,32 @@
 %09%09%7D else %7B%0D%0A%09%09%09%09
-%09
 log.error(%22unsup
@@ -1896,32 +1896,42 @@
 or(%22unsupported 
+timestamp 
 value class: %7B%7D%22
@@ -1936,48 +1936,21 @@
 %7D%22, 
-value.getClass().getCanonical
+class
 Name
-()
 );%0D%0A
-%09
 %09%09%09%09
@@ -1967,47 +1967,8 @@
 l;%0D%0A
-%09%09%09%09%7D%0D%0A%09%09%09%7D else %7B%0D%0A%09%09%09%09return value;%0D%0A
 %09%09%09%7D
@@ -2568,32 +2568,41 @@
 or(%22unsupported 
+geometry 
 value class: %7B%7D%22
@@ -2607,43 +2607,17 @@
 %7D%22, 
-value.getClass().getCanonical
+class
 Name
-()
 );%0D%0A
@@ -3261,16 +3261,21 @@
 pported 
+blob 
 value cl
@@ -3288,43 +3288,17 @@
 %7D%22, 
-value.getClass().getCanonical
+class
 Name
-()
 );%0D%0A

Fix Oracle timestamp conversion