/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 */

/* $Id$ */

package org.apache.fop.fo.properties;

import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.expr.PropertyException;

/**
 * Property subclass for the font shorthand
 */
public class FontShorthandProperty extends ListProperty {

    /**
     * Inner class for creating instances of FontShorthandProperty
     */
    public static class Maker extends PropertyMaker {

        private static final int[] PROP_IDS = {
            Constants.PR_FONT_SIZE, Constants.PR_FONT_FAMILY,
            Constants.PR_LINE_HEIGHT, Constants.PR_FONT_STYLE,
            Constants.PR_FONT_VARIANT, Constants.PR_FONT_WEIGHT
        };

        /**
         * @param propId ID of the property for which Maker should be created
         */
        public Maker(int propId) {
            super(propId);
        }

        /**
         * {@inheritDoc}
         */
        public Property make(PropertyList propertyList,
                String value, FObj fo) throws PropertyException {

            try {
                FontShorthandProperty newProp = new FontShorthandProperty();
                newProp.setSpecifiedValue(value);

                String specVal = value;
                Property prop = null;
                if ("inherit".equals(specVal)) {
                    /* fill the list with the individual properties from the parent */
                    for (int i = PROP_IDS.length; --i >= 0;) {
                        prop = propertyList.getFromParent(PROP_IDS[i]);
                        newProp.addProperty(prop, i);
                    }
                } else {
                    /* initialize list with nulls */
                    for (int pos = PROP_IDS.length; --pos >= 0;) {
                        newProp.addProperty(null, pos);
                    }
                    prop = checkEnumValues(specVal);
                    if (prop == null) {
                        /* not an enum:
                         * value should consist at least of font-size and font-family
                         * separated by a space
                         * mind the possible spaces from quoted font-family names
                         */
                        int spaceIndex = value.indexOf(' ');
                        int quoteIndex = (value.indexOf('\'') == -1)
                            ? value.indexOf('\"') : value.indexOf('\'');
                        if (spaceIndex == -1
                                || (quoteIndex != -1 && spaceIndex > quoteIndex)) {
                            /* no spaces or first space appears after the first
                             * single/double quote, so malformed value string
                             */
                            throw new PropertyException("Invalid property value: "
                                    + "font=\"" + value + "\"");
                        }
                        PropertyMaker m = null;
                        int fromIndex = spaceIndex + 1;
                        int toIndex = specVal.length();
                        /* at least one space that appears before the first
                         * single/double quote, so extract the individual properties
                         */
                        boolean fontFamilyParsed = false;
                        int commaIndex = value.indexOf(',');
                        while (!fontFamilyParsed) {
                            /* value contains a (list of) possibly quoted
                             * font-family name(s)
                             */
                            if (commaIndex == -1) {
                                /* no list, just a single name
                                 * (or first name in the list)
                                 */
                                if (quoteIndex != -1) {
                                    /* a single name, quoted
                                     */
                                    fromIndex = quoteIndex;
                                }
                                m = FObj.getPropertyMakerFor(PROP_IDS[1]);
                                prop = m.make(propertyList, specVal.substring(fromIndex), fo);
                                newProp.addProperty(prop, 1);
                                fontFamilyParsed = true;
                            } else {
                                if (quoteIndex != -1 && quoteIndex < commaIndex) {
                                    /* a quoted font-family name as first name
                                     * in the comma-separated list
                                     * fromIndex = index of the first quote
                                     */
                                    fromIndex = quoteIndex;
                                    quoteIndex = -1;
                                } else {
                                    fromIndex = value.lastIndexOf(' ', commaIndex) + 1;
                                }
                                commaIndex = -1;
                            }
                        }
                        toIndex = fromIndex - 1;
                        fromIndex = value.lastIndexOf(' ', toIndex - 1) + 1;
                        value = specVal.substring(fromIndex, toIndex);
                        int slashIndex = value.indexOf('/');
                        String fontSize = value.substring(0,
                                (slashIndex == -1) ? value.length() : slashIndex);
                        m = FObj.getPropertyMakerFor(PROP_IDS[0]);
                        prop = m.make(propertyList, fontSize, fo);
                        /* need to make sure subsequent call to LineHeightPropertyMaker.make()
                         * doesn't generate the default font-size property...
                         */
                        propertyList.putExplicit(PROP_IDS[0], prop);
                        newProp.addProperty(prop, 0);
                        if (slashIndex != -1) {
                            /* line-height */
                            String lineHeight = value.substring(slashIndex + 1);
                            m = FObj.getPropertyMakerFor(PROP_IDS[2]);
                            prop = m.make(propertyList, lineHeight, fo);
                            newProp.addProperty(prop, 2);
                        }
                        if (fromIndex != 0) {
                            toIndex = fromIndex - 1;
                            value = specVal.substring(0, toIndex);
                            fromIndex = 0;
                            spaceIndex = value.indexOf(' ');
                            do {
                                toIndex = (spaceIndex == -1) ? value.length() : spaceIndex;
                                String val = value.substring(fromIndex, toIndex);
                                for (int i = 6; --i >= 3;) {
                                    if (newProp.list.get(i) == null) {
                                        /* not set */
                                        m = FObj.getPropertyMakerFor(PROP_IDS[i]);
                                        val = m.checkValueKeywords(val);
                                        prop = m.checkEnumValues(val);
                                        if (prop != null) {
                                            newProp.addProperty(prop, i);
                                        }
                                    }
                                }
                                fromIndex = toIndex + 1;
                                spaceIndex = value.indexOf(' ', fromIndex);
                            } while (toIndex != value.length());
                        }
                    } else {
                        //TODO: implement enum values
                        log.warn("Enum values other than \"inherit\""
                                + " not yet supported for the font shorthand.");
                        return null;
                    }
                }
                if (newProp.list.get(0) == null || newProp.list.get(1) == null) {
                    throw new PropertyException("Invalid property value: "
                            + "font-size and font-family are required for the font shorthand"
                            + "\nfont=\"" + value + "\"");
                }
                return newProp;
           } catch (PropertyException pe) {
               pe.setLocator(propertyList.getFObj().getLocator());
               pe.setPropertyName(getName());
               throw pe;
           }
        }
    }

    private void addProperty(Property prop, int pos) {
        while (list.size() < (pos + 1)) {
            list.add(null);
        }
        list.set(pos, prop);
    }
}
