package daruma.geometry;

import daruma.geometry.CoordinateSystem;
import daruma.geometry.DrmPoint;

import daruma.geometry.TransformationException;
import daruma.geometry.TransformationErrorException;

import daruma.global_switch.ImplementationSwitches;

import daruma.util.LogWriter;

public class AffineCoordinateSystemTransformation
    extends CoordinateSystemTransformation
{
    private double[][] matrix;

    public AffineCoordinateSystemTransformation( String identifier,
						 CoordinateSystem sourceCS,
						 CoordinateSystem targetCS,
						 long sourceDimension,
						 long targetDimension,
						 double[][] matrix,
						 String transErrorDescription )
    {
	super( identifier, sourceCS, targetCS,
	       sourceDimension, targetDimension, transErrorDescription );

	this.matrix = matrix.clone();
    }

    public double[][] getMatrix()
    {
	return this.matrix;
    }

    @Override
    public String getTransformName()
    {
	return "AffineTransformation";
    }

    @Override
    public String encode()
    {
	StringBuilder buf = new StringBuilder();

	for ( int i = 0 ; i < this.matrix.length ; ++ i )
	{
	    for ( int j = 0 ; j < this.matrix[i].length ; ++ j )
	    {
		if ( i != 0 || j != 0 )
		{
		    buf.append( " " );
		}

		buf.append( this.matrix[i][j] );
	    }
	}

	return buf.toString();
    }


    @Override
    public DrmPoint transform( DrmPoint point ) throws TransformationException
    {
	int dim = point.getDimension();

	LogWriter.qwrite( "DEBUG",
			  "conv to lower = ",
			  ImplementationSwitches.instance()
			   .getAllowConversionToLowerDimension() );
	LogWriter.qwrite( "DEBUG",
			  "conv to upper = ",
			  ImplementationSwitches.instance()
			   .getAllowConversionToUpperDimension() );
	LogWriter.qwrite( "DEBUG",
			  "dim = ", dim );
	LogWriter.qwrite( "DEBUG",
			  "matrix.length = ", matrix.length );


	//
	// check strict/non-strict coordinate transform
	//
	if ( (! ImplementationSwitches.instance()
	        .getAllowConversionToLowerDimension()
	      && dim > this.matrix.length)
	     || (! ImplementationSwitches.instance()
		   .getAllowConversionToUpperDimension()
		 && dim < this.matrix.length) )
	{
	    throw new TransformationException( "dimension mismatch" );
	}


	if ( dim > 3 )
	{
	    throw new TransformationException( "source dimension too high" );
	}
	else if ( this.matrix.length > 3 )
	{
	    throw new TransformationException( "target dimension too high" );
	}

	double[] org = new double[this.matrix.length + 1];
	org[0] = point.getX();
	org[1] = point.getY();
	if ( this.matrix.length >= 3 )
	{
	    if ( dim >= 3 )
	    {
		org[2] = point.getZ();
	    }
	    else
	    {
		org[2] = 0.0;
	    }
	    org[3] = 1.0;
	}
	else
	{
	    org[2] = 1.0;
	}

	double[] val = new double[this.matrix.length];

	for ( int axis = 0 ; axis < this.matrix.length ; ++ axis )
	{
	    val[axis] = 0.0;

	    for ( int i = 0 ; i < this.matrix.length + 1 ; ++ i )
	    {
		val[axis] += this.matrix[axis][i] * org[i];

		LogWriter.qwrite( "DEBUG",
				  "ret[", axis, "] += ",
				  this.matrix[axis][i], " * ", org[i] );
	    }
	}

	DrmPoint ret;

	if ( this.matrix.length >= 3 )
	{
	    ret = new DrmPoint( val[0], val[1], val[2] );
	}
	else
	{
	    ret = new DrmPoint( val[0], val[1] );
	}
	ret.setSrsName( this.getTargetCS().getSrsName() );

	return ret;
    }
}
