package daruma.geometry;

import daruma.geometry.CoordinateSystem;
import daruma.geometry.CoordinateSystemTransformation;
import daruma.geometry.CoordinateSystemTransformationDictionary;

import daruma.util.Pair;
import java.util.Map;
import java.util.List;
import java.util.LinkedList;

import daruma.util.EqualChecker;
import daruma.util.LogWriter;

public class TransformationContext
{
    public static enum TransformationType { NoConv, Conv, Selective };

    private TransformationType transformationType;
    private CoordinateSystem targetCoord;
    private CoordinateSystemTransformationDictionary
				coordTransDictionary = null;

    public TransformationContext()
    {
	this.transformationType = TransformationType.Conv;
	this.targetCoord = null;
    }

    public TransformationContext( TransformationType transformationType,
				  CoordinateSystem targetCoord )
    {
	this.transformationType = transformationType;
	this.targetCoord = targetCoord;
    }

    public TransformationType getTransformationType()
    {
	return this.transformationType;
    }

    public void setTransformationType( TransformationType type )
    {
	this.transformationType = type;
    }

    public CoordinateSystem getTargetCoordinateSystem()
    {
	return this.targetCoord;
    }

    public void setCoordTransDictionary
		( CoordinateSystemTransformationDictionary dictionary )
    {
	this.coordTransDictionary = dictionary;
    }

    public DrmGeometry transform( DrmGeometry geom )
	    throws TransformationException
    {
	// if target's srsName and object's srsName is same,
	// no need to transform
	if ( this.targetCoord == null )
	{
	    if ( geom.getSrsName() == null )
	    {
		return geom;
	    }
	}
	else
	{
	    if ( EqualChecker.equals( this.targetCoord.getSrsName(),
				      geom.getSrsName() ) )
	    {
		return geom;
	    }
	}


	List<CoordinateSystemTransformation>
	    transPath = search( this.coordTransDictionary,
				new CoordinateSystem( geom.getSrsName() ),
				this.targetCoord );

	if ( transPath == null )
	{
	    throw new TransformationException( "no route to transform" );
	}

	for ( CoordinateSystemTransformation trans : transPath )
	{
	    geom = geom.transform( trans );
	}

	return geom;
    }


    /**
     * returns transformation path from sourceCS to targetCS.
     *
     * return null if no route founds.
     * targetCS must not equals to sourceCS,
     */
    public
    static List<CoordinateSystemTransformation>
		search( CoordinateSystemTransformationDictionary dictionary,
			CoordinateSystem sourceCS,
			CoordinateSystem targetCS )
    {
	assert ! sourceCS.equals( targetCS );

	LogWriter.qwrite( "DEBUG",
			  "Entering search, [",
			  sourceCS.getSrsName(),
			  "] to [", targetCS.getSrsName(), "]" );

	List<CoordinateSystemTransformation>
	    ret = new LinkedList<CoordinateSystemTransformation>();

	CoordinateSystemTransformation trans;
	trans = dictionary.get( sourceCS, targetCS );

	if ( trans != null )
	{
	    LogWriter.qwrite( "DEBUG", "route found!!" );

	    ret.add( trans );

	    return ret;
	}


	Map<CoordinateSystem,CoordinateSystemTransformation> nextCandicates;
	nextCandicates = dictionary.getTransFrom( sourceCS );

	if ( nextCandicates == null )
	{
	    LogWriter.qwrite( "DEBUG", "no candidates!!" );
	    return null;
	}

	for ( Map.Entry<CoordinateSystem, CoordinateSystemTransformation> c
		  : nextCandicates.entrySet() )
	{
	    CoordinateSystem nextTarget = c.getKey();
	    CoordinateSystemTransformation tr = c.getValue();

	    CoordinateSystemTransformationDictionary dic = dictionary.clone();
	    dic.remove( tr );

	    List<CoordinateSystemTransformation>
		path = search( dic, nextTarget, targetCS );

	    if ( path != null )
	    {
		LogWriter.qwrite( "DEBUG", "path found!!" );

		ret.add( tr );
		ret.addAll( path );

		return ret;
	    }
	}

	LogWriter.qwrite( "DEBUG", "path not found!!" );
	return null;
    }
}
