package btools.util;

/**
 * TinyDenseLongMap implements the DenseLongMap interface
 * but actually is made for a medium count of non-dense keys
 * 
 * It's used as a replacement for DenseLongMap where we
 * have limited memory and far less keys than maykey
 *
 * @author ab
 */
public class TinyDenseLongMap extends DenseLongMap
{
  private long[][] al;
  private int[] pa;
  private int size = 0;
  private int _maxKeepExponent = 14; // the maximum exponent to keep the invalid arrays

  protected static final int MAXLISTS = 31; // enough for size Integer.MAX_VALUE

  public TinyDenseLongMap()
  {
    super();

    // pointer array
    pa = new int[MAXLISTS];

    // allocate key lists
    al = new long[MAXLISTS][];
    al[0] = new long[1]; // make the first array (the transient buffer)

    // same for the values
    vla = new byte[MAXLISTS][];
    vla[0] = new byte[1];
  }


  private byte[][] vla; // value list array

  private void fillReturnValue(byte[] rv, int idx, int p )
  {
    rv[0] = vla[idx][p];
    if ( rv.length == 2 )
    {
      vla[idx][p] = rv[1];
    }
  }

  @Override
  public void put( long id, int value )
  {
    byte[] rv = new byte[2];
    rv[1] = (byte)value;
    if ( contains( id, rv ) )
    {
      return;
    }

    vla[0][0] = (byte)value;
    _add( id );
  }


  /**
   * Get the byte for the given id
   * @param id the key to query
   * @return the object
   * @exception IllegalArgumentException if id is unknown
   */
  @Override
  public int getInt( long id )
  {
    byte[] rv = new byte[1];
    if ( contains( id, rv ) )
    {
      return rv[0];
    }
    return -1;
  }


  private boolean _add( long id )
  {
    if ( size == Integer.MAX_VALUE )
    {
      throw new IllegalArgumentException( "cannot grow beyond size Integer.MAX_VALUE" );
    }

    // put the new entry in the first array
    al[0][0] = id;

    // determine the first empty array
    int bp = size++; // treat size as bitpattern
    int idx = 1;
    int n = 1;

    pa[0] = 1;
    pa[1] = 1;

    while ( (bp&1) == 1 )
    {
      bp >>= 1;
      pa[idx++] = n;
      n <<= 1;
    }

    // create it if not existant
    if ( al[idx] == null )
    {
      al[idx] = new long[n];
      vla[idx] = new byte[n];
    }

    // now merge the contents of arrays 0...idx-1 into idx
    while ( n > 0 )
    {
      long maxId = 0;
      int maxIdx = -1;

      for ( int i=0; i<idx; i++ )
      {
        int p = pa[i];
        if ( p > 0 )
        {
          long currentId = al[i][p-1];
          if ( maxIdx < 0 || currentId > maxId )
          {
            maxIdx = i;
            maxId = currentId;
          }
        }
      }

      // current maximum found, copy to target array
      if ( n < al[idx].length && maxId == al[idx][n] )
      {
        throw new IllegalArgumentException( "duplicate key found in late check: " + maxId );
      }
      --n;
      al[idx][n] = maxId;
      vla[idx][n] = vla[maxIdx][pa[maxIdx]-1];

      --pa[maxIdx];
    }

    // de-allocate empty arrays of a certain size (fix at 64kByte)
    while ( idx-- > _maxKeepExponent )
    {
      al[idx] = null;
      vla[idx] = null;
    }

    return false;
  }


  private boolean contains( long id, byte[] rv )
  {
    // determine the first empty array
    int bp = size; // treat size as bitpattern
    int idx = 1;

    while ( bp != 0 )
    {
      if ( (bp&1) == 1 )
      {
        // array at idx is valid, check
        if ( contains( idx, id, rv ) )
        {
          return true;
        }
      }
      idx++;
      bp >>= 1;
    }
    return false;
  }


  // does sorted array "a" contain "id" ?
  private boolean contains( int idx, long id, byte[] rv )
  {
    long[] a = al[idx];
    int offset = a.length;
    int n = 0;

    while ( (offset >>= 1) > 0 )
    {
      int nn = n + offset;
      if ( a[nn] <= id )
      {
        n = nn;
      }
    }
    if ( a[n] == id )
    {
      if ( rv != null )
      {
        fillReturnValue( rv, idx, n );
      }
      return true;
    }
    return false;
  }

}
