package daruma.geometry;

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

import daruma.util.Pair;
import java.util.Map;
import java.util.HashMap;

public class CoordinateSystemTransformationDictionary
{
    private Map<Pair<CoordinateSystem, CoordinateSystem>,
		CoordinateSystemTransformation> transMap
		= new HashMap<Pair<CoordinateSystem, CoordinateSystem>,
		      CoordinateSystemTransformation>();

    private Map<CoordinateSystem,
		Map<CoordinateSystem,CoordinateSystemTransformation>> targetMap
		= new HashMap<CoordinateSystem,
			      Map<CoordinateSystem,
				  CoordinateSystemTransformation>>();

    private Map<CoordinateSystem,
		Map<CoordinateSystem,CoordinateSystemTransformation>> sourceMap
		= new HashMap<CoordinateSystem,
			      Map<CoordinateSystem,
				  CoordinateSystemTransformation>>();

    public CoordinateSystemTransformationDictionary()
    {
    }

    public void put( CoordinateSystemTransformation trans )
    {
	final CoordinateSystem  sourceCS = trans.getSourceCS();
	final CoordinateSystem  targetCS = trans.getTargetCS();


	//
	// update transMap
	//
	this.transMap.put( new Pair<CoordinateSystem, CoordinateSystem>
			       ( sourceCS , targetCS ),
			   trans );

	
	//
	// update targetMap
	//
	Map<CoordinateSystem, CoordinateSystemTransformation> t;
	t = this.targetMap.get( targetCS );
	if ( t == null )
	{
	    t = new HashMap<CoordinateSystem,
			    CoordinateSystemTransformation>();

	    this.targetMap.put( targetCS , t );
	}
	t.put( sourceCS , trans );


	//
	// update sourceMap
	//
	Map<CoordinateSystem, CoordinateSystemTransformation> s;
	s = this.sourceMap.get( sourceCS );
	if ( s == null )
	{
	    s = new HashMap<CoordinateSystem,
			    CoordinateSystemTransformation>();

	    this.sourceMap.put( sourceCS , s );
	}
	s.put( targetCS , trans );
    }

    public CoordinateSystemTransformation get( CoordinateSystem sourceCS ,
					       CoordinateSystem targetCS )
    {
	return this.transMap.get( new Pair<CoordinateSystem, CoordinateSystem>
				      ( sourceCS , targetCS ) );
    }

    public Map<CoordinateSystem,CoordinateSystemTransformation>
		getTransTo( CoordinateSystem targetCS )
    {
	return this.targetMap.get( targetCS );
    }

    public Map<CoordinateSystem,CoordinateSystemTransformation>
		getTransFrom( CoordinateSystem sourceCS )
    {
	return this.sourceMap.get( sourceCS );
    }

    public CoordinateSystemTransformation
		remove( CoordinateSystemTransformation trans )
    {
	final CoordinateSystem  sourceCS = trans.getSourceCS();
	final CoordinateSystem  targetCS = trans.getTargetCS();

	CoordinateSystemTransformation removed;

	//
	// update transMap
	//
	removed = this.transMap.remove
			( new Pair<CoordinateSystem, CoordinateSystem>
			  ( sourceCS , targetCS ) );

	if ( removed != null)
	{
	    return removed;
	}


	//
	// update sourceMap
	//
	Map<CoordinateSystem, CoordinateSystemTransformation> s;
	s = this.sourceMap.get( sourceCS );
	assert s != null;

	s.remove( targetCS );

	if ( s.isEmpty() )
	{
	    this.sourceMap.remove( sourceCS );
	}


	//
	// update targetMap
	//
	Map<CoordinateSystem, CoordinateSystemTransformation> t;
	t = this.targetMap.get( targetCS );
	assert t != null;

	t.remove( sourceCS );

	if ( t.isEmpty() )
	{
	    this.targetMap.remove( targetCS );
	}


	return removed;
    }


    /**
     * create shallow copy
     */
    public CoordinateSystemTransformationDictionary clone()
    {
	CoordinateSystemTransformationDictionary
	    ret = new CoordinateSystemTransformationDictionary();

	for ( CoordinateSystemTransformation trans
		  : transMap.values() )
	{
	    ret.put( trans );
	}

	return ret;
    }

    public Map<Pair<CoordinateSystem, CoordinateSystem>,
	       CoordinateSystemTransformation> getAllTransform()
    {
	return transMap;
    }


    public void debugPrint( java.io.PrintStream out )
    {
	CoordinateSystemTransformationDictionary
	    ret = new CoordinateSystemTransformationDictionary();

	for ( CoordinateSystemTransformation trans : transMap.values() )
	{
	    out.println( "[" + trans.getSourceCS().getSrsName() + "] -> "
		       + "[" + trans.getTargetCS().getSrsName() + "]" );
	}

	for ( Map.Entry<CoordinateSystem,
			Map<CoordinateSystem,CoordinateSystemTransformation>> s
		  : sourceMap.entrySet() )
	{
	    out.println( "sourceMap[" + s.getKey().getSrsName() + "]" );

	    for ( CoordinateSystem t : s.getValue().keySet() )
	    {
		out.println( " -> [" + t.getSrsName() + "]" );
	    }
	}

	for ( Map.Entry<CoordinateSystem,
			Map<CoordinateSystem,CoordinateSystemTransformation>> t
		  : targetMap.entrySet() )
	{
	    out.println( "targetMap[" + t.getKey().getSrsName() + "]" );

	    for ( CoordinateSystem s : t.getValue().keySet() )
	    {
		out.println( " <- [" + s.getSrsName() + "]" );
	    }
	}
    }
}
