package edu.cmu.cs;

import edu.cmu.cs.glacier.qual.Immutable;

/**
 * Object that contains lists of keys and values that hash to the same bucket.
 */
@Immutable
final class HashBucket {
    /** List of keys of this collision list. */
    final @Immutable Object @Immutable[] keys;
    /** List of values of this collision list. */
    final @Immutable Object @Immutable [] values;

    /**
     * Constructor.
     *
     * @param keys key array
     * @param values value array
     */
    HashBucket(@Immutable Object @Immutable[] keys, @Immutable Object @Immutable[] values) {
        this.keys = keys;
        this.values = values;
    }

    HashBucket(@Immutable Object k, @Immutable Object v) {
        this.keys = new Object @Immutable[] { k };
        this.values = new Object @Immutable[] { v };
    }

    HashBucket delete(final @Immutable Object k) {
        for(int i = 0; i < keys.length; i++) {
            if(k.equals(keys[i])) {
                if (keys.length == 1) {
                    // There's nothing left.
                    return null;
                }

                int newSize = keys.length - 1;
                final Object[] ks = new Object[newSize];
                System.arraycopy(keys, 0, ks, 0, i);
                System.arraycopy(keys, i + 1, ks, i, newSize - i);
                final Object[] vs = new Object[newSize];
                System.arraycopy(values, 0, vs, 0, i);
                System.arraycopy(values, i + 1, vs, i, newSize - i);
                return new HashBucket(ks.clone(), vs.clone());
            }
        }
        return this;
    }


    HashBucket put(final @Immutable Object k, final @Immutable Object v) {
        // replace or merge
        for(int i = 0; i < keys.length; i++) {
            if(k.equals(keys[i])) {
                // replace value
                // TODO: finish this implementation. Returning "this" is bogus.
                return this;
            }
        }

        return new HashBucket(Array.add(keys, k).clone(), Array.add(values, v).clone());
    }


    Object get(final Object k) {
        for(int i = 0; i < keys.length; i++)
            if(k.equals(keys[i])) return values[i];
        return null;
    }

    boolean contains(final Object k)
    {
        for(int i = 0; i < keys.length; i++)
            if(k.equals(keys[i])) return true;
        return false;
    }

    boolean verify() {
        for(int i = 1; i < keys.length; i++) {
            for(int j = i; j-- > 0;) {
                if(keys[i].equals(keys[j])) return false;
            }
        }

        return true;
    }

    int size() {
        return keys.length;
    }

    Map copyIntoMap(Map m) {
        for (int i = 0; i < keys.length; i++) {
            m = m.put(keys[i], values[i]);
        }

        return m;
    }

    StringBuilder toDebugString(final StringBuilder sb, final String ind) {
        sb.append(ind).append("(\n");
        final int kl = keys.length;
        for(int k = 0; k < kl; k++) {
            sb.append(ind).append("      ").append(keys[k]).append(" => ").append(values[k]).append('\n');
        }
        sb.append(ind).append("      )");
        return sb;
    }


    StringBuilder toString(final StringBuilder sb) {
        final int kl = keys.length;
        for(int i = 0; i < kl; i++) {
            sb.append(keys[i]).append(" => ").append(values[i]);
            if (i < kl - 1) {
                sb.append(", ");
            }
        }
        return sb;
    }

}
