package leaf.data;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

public class OrderedMap<KeyClass extends Object> implements Iterable<KeyClass>
{
	private static final long serialVersionUID = 1L;

	private Vector<KeyClass> order = new Vector<KeyClass>();

	private HashMap<KeyClass, List<Object>> map = new HashMap<KeyClass, List<Object>>();

	public OrderedMap()
	{
	}

	public OrderedMap(Collection<KeyClass> allPrestadores)
	{
		Iterator<KeyClass> iterator = allPrestadores.iterator();
		while( iterator.hasNext() )
		{
			KeyClass value = iterator.next();
			this.putLast( value, value );
		}
	}

	public List<Object> getRow( int row )
	{
		return map.get( order.get( row ) );
	}

	public List<Object> getValues( KeyClass key )
	{
		return map.get( key );
	}

	public int rows()
	{
		return order.size();
	}

	public KeyClass getKeyForValue( Object value )
	{
		for( KeyClass key : map.keySet() )
		{
			List<Object> values = map.get( key );
			if( value.equals( values ) )
			{
				return key;
			}
			else
			{
				for( Object currentValue : values )
				{
					if( currentValue.equals( value ) )
					{
						return key;
					}
				}
			}
		}
		return null;
	}

	/**
	 * returns the length of the biggest line
	 * 
	 * @return
	 */
	public int columns()
	{
		int result = 0;
		for( KeyClass key : order )
		{
			result = map.get( key ).size() > result ? map.get( key ).size() : result;
		}
		return result;
	}

	public List<Object> getColumn( int i )
	{
		List<Object> result = new ArrayList<Object>();
		if( order != null && order.size() > 0 )
		{
			for( KeyClass key : order )
			{
				List<Object> row = map.get( key );
				if( row != null && row.size() > 0 )
				{
					result.add( row.get( 0 ) );
				}
				else
				{
					row.add( null );
				}
			}
		}
		return result;
	}

	public KeyClass getFirst()
	{
		return order.isEmpty() ? null : order.get( 0 );
	}

	public Iterator<KeyClass> iterator()
	{
		return order.iterator();
	}

	public boolean containsKey( KeyClass key )
	{
		return map.containsKey( key );
	}

	public Object getValueAt( int row, int column )
	{
		Object result = null;
		if( row < order.size() )
		{
			List<Object> line = map.get( order.get( row ) );
			if( column < line.size() )
			{
				result = line.get( column );
			}
		}
		return result;
	}

	/**
	 * Adds arguments to the end of the row (on given order)
	 * 
	 * @param key
	 * @param values
	 */
	public void putLast( KeyClass key, Object... values )
	{
		if( values != null )
		{
			for( Object currentValue : values )
			{
				putLast( key, currentValue );
			}
		}
	}

	/**
	 * Adds argument to the end of the row
	 * 
	 * @param key
	 * @param value
	 */
	public void putLast( KeyClass key, Object value )
	{
		List<Object> list;
		if( map.containsKey( key ) )
		{
			list = map.get( key );
		}
		else
		{
			list = new ArrayList<Object>();
			order.add( key );
		}
		list.add( value );
		map.put( key, list );
	}

	public List<Object> remove( Object key )
	{
		order.remove( key );
		return map.remove( key );
	}

	/**
	 * Orders by first column (rows compared as strings)
	 */
	public void order()
	{
		order( 0 );
	}

	public void order( int columnNumber )
	{
		order( new int[] {
			columnNumber
		}, 0, 0, order.size() - 1, order );
	}

	public void order( int[] colNumbers )
	{
		order( colNumbers, 0, 0, order.size() - 1, order );
	}

	private void order( int[] colNumbers, int currentColumnIndex, int fromLineNumber, int toLineNumber, List<KeyClass> order )
	{
		if( colNumbers != null && currentColumnIndex >= 0 && fromLineNumber < toLineNumber && toLineNumber < order.size() && currentColumnIndex < colNumbers.length )
		{
			int columnNumber = colNumbers[currentColumnIndex];
			if( order != null && order.size() > 0 )
			{
				List<Pair<String, KeyClass>> sortedList = new ArrayList<Pair<String, KeyClass>>();
				for( int i = fromLineNumber; i < order.size() && i < (toLineNumber + 1); ++i )
				{
					KeyClass key = order.get( i );
					List<Object> row = map.get( key );
					String value = "";
					if( row != null && row.size() > columnNumber && row.get( columnNumber ) != null )
					{
						value = row.get( columnNumber ).toString();
					}
					sortedList.add( new Pair<String, KeyClass>( value, key ) );
				}
				Collections.sort( sortedList );
				List<Pair<String, KeyClass>> equalEntries = new ArrayList<Pair<String, KeyClass>>();
				for( int i = 0; i < sortedList.size(); ++i )
				{
					Pair<String, KeyClass> entry = sortedList.get( i );
					if( equalEntries.isEmpty() && i < (sortedList.size() - 1) )
					{
						equalEntries.add( entry );
					}
					else
					{
						Pair<String, KeyClass> previousEntry = equalEntries.get( 0 );
						if( previousEntry.getCar().equals( entry.getCar() ) )
						{
							if( i < (sortedList.size() - 1) )
							{
								equalEntries.add( entry );
								continue;
							}
							else
							{
								equalEntries.add( entry );
								++i;
							}
						}
						if( equalEntries.size() > 1 )
						{
							List<KeyClass> toSubOrder = new Vector<KeyClass>();
							List<String> DEBUGLIST = new Vector<String>();
							for( Pair<String, KeyClass> pair : equalEntries )
							{
								toSubOrder.add( pair.getCdr() );
								DEBUGLIST.add( pair.getCar() );
							}
							order( colNumbers, currentColumnIndex + 1, 0, toSubOrder.size() - 1, toSubOrder );
							for( int j = 0; j < toSubOrder.size(); ++j )
							{
								sortedList.set( i - toSubOrder.size() + j, new Pair<String, KeyClass>( "", toSubOrder.get( j ) ) );
							}
						}
						equalEntries.clear();
						if( i < (sortedList.size() - 1) )
						{
							equalEntries.add( entry );
						}
					}
				}
				for( int i = 0; i < sortedList.size(); ++i )
				{
					Pair<String, KeyClass> value = sortedList.get( i );
					order.set( i, value.getCdr() );
				}
			}
		}
	}

	public void addRow( KeyClass key, List<Object> grupo )
	{
		if( key != null && grupo != null )
		{
			order.add( key );
			map.put( key, grupo );
		}
	}

	public void clear()
	{
		order.clear();
		map.clear();
	}

	public void deleteRow( int row )
	{
		if( row < order.size() )
		{
			KeyClass key = order.get( row );
			order.remove( row );
			map.remove( key );
		}
	}

	public KeyClass getKeyForRow( int row )
	{
		KeyClass result = null;
		if( row < order.size() )
		{
			result = order.get( row );
		}
		return result;
	}

	public void setValueAt( int row, int col, Object obj )
	{
		if( row < order.size() )
		{
			List<Object> line = map.get( order.get( row ) );
			if( col < line.size() )
			{
				line.add( col, obj );
			}
		}
	}

	public Object getFirstValue( KeyClass key )
	{
		return getValue( key, 0 );
	}

	public Object getValue( KeyClass key, int i )
	{
		Object result = null;
		if( key != null && order.contains( key ) )
		{
			List<Object> row = map.get( key );
			if( row != null && row.size() > i )
			{
				result = row.get( i );
			}
		}
		return result;
	}

}
