package com.jscape.inet.terminal;

import java.util.StringTokenizer;

/**
 * Sequence type with '[' second character. "Canonical" representation:
 * "\033[Pn;PnC", where Pn - decimal number, C - alpha character.
 * Sequence is complete when any alpha character came.
 * "Singleton" pattern [Gamma94].
 */
public class CSIType
        implements SequenceType {

    private static final String DEFAULT_PARAMETER_DELIM = ";";
    private static final CSIType INSTANCE = new CSIType();

    private String parameterDelim = DEFAULT_PARAMETER_DELIM;

    //----------------------------------------------------------------------------

    /**
     * Returns one and only one instance of this type.
     *
     * @return one and only one instance of <code>CSIType</code> object.
     */
    public static CSIType getInstance() {
        return INSTANCE;
    }

    //----------------------------------------------------------------------------

    /**
     * Private constructor to prevent uncontrolled creation.
     */
    private CSIType() {
    }

    //----------------------------------------------------------------------------

    /**
     * Checks if sequence is complete.
     *
     * @param value sequence value.
     *
     * @return true if sequence is complete.
     */
    public boolean isComplete(StringBuffer value) {
        char lastChar = value.charAt(value.length() - 1);
        return Character.isLetter(lastChar);
    }

    //----------------------------------------------------------------------------

    /**
     * Extracts and returns sequence parameters as array of <code>int</code>.
     *
     * @param value sequence value.
     *
     * @return array of <code>int</code>.
     */
    public int[] getParameters(StringBuffer value) {
        ensureEnought(value);
        String params = value.substring(2, value.length() - 1);

        if (params.length() == 0) {
            return new int[0];
        }

        if (!Character.isDigit(params.charAt(0))) {
            params = params.substring(1, params.length());
        }

        StringTokenizer tokenizer =
                new StringTokenizer(params, this.parameterDelim);
        int sz = tokenizer.countTokens();

        if (sz == 0) {
            return new int[]{ Integer.parseInt(params) };
        }

        int[] result = new int[sz];
        int i = 0;

        while (tokenizer.hasMoreTokens()) {
            result[i++] = Integer.parseInt(tokenizer.nextToken());
        }

        return result;
    }

    //----------------------------------------------------------------------------

    /**
     * Returns "canonical" form of control sequence (i.e. without parameters).
     * For example: having "\033[0;7m" incoming sequence its "canonical" form
     * will be "\033[m".
     *
     * @param value sequence value.
     *
     * @return string with "canonical" sequence form.
     */
    public String getCanonicalForm(StringBuffer value) {
        ensureEnought(value);
        String result = value.substring(0, 2);
        result += value.charAt(value.length() - 1);

        return result;
    }

    //----------------------------------------------------------------------------

    /**
     * Sets parameter delimeter.
     *
     * @param delim string with parameter delimeter.
     */
    public void setParameterDelim(String delim) {
        if (delim == null) {
            throw new NullPointerException();
        }

        this.parameterDelim = delim;
    }

    //----------------------------------------------------------------------------

    /**
     * Checks if there is enough characters in sequence to get it's "canonical"
     * form.
     *
     * @param value sequence value.
     */
    private void ensureEnought(StringBuffer value) {
        if (value.length() < 3) {
            throw new IllegalArgumentException("Not enough chars");
        }
    }

}