/*
 * 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.property.css2;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.ArrayUtils;

import org.trix.cuery.property.Property;
import org.trix.cuery.property.PropertyDefinition;
import org.trix.cuery.property.PropertyRegistry;
import org.trix.cuery.property.config.FontConfiguration;
import org.trix.cuery.value.CSSLength;
import org.trix.cuery.value.CSSPercentage;
import org.trix.cuery.value.CSSValue;
import org.trix.cuery.value.CentimeterLength;
import org.trix.cuery.value.EMLength;
import org.trix.cuery.value.Identifier;
import org.trix.cuery.value.InchLength;
import org.trix.cuery.value.MillimeterLength;
import org.trix.cuery.value.PicaLength;
import org.trix.cuery.value.PixelLength;
import org.trix.cuery.value.PointLength;
import org.trix.cuery.value.ValueUtil;

/**
 * DOCUMENT.
 * 
 * @author <a href="mailto:Teletha.T@gmail.com">Teletha Testarossa</a>
 * @version $ Id: FontSizeDefinition.java,v 1.02 2005/09/20 4:25:59 Teletha Exp $
 */
public class FontSizeDefinition extends PropertyDefinition {

    /** The keyword for font size. */
    private static final String[] ABSOLUTE_SIZE = {"xx-small", "x-small", "small", "medium", "large", "x-large",
            "xx-large"};

    /** The cache for absolute size value. */
    private final List absolutes = new ArrayList(ABSOLUTE_SIZE.length);

    /**
     * Create FontSizeDefinition instance.
     */
    public FontSizeDefinition() {
        FontConfiguration config = PropertyRegistry.getFontConfiguration();

        float result;
        float medium = config.getMediumFontSize();
        float scale = config.getScalingFactor();

        // initialize
        for (int i = 0; i < ABSOLUTE_SIZE.length; i++) {
            switch (i) {
            case 0:
                result = medium / scale / scale / scale;
                break;

            case 1:
                result = medium / scale / scale;
                break;

            case 2:
                result = medium / scale;
                break;

            case 3:
                result = medium;
                break;

            case 4:
                result = medium * scale;
                break;

            case 5:
                result = medium * scale * scale;
                break;

            case 6:
                result = medium * scale * scale * scale;
                break;

            default:
                result = medium;
                break;
            }
            // store
            absolutes.add(i, new PixelLength(ValueUtil.round(result)));
        }
    }

    /**
     * @see org.trix.cuery.property.PropertyDefinition#computeEMLength(org.trix.cuery.value.EMLength,
     *      org.trix.cuery.property.Property, org.trix.cuery.property.Property)
     */
    protected CSSValue computeEMLength(EMLength length, Property property, Property parent) {
        if (parent == null) {
            return getComputedValue(getInitialValue(), property, parent);
        } else {
            CSSLength fontSize = (CSSLength) parent.getValue("font-size");
            return ValueUtil.multiply(fontSize, length.getFloatValue());
        }
    }

    /**
     * @see org.trix.cuery.property.PropertyDefinition#computeCentimeterLength(org.trix.cuery.value.CentimeterLength,
     *      org.trix.cuery.property.Property, org.trix.cuery.property.Property)
     */
    protected CSSValue computeCentimeterLength(CentimeterLength length, Property property, Property parent) {
        return ValueUtil.convertToPixelLength(length);
    }

    /**
     * @see org.trix.cuery.property.PropertyDefinition#computeIdentifier(org.trix.cuery.value.Identifier,
     *      org.trix.cuery.property.Property, org.trix.cuery.property.Property)
     */
    protected CSSValue computeIdentifier(Identifier identifier, Property property, Property parent) {
        String value = identifier.getCssText();

        if (value.equals("larger")) {
            return getLargerFontSize(parent.getValue(name));
        } else if (value.equals("smaller")) {
            return getSmallerFontSize(parent.getValue(name));
        } else {
            return getAbsoluteFontSize(value);
        }
    }

    /**
     * @see org.trix.cuery.property.PropertyDefinition#computeInchLength(org.trix.cuery.value.InchLength,
     *      org.trix.cuery.property.Property, org.trix.cuery.property.Property)
     */
    protected CSSValue computeInchLength(InchLength length, Property property, Property parent) {
        return ValueUtil.convertToPixelLength(length);
    }

    /**
     * @see org.trix.cuery.property.PropertyDefinition#computeMillimeterLength(org.trix.cuery.value.MillimeterLength,
     *      org.trix.cuery.property.Property, org.trix.cuery.property.Property)
     */
    protected CSSValue computeMillimeterLength(MillimeterLength length, Property property, Property parent) {
        return ValueUtil.convertToPixelLength(length);
    }

    /**
     * @see org.trix.cuery.property.PropertyDefinition#computePercentage(org.trix.cuery.value.CSSPercentage,
     *      org.trix.cuery.property.Property, org.trix.cuery.property.Property)
     */
    protected CSSValue computePercentage(CSSPercentage percentage, Property property, Property parent) {
        CSSLength fontSize = (CSSLength) parent.getValue("font-size");
        return ValueUtil.multiply(fontSize, percentage);
    }

    /**
     * @see org.trix.cuery.property.PropertyDefinition#computePicaLength(org.trix.cuery.value.PicaLength,
     *      org.trix.cuery.property.Property, org.trix.cuery.property.Property)
     */
    protected CSSValue computePicaLength(PicaLength length, Property property, Property parent) {
        return ValueUtil.convertToPixelLength(length);
    }

    /**
     * @see org.trix.cuery.property.PropertyDefinition#computePointLength(org.trix.cuery.value.PointLength,
     *      org.trix.cuery.property.Property, org.trix.cuery.property.Property)
     */
    protected CSSValue computePointLength(PointLength length, Property property, Property parent) {
        return ValueUtil.convertToPixelLength(length);
    }

    /**
     * Return an absolute font size by keyword.
     * 
     * @param keyword A keyword for abusolute size. ie, medium, large.
     * @return An actual font size.
     */
    private PixelLength getAbsoluteFontSize(String keyword) {
        // check null
        if (keyword == null || keyword.length() == 0) {
            return null;
        }

        int index = ArrayUtils.indexOf(ABSOLUTE_SIZE, keyword);

        // check keyword
        if (index == -1) {
            return null;
        }

        // retrive
        return (PixelLength) absolutes.get(index);
    }

    /**
     * Return a larger font size from base size.
     * 
     * @param base A base font size.
     * @return A larger font size.
     */
    private PixelLength getLargerFontSize(CSSValue base) {
        if (ValueUtil.isAbsoluteCSSLength(base)) {
            float baseLength = ValueUtil.convertToPixelLength(base.getFloatValue(), base.getLexicalUnitType());

            for (int i = 0; i < absolutes.size(); i++) {
                PixelLength pixel = (PixelLength) absolutes.get(i);

                if (baseLength < pixel.getFloatValue()) {
                    return pixel;
                }
            }

            // not found

        }
        return null;
    }

    /**
     * Return a smaller font size from base size.
     * 
     * @param base A base font size.
     * @return A larger font size.
     */
    private PixelLength getSmallerFontSize(CSSValue base) {
        if (ValueUtil.isAbsoluteCSSLength(base)) {
            float baseLength = ValueUtil.convertToPixelLength(base.getFloatValue(), base.getLexicalUnitType());

            for (int i = absolutes.size(); i > 0; i--) {
                PixelLength pixel = (PixelLength) absolutes.get(i);

                if (baseLength > pixel.getFloatValue()) {
                    return pixel;
                }
            }

            // not found

        }
        return null;
    }
}
