/*
 * Copyright 2004-2005 The Trix Development Team.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.trix.cuery.value;

import org.w3c.css.sac.LexicalUnit;
import org.w3c.dom.DOMException;
import org.w3c.dom.css.Counter;
import org.w3c.dom.css.RGBColor;
import org.w3c.dom.css.Rect;

/**
 * DOCUMENT.
 * 
 * @author <a href="mailto:Teletha.T@gmail.com">Teletha Testarossa</a>
 * @version $ Id: AbstractCSSValue.java,v 1.04 2005/09/28 11:15:43 Teletha Exp $
 */
public abstract class AbstractCSSValue implements CSSValue {

    /** The float value. */
    protected float floatValue;

    /** The string value. */
    protected String stringValue;

    /** The param value. */
    protected CSSValue params;

    /** The previous value. */
    private AbstractCSSValue previous;

    /** The next value. */
    private AbstractCSSValue next;

    /** The value length. */
    private int length = 1;

    /** The current position. */
    private int position = 0;

    /**
     * Create AbstractCSSValue instance.
     * 
     * @param previous A previous value of this value.
     */
    protected AbstractCSSValue(CSSValue previous) {
        this.previous = (AbstractCSSValue) previous;

        // make value chain
        if (previous != null) {
            this.previous.next = this;
            this.length = this.previous.length;
            this.position = this.previous.length;
            propagate();
        }
    }

    /**
     * @see org.trix.cuery.value.CSSValue#get(int)
     */
    public CSSValue get(int index) {
        // check length
        if (index < 0 || length - 1 < index) {
            return null;
        }

        AbstractCSSValue current = this;

        // search
        while (current != null && index != current.position) {
            if (index > current.position) {
                current = current.next;
            } else if (index < current.position) {
                current = current.previous;
            }
        }
        return current;
    }

    /**
     * @see org.trix.cuery.value.CSSValue#getPosition()
     */
    public int getPosition() {
        return position;
    }

    /**
     * @see org.trix.cuery.value.CSSValue#getNextValue()
     */
    public CSSValue getNextValue() {
        return next;
    }

    /**
     * @see org.trix.cuery.value.CSSValue#getPreviousValue()
     */
    public CSSValue getPreviousValue() {
        return previous;
    }

    /**
     * @see org.trix.cuery.value.CSSValue#getType()
     */
    public Class getType() {
        return getClass();
    }

    /**
     * @see org.w3c.css.sac.LexicalUnit#getNextLexicalUnit()
     */
    public LexicalUnit getNextLexicalUnit() {
        return next;
    }

    /**
     * @see org.w3c.css.sac.LexicalUnit#getPreviousLexicalUnit()
     */
    public LexicalUnit getPreviousLexicalUnit() {
        return previous;
    }

    /**
     * @see org.w3c.css.sac.LexicalUnit#getIntegerValue()
     */
    public int getIntegerValue() {
        return Math.round(floatValue);
    }

    /**
     * @see org.w3c.css.sac.LexicalUnit#getDimensionUnitText()
     */
    public String getDimensionUnitText() {
        return "";
    }

    /**
     * @see org.w3c.css.sac.LexicalUnit#getFunctionName()
     */
    public String getFunctionName() {
        return "";
    }

    /**
     * @see org.w3c.css.sac.LexicalUnit#getParameters()
     */
    public LexicalUnit getParameters() {
        return params;
    }

    /**
     * @see org.w3c.css.sac.LexicalUnit#getSubValues()
     */
    public LexicalUnit getSubValues() {
        return params;
    }

    /**
     * @see org.w3c.dom.css.CSSPrimitiveValue#getFloatValue(short)
     */
    public float getFloatValue(short unitType) throws DOMException {
        // check range of unit type
        if (unitType < CSS_NUMBER || CSS_DIMENSION < unitType) {
            throw new DOMException(unitType, "This type is out of range.");
        }
        return 0;
    }

    /**
     * @see org.w3c.dom.css.CSSPrimitiveValue#getCounterValue()
     */
    public Counter getCounterValue() throws DOMException {
        throw new DOMException(DOMException.INVALID_ACCESS_ERR, "This value is not a CSS Counter.");
    }

    /**
     * @see org.w3c.dom.css.CSSPrimitiveValue#getRectValue()
     */
    public Rect getRectValue() throws DOMException {
        throw new DOMException(DOMException.INVALID_ACCESS_ERR, "This value is not a CSS Rect.");
    }

    /**
     * @see org.w3c.dom.css.CSSPrimitiveValue#getRGBColorValue()
     */
    public RGBColor getRGBColorValue() throws DOMException {
        throw new DOMException(DOMException.INVALID_ACCESS_ERR, "This value is not a CSS RGBColor.");
    }

    /**
     * @see org.w3c.dom.css.CSSValue#getCssText()
     */
    public String getCssText() {
        return stringValue;
    }

    /**
     * @see org.w3c.dom.css.CSSValue#setCssText(java.lang.String)
     */
    public void setCssText(String text) throws DOMException {
    }

    /**
     * @see org.w3c.dom.css.CSSValue#getCssValueType()
     */
    public short getCssValueType() {
        if (length == 1) {
            return CSS_PRIMITIVE_VALUE;
        } else {
            return CSS_VALUE_LIST;
        }
    }

    /**
     * @see org.w3c.dom.css.CSSPrimitiveValue#setFloatValue(short, float)
     */
    public void setFloatValue(short unitType, float value) throws DOMException {
        // check range of unit type
        if (unitType < CSS_NUMBER || CSS_DIMENSION < unitType) {
            throw new DOMException(unitType, "This type is out of range.");
        }
    }

    /**
     * @see org.w3c.dom.css.CSSPrimitiveValue#setStringValue(short, java.lang.String)
     */
    public void setStringValue(short unitType, String value) throws DOMException {
        // check range of unit type
        if (unitType < CSS_STRING || CSS_ATTR < unitType) {
            throw new DOMException(unitType, "This type is out of range.");
        }
    }

    /**
     * @see org.w3c.dom.css.CSSValueList#getLength()
     */
    public int getLength() {
        return length;
    }

    /**
     * @see org.w3c.dom.css.CSSValueList#item(int)
     */
    public org.w3c.dom.css.CSSValue item(int index) {
        return get(index);
    }

    /**
     * @see java.lang.Object#toString()
     */
    public final String toString() {
        return getCssText();
    }

    /**
     * Propagate value length to previous value.
     */
    private void propagate() {
        length++;

        if (previous != null) {
            previous.propagate();
        }
    }

}
