package daruma.wfs;

import daruma.xml.handler.XSAXDefaultHandler;
import daruma.xml.handler.XSAXDOMCreateHandler;
import daruma.xml.URI;
import daruma.xml.util.ElementUtil;
import daruma.xml.util.XMLParseErrorException;
import daruma.util.ISO8601DateFormat;
import daruma.util.LogWriter;

import daruma.storage_manager.StorageManager;
import daruma.storage_manager.StorageException;

import daruma.geometry.CoordinateSystem;
import daruma.geometry.CoordinateSystemTransformation;
import daruma.geometry.AffineCoordinateSystemTransformation;


import org.xml.sax.XMLReader;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import org.w3c.dom.Element;

import java.io.PrintWriter;
import java.io.OutputStream;
import java.util.Date;

import java.util.List;
import java.util.ArrayList;

import daruma.xml.Lexicon;


// XXX: This class is test a version class only for development.
//      Specification and code will be changed.

public class RegisterCoordinateSystemTransformationHandler extends XSAXDOMCreateHandler
{
    private StorageManager	storage;
    private Date		startTime;
    private Date		endTime;
    private boolean		errorOccured;
    private List<String>	errorMessages;

    public RegisterCoordinateSystemTransformationHandler( OutputStream  out ,
							  XMLReader  parser ,
							  boolean  isTopLevelHandler ,
							  StorageManager  storage )
	throws SAXException
    {
	super( out , parser , isTopLevelHandler );
	super.setInhibitEndPrefixBeforeEndDocument( true );

	this.storage = storage;

	this.errorOccured = false;
	this.errorMessages = new ArrayList<String>();

	this.endTime = null;
	try
	{
	    this.startTime = this.storage.getCurrentTime();
	}
	catch( StorageException e )
	{
	    this.throwError( new SAXParseException( e.getMessage() ,
						    super.getLocator() ,
						    e ) );
	}
    }

    public void xEndDocument() throws SAXException
    {
	//
	// parse request
	//
	Element request = super.getDocumentElement();

/*
  <misp:Transformation>
    <misp:identifier>Robot1-to-Room1</misp:identifier>
    <misp:sourceCS>Robot1</misp:sourceCS>
    <misp:targetCS>Room1</misp:targetCS>

    <misp:transformationMethod>
      <misp:AffineTransformation>
	<misp:sourceDimensions>2</misp:sourceDimensions>
	<misp:targetDimensions>2</misp:targetDimensions>
	<misp:parameter>
	  1 0 1
	  0 1 0
	</misp:parameter>
      </misp:AffineTransformation>
    </misp:transformationMethod>

  </misp:Transformation>
*/

	List<Element> transformationElements = ElementUtil.getChildElements( request );

	for( Element transElement : transformationElements )
	{
	    if ( ! transElement.getLocalName().equals( "Transformation" )
	      || ! transElement.getNamespaceURI().equals( URI.MISP ) )
	    {
		this.throwError( new SAXParseException
				 ( "Transformation in namespace "
				   + URI.MISP + " expected, found "
				   + transElement.getLocalName()
				   + " in namespace "
				   + transElement.getNamespaceURI()
				   + "." ,
				   super.getLocator() ) );
	    }



	    // XXX
	    // XXX: no error checks for parsing
	    // XXX

	    List<Element> children = ElementUtil.getChildElements( transElement );

	    if ( children.size() != 4 )
	    {
		this.throwError( new SAXParseException
				 ( "number of child elements in "
				   + transElement
				   + " unmached, expected was 4." ,
				   super.getLocator() ) );
	    }


	    String identifier = children.get(0).getTextContent();
	    String sourceCS   = children.get(1).getTextContent();
	    String targetCS   = children.get(2).getTextContent();

	    LogWriter.qwrite( "DEBUG", "identifier = [", identifier, "]" );
	    LogWriter.qwrite( "DEBUG", "sourceCS = [", sourceCS, "]" );
	    LogWriter.qwrite( "DEBUG", "targetCS = [", targetCS, "]" );


	    List<Element> methodChildren = ElementUtil.getChildElements( children.get(3) );

	    if ( methodChildren.size() != 1 )
	    {
		this.throwError( new SAXParseException
				 ( "number of child elements in " + children.get(3)
				   + " unmached, expected was 1." ,
				   super.getLocator() ) );
	    }

	    Element affineElement = methodChildren.get(0);
	    // XXX: should check affineElement == misp:AffineTransformation

	    List<Element> affineChildren = ElementUtil.getChildElements( affineElement );

	    if ( affineChildren.size() != 3 )
	    {
		this.throwError( new SAXParseException
				 ( "number of child elements in " + affineElement
				   + " unmached, expected was 3." ,
				   super.getLocator() ) );
	    }


	    String sourceDimensionsString = affineChildren.get(0).getTextContent();
	    String targetDimensionsString = affineChildren.get(1).getTextContent();
	    String parameterString = affineChildren.get(2).getTextContent();

	    int sourceDimensions = -1;
	    int targetDimensions = -1;

	    try
	    {
		sourceDimensions = new Integer(sourceDimensionsString);
	    }
	    catch( NumberFormatException e )
	    {
		this.throwError( new SAXParseException
				 ( "number format error in sourceDimensions,"
				   + " input = [" + sourceDimensionsString + "]",
				   super.getLocator() ) );
	    }

	    try
	    {
		targetDimensions = new Integer(targetDimensionsString);
	    }
	    catch( NumberFormatException e )
	    {
		this.throwError( new SAXParseException
				 ( "number format error in targetDimensions,"
				   + " input = [" + targetDimensionsString + "]",
				   super.getLocator() ) );
	    }

	    LogWriter.qwrite( "DEBUG", "sourceDimensions = ", sourceDimensions );
	    LogWriter.qwrite( "DEBUG", "targetDimensions = ", targetDimensions );
	    LogWriter.qwrite( "DEBUG", "parameterString = [", parameterString, "]" );

	    if ( sourceDimensions != targetDimensions )
	    {
		this.throwError( new SAXParseException
				 ( "unsupported the case sourceDimensions != targetDimensions",
				   super.getLocator() ) );
	    }


	    String[] params = parameterString.trim().split( "[ \t\n]+" );

	    if ( params.length != sourceDimensions * (sourceDimensions + 1) )
	    {
		this.throwError( new SAXParseException
				 ( "number of affine parameter unmatched, "
				   + "found = " + params.length
				   + ", expected was " + sourceDimensions * (sourceDimensions + 1),
				   super.getLocator() ) );
	    }

	    double[][] t = new double[sourceDimensions][sourceDimensions + 1];

	    for( int i = 0  ;  i < sourceDimensions  ;  ++ i )
	    {
		for( int j = 0  ;  j < sourceDimensions + 1  ;  ++ j )
		{
		    double v = 0.0;
		    final String valueString = params[i * (sourceDimensions + 1) + j];

		    try
		    {
			v = new Double( valueString );
		    }
		    catch( NumberFormatException e )
		    {
			this.throwError( new SAXParseException
					 ( "number format error in affine parameter,"
					   + " input = [" + valueString + "]",
					   super.getLocator() ) );
		    }

		    t[i][j] = v;

		    LogWriter.qwrite( "DEBUG",
				      "value[" , i , "][" , j , "] = [",
				      t[i][j], "]" );
		}
	    }

	    AffineCoordinateSystemTransformation
		trans = new AffineCoordinateSystemTransformation
			    ( identifier,
			      new CoordinateSystem( sourceCS ),
			      new CoordinateSystem( targetCS ),
			      sourceDimensions,
			      targetDimensions,
			      t );

	    try
	    {
		this.storage.registerCoordinateSystemTransformation( trans );
	    }
	    catch( StorageException e )
	    {
		this.throwError( new SAXParseException
				 ( "registration failed, " + e.getMessage() ,
				   super.getLocator() ,
				   e ) );
	    }
	}


	try
	{
	    this.endTime = this.storage.getCurrentTime();
	}
	catch( StorageException e )
	{
	    this.throwError( new SAXParseException( e.getMessage() ,
						    super.getLocator() ,
						    e ) );
	}

	printOutput();
    }

    private void printOutput() throws SAXException
    {
	PrintWriter out = super.getPrintWriter();

	String	status;

	if ( this.errorOccured )
	{
	    status = "FAILURE";
	}
	else
	{
	    status = "SUCCESS";
	}


	{
	    ResponseInfo responseInfo = super.getResponse();
	    Element topLevelElement = ElementUtil.genElementSimple
				      ( Lexicon.MispRegisterCoordinateSystemTransformationResponse,
					null, responseInfo.document, responseInfo.leaf, true );
	    responseInfo.setResponseToLeaf( topLevelElement );

	    responseInfo.addResponseStatusToLeaf( super.getMostRecentTransactionURI(),
						  this.startTime, this.endTime );

	    super.getResponse().outputHeaderPart(out);
	}

	out.println( "  <Status>" + status + "</Status>" );

	for ( String s : this.errorMessages )
	{
	    out.println( "  <Error>" );
	    out.println( "    <Message>" + s
			 + "</Message>" );
	    out.println( "  </Error>" );
	}

	out.flush();

	try
	{
	    this.getOutputStream().flush();
	}
	catch( java.io.IOException  e )
	{
	    throw new SAXException( e );
	}
    }

    private void setError( String  errorMessage )
    {
	this.errorOccured = true;
	this.errorMessages.add( errorMessage );
    }

    private void throwError( SAXParseException  e ) throws SAXException
    {
	this.setError( e.getMessage() );

	this.printOutput();

	throw e;
    }
}
