/*
*******************************************************************************
*   Copyright (C) 2011-2014, International Business Machines
*   Corporation and others.  All Rights Reserved.
*******************************************************************************
*   created on: 2011jan07
*   created by: Markus W. Scherer
*   ported from ICU4C ucharstriebuilder/.cpp
*/

package com.ibm.icu.util;

import java.nio.CharBuffer;

/**
 * Builder class for CharsTrie.
 *
 * <p>This class is not intended for public subclassing.
 *
 * @stable ICU 4.8
 * @author Markus W. Scherer
 */
public final class CharsTrieBuilder extends StringTrieBuilder {
    /**
     * Constructs an empty builder.
     * @stable ICU 4.8
     */
    public CharsTrieBuilder() {}

    /**
     * Adds a (string, value) pair.
     * The string must be unique.
     * The string contents will be copied; the builder does not keep
     * a reference to the input CharSequence.
     * @param s The input string.
     * @param value The value associated with this char sequence.
     * @return this
     * @stable ICU 4.8
     */
    public CharsTrieBuilder add(CharSequence s, int value) {
        addImpl(s, value);
        return this;
    }

    /**
     * Builds a CharsTrie for the add()ed data.
     * Once built, no further data can be add()ed until clear() is called.
     *
     * <p>A CharsTrie cannot be empty. At least one (string, value) pair
     * must have been add()ed.
     *
     * <p>Multiple calls to build() or buildCharSequence() return tries or sequences
     * which share the builder's char array, without rebuilding.
     * After clear() has been called, a new array will be used.
     * @param buildOption Build option, see StringTrieBuilder.Option.
     * @return A new CharsTrie for the add()ed data.
     * @stable ICU 4.8
     */
    public CharsTrie build(StringTrieBuilder.Option buildOption) {
        return new CharsTrie(buildCharSequence(buildOption), 0);
    }

    /**
     * Builds a CharsTrie for the add()ed data and char-serializes it.
     * Once built, no further data can be add()ed until clear() is called.
     *
     * <p>A CharsTrie cannot be empty. At least one (string, value) pair
     * must have been add()ed.
     *
     * <p>Multiple calls to build() or buildCharSequence() return tries or sequences
     * which share the builder's char array, without rebuilding.
     * After clear() has been called, a new array will be used.
     * @param buildOption Build option, see StringTrieBuilder.Option.
     * @return A CharSequence with the char-serialized CharsTrie for the add()ed data.
     * @stable ICU 4.8
     */
    public CharSequence buildCharSequence(StringTrieBuilder.Option buildOption) {
        buildChars(buildOption);
        return CharBuffer.wrap(chars, chars.length-charsLength, charsLength);
    }

    private void buildChars(StringTrieBuilder.Option buildOption) {
        // Create and char-serialize the trie for the elements.
        if(chars==null) {
            chars=new char[1024];
        }
        buildImpl(buildOption);
    }

    /**
     * Removes all (string, value) pairs.
     * New data can then be add()ed and a new trie can be built.
     * @return this
     * @stable ICU 4.8
     */
    public CharsTrieBuilder clear() {
        clearImpl();
        chars=null;
        charsLength=0;
        return this;
    }

    /**
     * {@inheritDoc}
     * @internal
     * @deprecated This API is ICU internal only.
     */
    @Deprecated
    @Override
    protected boolean matchNodesCanHaveValues() /*const*/ { return true; }

    /**
     * {@inheritDoc}
     * @internal
     * @deprecated This API is ICU internal only.
     */
    @Deprecated
    @Override
    protected int getMaxBranchLinearSubNodeLength() /*const*/ { return CharsTrie.kMaxBranchLinearSubNodeLength; }
    /**
     * {@inheritDoc}
     * @internal
     * @deprecated This API is ICU internal only.
     */
    @Deprecated
    @Override
    protected int getMinLinearMatch() /*const*/ { return CharsTrie.kMinLinearMatch; }
    /**
     * {@inheritDoc}
     * @internal
     * @deprecated This API is ICU internal only.
     */
    @Deprecated
    @Override
    protected int getMaxLinearMatchLength() /*const*/ { return CharsTrie.kMaxLinearMatchLength; }

    private void ensureCapacity(int length) {
        if(length>chars.length) {
            int newCapacity=chars.length;
            do {
                newCapacity*=2;
            } while(newCapacity<=length);
            char[] newChars=new char[newCapacity];
            System.arraycopy(chars, chars.length-charsLength,
                             newChars, newChars.length-charsLength, charsLength);
            chars=newChars;
        }
    }
    /**
     * {@inheritDoc}
     * @internal
     * @deprecated This API is ICU internal only.
     */
    @Deprecated
    @Override
    protected int write(int unit) {
        int newLength=charsLength+1;
        ensureCapacity(newLength);
        charsLength=newLength;
        chars[chars.length-charsLength]=(char)unit;
        return charsLength;
    }
    /**
     * {@inheritDoc}
     * @internal
     * @deprecated This API is ICU internal only.
     */
    @Deprecated
    @Override
    protected int write(int offset, int length) {
        int newLength=charsLength+length;
        ensureCapacity(newLength);
        charsLength=newLength;
        int charsOffset=chars.length-charsLength;
        while(length>0) {
            chars[charsOffset++]=strings.charAt(offset++);
            --length;
        }
        return charsLength;
    }
    private int write(char[] s, int length) {
        int newLength=charsLength+length;
        ensureCapacity(newLength);
        charsLength=newLength;
        System.arraycopy(s, 0, chars, chars.length-charsLength, length);
        return charsLength;
    }

    // For writeValueAndFinal(), writeValueAndType() and writeDeltaTo().
    private final char[] intUnits=new char[3];

    /**
     * {@inheritDoc}
     * @internal
     * @deprecated This API is ICU internal only.
     */
    @Deprecated
    @Override
    protected int writeValueAndFinal(int i, boolean isFinal) {
        if(0<=i && i<=CharsTrie.kMaxOneUnitValue) {
            return write(i|(isFinal ? CharsTrie.kValueIsFinal : 0));
        }
        int length;
        if(i<0 || i>CharsTrie.kMaxTwoUnitValue) {
            intUnits[0]=(char)(CharsTrie.kThreeUnitValueLead);
            intUnits[1]=(char)(i>>16);
            intUnits[2]=(char)i;
            length=3;
        // } else if(i<=CharsTrie.kMaxOneUnitValue) {
        //     intUnits[0]=(char)(i);
        //     length=1;
        } else {
            intUnits[0]=(char)(CharsTrie.kMinTwoUnitValueLead+(i>>16));
            intUnits[1]=(char)i;
            length=2;
        }
        intUnits[0]=(char)(intUnits[0]|(isFinal ? CharsTrie.kValueIsFinal : 0));
        return write(intUnits, length);
    }
    /**
     * {@inheritDoc}
     * @internal
     * @deprecated This API is ICU internal only.
     */
    @Deprecated
    @Override
    protected int writeValueAndType(boolean hasValue, int value, int node) {
        if(!hasValue) {
            return write(node);
        }
        int length;
        if(value<0 || value>CharsTrie.kMaxTwoUnitNodeValue) {
            intUnits[0]=(char)(CharsTrie.kThreeUnitNodeValueLead);
            intUnits[1]=(char)(value>>16);
            intUnits[2]=(char)value;
            length=3;
        } else if(value<=CharsTrie.kMaxOneUnitNodeValue) {
            intUnits[0]=(char)((value+1)<<6);
            length=1;
        } else {
            intUnits[0]=(char)(CharsTrie.kMinTwoUnitNodeValueLead+((value>>10)&0x7fc0));
            intUnits[1]=(char)value;
            length=2;
        }
        intUnits[0]|=(char)node;
        return write(intUnits, length);
    }
    /**
     * {@inheritDoc}
     * @internal
     * @deprecated This API is ICU internal only.
     */
    @Deprecated
    @Override
    protected int writeDeltaTo(int jumpTarget) {
        int i=charsLength-jumpTarget;
        assert(i>=0);
        if(i<=CharsTrie.kMaxOneUnitDelta) {
            return write(i);
        }
        int length;
        if(i<=CharsTrie.kMaxTwoUnitDelta) {
            intUnits[0]=(char)(CharsTrie.kMinTwoUnitDeltaLead+(i>>16));
            length=1;
        } else {
            intUnits[0]=(char)(CharsTrie.kThreeUnitDeltaLead);
            intUnits[1]=(char)(i>>16);
            length=2;
        }
        intUnits[length++]=(char)i;
        return write(intUnits, length);
    }

    // char serialization of the trie.
    // Grows from the back: charsLength measures from the end of the buffer!
    private char[] chars;
    private int charsLength;
}
