/*
 * 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 java.awt.Color;

import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.datatypes.Length;
import org.apache.fop.datatypes.Numeric;
import org.apache.fop.datatypes.PercentBaseContext;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.expr.PropertyException;

/**
 * Class for handling numeric properties
 */
public class NumberProperty extends Property implements Numeric {

    /**
     * Inner class for making NumberProperty objects
     */
    public static class Maker extends PropertyMaker {

        /**
         * Constructor for NumberProperty.Maker
         * @param propId the id of the property for which a Maker should be created
         */
        public Maker(int propId) {
            super(propId);
        }

        /**
         * {@inheritDoc}
         */
        public Property convertProperty(Property p,
                                        PropertyList propertyList, FObj fo) 
                    throws PropertyException {
            if (p instanceof NumberProperty) {
                return p;
            }
            if (p instanceof EnumProperty) {
                return EnumNumber.getInstance(p);
            }
            Number val = p.getNumber();
            if (val != null) {
                return new NumberProperty(val);
            }
            return convertPropertyDatatype(p, propertyList, fo);
        }

    }

    /** cache holding all canonical NumberProperty instances */
    private static final PropertyCache cache = new PropertyCache();
    
    private final Number number;

    /**
     * Constructor for Number input
     * @param num Number object value for property
     */
    protected NumberProperty(Number num) {
        this.number = num;
    }

    /**
     * Constructor for double input
     * @param num double numeric value for property
     */
    protected NumberProperty(double num) {
        this.number = new Double(num);
    }

    /**
     * Constructor for integer input
     * @param num integer numeric value for property
     */
    protected NumberProperty(int num) {
        this.number = new Integer(num);
    }
    
    /**
     * Returns the canonical NumberProperty instance
     * corresponding to the given Number
     * @param num   the base Number
     * @return  the canonical NumberProperty
     */
    public static NumberProperty getInstance(Number num) {
        return (NumberProperty)cache.fetch(
                    new NumberProperty(num));
    }
    
    /**
     * Returns the canonical NumberProperty instance
     * corresponding to the given double
     * @param num   the base double value
     * @return  the canonical NumberProperty
     */
    public static NumberProperty getInstance(double num) {
        return (NumberProperty)cache.fetch(
                    new NumberProperty(num));
    }

    /**
     * Returns the canonical NumberProperty instance
     * corresponding to the given int
     * @param num   the base int value
     * @return  the canonical NumberProperty
     */
    public static NumberProperty getInstance(int num) {
        return (NumberProperty)cache.fetch(
                    new NumberProperty(num));
    }
    
    /**
     * Plain number always has a dimension of 0.
     * @return a dimension of 0.
     */
    public int getDimension() {
        return 0;
    }

    /**
     * Return the value of this Numeric.
     * @return The value as a double.
     */
    public double getNumericValue() {
        return number.doubleValue();
    }

    /**
     * Return the value of this Numeric.
     * @param context Evaluation context
     * @return The value as a double.
     */
    public double getNumericValue(PercentBaseContext context) {
        return getNumericValue();
    }

    /** {@inheritDoc} */
    public int getValue() {
        return number.intValue();
    }

    /**
     * Return the value
     * @param context Evaluation context
     * @return The value as an int.
     */
    public int getValue(PercentBaseContext context) {
        return getValue();
    }

    /**
     * Return true because all numbers are absolute.
     * @return true.
     */
    public boolean isAbsolute() {
        return true;
    }

    /**
     * @return this.number cast as a Number
     */
    public Number getNumber() {
        return this.number;
    }

    /**
     * @return this.number cast as an Object
     */
    public Object getObject() {
        return this.number;
    }

    /**
     * Convert NumberProperty to Numeric object
     * @return Numeric object corresponding to this
     */
    public Numeric getNumeric() {
        return this;
    }

    /** {@inheritDoc} */
    public Length getLength() {
        //Assume pixels (like in HTML) when there's no unit
        return FixedLength.getInstance(getNumericValue(), "px");
    }

    /**
     * Convert NumberProperty to a Color. Not sure why this is needed.
     * @param foUserAgent FOUserAgent 
     * @return Color that corresponds to black
     */
    public Color getColor(FOUserAgent foUserAgent) {
        // TODO: Implement somehow
        // Convert numeric value to color ???
        // Convert to hexadecimal and then try to make it into a color?
        return Color.black;
    }

}

@@ -6126,10 +6126,487 @@
 %0A    %7D%0A%0A
+    /** %7B@inheritDoc%7D */%0A    public int hashCode() %7B%0A        return number.hashCode();%0A    %7D%0A    %0A    /** %7B@inheritDoc%7D */%0A    public boolean equals(Object o) %7B%0A        if (o != null && o instanceof NumberProperty) %7B%0A            NumberProperty np = (NumberProperty) o;%0A            return (np.number == this.number%0A                    %7C%7C (this.number != null%0A                        && this.number.equals(np.number)));%0A        %7D else %7B%0A            return false;%0A        %7D%0A    %7D%0A
 %7D%0A

add implementation of hashCode() and equals() to NumberProperty, to make the caching work as expected.