package com.ctc.wstx.util;

import java.util.ArrayList;
import java.util.Iterator;

/**
 * Data container similar {@link java.util.List} (from storage perspective),
 * but that can be used in multiple ways. For some uses it acts more like
 * type-safe String list/vector; for others as order associative list of
 * String-to-String mappings.
 */
public final class StringVector
{
    private String[] mStrings;

    private int mSize;

    /*
    ///////////////////////////////////////////////////////
    // Life-cycle:
    ///////////////////////////////////////////////////////
     */

    public StringVector(int initialCount) {
        mStrings = new String[initialCount];
    }

    // 12-Jan-2006, TSa: Not used at this point... can be removed maybe?
    /*
    private StringVector(String[] str, int size) {
        mStrings = str;
        mSize = size;
    }

    public StringVector makeCopy(int extraSize) {
        int newSize = mSize + extraSize;
        String[] strs = new String[newSize];
        if (mSize > 0) {
            System.arraycopy(mStrings, 0, strs, 0, mSize);
        }
        return new StringVector(strs, mSize);
    }
    */

    /*
    ///////////////////////////////////////////////////////
    // Basic accessors
    ///////////////////////////////////////////////////////
     */

    public int size() { return mSize; }

    public boolean isEmpty() { return mSize == 0; }

    public String getString(int index) {
        if (index < 0 || index >= mSize) {
            throw new IllegalArgumentException("Index "+index+" out of valid range; current size: "+mSize+".");
        }
        return mStrings[index];
    }

    public String getLastString() {
        if (mSize < 1) {
            throw new IllegalStateException("getLastString() called on empty StringVector.");
        }
        return mStrings[mSize-1];
    }

    public String[] getInternalArray() {
        return mStrings;
    }

    public String[] asArray() {
        String[] strs = new String[mSize];
        System.arraycopy(mStrings, 0, strs, 0, mSize);
        return strs;
    }

    public boolean containsInterned(String value) {
        String[] str = mStrings;
        for (int i = 0, len = mSize; i < len; ++i) {
            if (str[i] == value) {
                return true;
            }
        }
        return false;
    }

    /*
    ///////////////////////////////////////////////////////
    // Mutators:
    ///////////////////////////////////////////////////////
     */

    public void addString(String str) {
        if (mSize == mStrings.length) {
            String[] old = mStrings;
            int oldSize = old.length;
            mStrings = new String[oldSize + (oldSize << 1)];
            System.arraycopy(old, 0, mStrings, 0, oldSize);
        }
        mStrings[mSize++] = str;
    }

    public void addStrings(String str1, String str2) {
        if ((mSize + 2) > mStrings.length) {
            String[] old = mStrings;
            int oldSize = old.length;
            mStrings = new String[oldSize + (oldSize << 1)];
            System.arraycopy(old, 0, mStrings, 0, oldSize);
        }
        mStrings[mSize] = str1;
        mStrings[mSize+1] = str2;
        mSize += 2;
    }

    // 12-Jan-2006, TSa: Not used at this point... can be removed maybe?
    /*
    public void addStrings(StringVector v)
    {
        int addSize = v.size();
        if ((mSize + addSize) > mStrings.length) {
            String[] old = mStrings;
            int newSize = old.length;
            newSize += (newSize >> 1);
            if (newSize < (mSize + addSize)) {
                newSize = mSize + addSize;
            }
            mStrings = new String[newSize];
            System.arraycopy(old, 0, mStrings, 0, mSize);
        }
        for (int i = 0; i < addSize; ++i) {
            mStrings[mSize + i] = v.getString(i);
        }
        mSize += addSize;
    }
    */

    public void setString(int index, String str) {
        mStrings[index] = str;
    }

    public void clear(boolean removeRefs) {
        if (removeRefs) {
            for (int i = 0, len = mSize; i < len; ++i) {
                mStrings[i] = null;
            }
        }
        mSize = 0;
    }

    public String removeLast() {
        String result = mStrings[--mSize];
        mStrings[mSize] = null;
        return result;
    }

    public void removeLast(int count) {
        while (--count >= 0) {
            mStrings[--mSize] = null;
        }
    }

    /*
    ///////////////////////////////////////////////////////
    // Specialized "map accessors":
    ///////////////////////////////////////////////////////
     */

    /**
     * Specialized access method; treats vector as a Map, with 2 Strings
     * per entry; first one being key, second value. Further, keys are
     * assumed to be canonicalized with passed in key (ie. either intern()ed,
     * or resolved from symbol table).
     * Starting from the
     * end (assuming even number of entries), tries to find an entry with
     * matching key, and if so, returns value.
     */
    public String findLastFromMap(String key) {
        int index = mSize;
        while ((index -= 2) >= 0) {
            if (mStrings[index] == key) {
                return mStrings[index+1];
            }
        }
        return null;
    }

    public String findLastNonInterned(String key)
    {
        int index = mSize;
        while ((index -= 2) >= 0) {
            String curr = mStrings[index];
            if (curr == key || (curr != null && curr.equals(key))) {
                return mStrings[index+1];
            }
        }
        return null;
    }

    public int findLastIndexNonInterned(String key) {
        int index = mSize;
        while ((index -= 2) >= 0) {
            String curr = mStrings[index];
            if (curr == key || (curr != null && curr.equals(key))) {
                return index;
            }
        }
        return -1;
    }

    public String findLastByValueNonInterned(String value) {
        for (int index = mSize-1; index > 0; index -= 2) {
            String currVal = mStrings[index];
            if (currVal == value || (currVal != null && currVal.equals(value))) {
                return mStrings[index-1];
            }
        }
        return null;
    }

    public int findLastIndexByValueNonInterned(String value) {
        for (int index = mSize-1; index > 0; index -= 2) {
            String currVal = mStrings[index];
            if (currVal == value || (currVal != null && currVal.equals(value))) {
                return index-1;
            }
        }
        return -1;
    }

    /*
      // Not needed any more
    public Iterator findAllByValueNonInterned(String value) {
        String first = null;
        ArrayList all = null;
        for (int index = mSize-1; index > 0; index -= 2) {
            String currVal = mStrings[index];
            if (currVal == value || (currVal != null && currVal.equals(value))) {
                if (first == null) {
                    first = mStrings[index-1];
                } else {
                    if (all == null) {
                        all = new ArrayList();
                        all.add(first);
                    }
                    all.add(mStrings[index-1]);
                }
            }
        }
        if (all != null) {
            return all.iterator();
        }
        if (first != null) {
            return new SingletonIterator(first);
        }
        return EmptyIterator.getInstance();
    }
    */

    /*
    ///////////////////////////////////////////////////////
    // Other methods
    ///////////////////////////////////////////////////////
     */

    public String toString() {
        StringBuffer sb = new StringBuffer(mSize * 16);
        sb.append("[(size = ");
        sb.append(mSize);
        sb.append(" ) ");
        for (int i = 0; i < mSize; ++i) {
            if (i > 0) {
                sb.append(", ");
            }
            sb.append('"');
            sb.append(mStrings[i]);
            sb.append('"');

            sb.append(" == ");
            sb.append(Integer.toHexString(System.identityHashCode(mStrings[i])));
        }
        sb.append(']');
        return sb.toString();
    }
}
