/*
* JBoss, Home of Professional Open Source
* Copyright 2006, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.metatype.api.values;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;

import org.jboss.metatype.api.types.Name;
import org.jboss.metatype.api.types.SimpleMetaType;

/**
 * SimpleValue.
 * 
 * @author <a href="adrian@jboss.com">Adrian Brock</a>
 * @version $Revision: 1.1 $
 */
public class SimpleValueSupport extends AbstractMetaValue implements SimpleValue
{
   /** The serialVersionUID */
   private static final long serialVersionUID = 8473043036261557127L;
   
   /** The simple meta type */
   private SimpleMetaType metaType;
   
   /** The value */
   private Serializable value;

   /**
    * Wrap a simple object in simple value
    * 
    * @param object the simple object
    * @return the simple value
    */
   public static SimpleValue wrap(Serializable object)
   {
      if (object == null)
         return null;
      String className = object.getClass().getName();
      return wrap(object, className);
   }
   private static SimpleValue wrap(Serializable object, String className)
   {
      SimpleMetaType metaType = SimpleMetaType.resolve(className);
      return new SimpleValueSupport(metaType, object);
   }
   public static SimpleValue wrap(boolean value)
   {
      return wrap(value, boolean.class.getName());
   }
   public static SimpleValue wrap(byte value)
   {
      return wrap(value, byte.class.getName());
   }
   public static SimpleValue wrap(char value)
   {
      return wrap(value, char.class.getName());
   }
   public static SimpleValue wrap(short value)
   {
      return wrap(value, short.class.getName());
   }
   public static SimpleValue wrap(int value)
   {
      return wrap(value, int.class.getName());
   }
   public static SimpleValue wrap(long value)
   {
      return wrap(value, long.class.getName());
   }
   public static SimpleValue wrap(float value)
   {
      return wrap(value, float.class.getName());
   }
   public static SimpleValue wrap(double value)
   {
      return wrap(value, double.class.getName());
   }
   
   /**
    * Create a new SimpleValueSupport.
    * 
    * @param metaType the simple meta type
    * @param value the value
    * @throws IllegalArgumentException for a null simpleMetaType
    */
   public SimpleValueSupport(SimpleMetaType metaType, Serializable value)
   {
      if (metaType == null)
         throw new IllegalArgumentException("Null simple meta type");
      this.metaType = metaType;
      setValue(value);
   }

   public SimpleMetaType getMetaType()
   {
      return metaType;
   }

   /**
    * Get the value.
    * 
    * @return the value.
    */
   public Serializable getValue()
   {
      return value;
   }

   /**
    * Set the value.
    * 
    * @param value the value.
    */
   public void setValue(Serializable value)
   {
      this.value = value;
   }

   @Override
   public boolean equals(Object obj)
   {
      if (obj == this)
         return true;
      
      if (obj == null || obj instanceof SimpleValue == false)
         return false;

      SimpleValue other = (SimpleValue) obj;
      if (metaType.equals(other.getMetaType()) == false)
      {
         // Check equivalent types
         if (metaType.equalsIgnorePrimitive(other.getMetaType()) == false)
            return false;
      }

      Object otherValue = other.getValue();
      if (value == null && otherValue == null)
         return true;
      if (value == null && otherValue != null)
         return false;
      return value.equals(otherValue);
   }
   
   @Override
   public int hashCode()
   {
      if (value == null)
         return 0;
      return value.hashCode();
   }

   @Override
   public String toString()
   {
      return metaType + ":" + value;
   }

   public int compareTo(SimpleValue sv)
   {
      int compare = -1;
      if(getMetaType() == SimpleMetaType.BIGINTEGER && sv.getMetaType() == SimpleMetaType.BIGINTEGER)
      {
         BigInteger v1 = BigInteger.class.cast(value);
         BigInteger v2 = BigInteger.class.cast(sv.getValue());
         compare = v1.compareTo(v2);
      }
      else if(getMetaType() == SimpleMetaType.BIGDECIMAL && sv.getMetaType() == SimpleMetaType.BIGDECIMAL)
      {
         BigDecimal v1 = BigDecimal.class.cast(value);
         BigDecimal v2 = BigDecimal.class.cast(sv.getValue());
         compare = v1.compareTo(v2);
      }
      else if(getMetaType() == SimpleMetaType.BOOLEAN && sv.getMetaType() == SimpleMetaType.BOOLEAN)
      {
         Boolean v1 = Boolean.class.cast(value);
         Boolean v2 = Boolean.class.cast(sv.getValue());
         compare = v1.compareTo(v2);
      }
      else if(getMetaType() == SimpleMetaType.BOOLEAN_PRIMITIVE && sv.getMetaType() == SimpleMetaType.BOOLEAN_PRIMITIVE)
      {
         Boolean v1 = Boolean.class.cast(value);
         Boolean v2 = Boolean.class.cast(sv.getValue());
         compare = v1.compareTo(v2);
      }
      else if(getMetaType() == SimpleMetaType.BYTE && sv.getMetaType() == SimpleMetaType.BYTE)
      {
         Byte v1 = Byte.class.cast(value);
         Byte v2 = Byte.class.cast(sv.getValue());
         compare = v1.compareTo(v2);
      }
      else if(getMetaType() == SimpleMetaType.CHARACTER && sv.getMetaType() == SimpleMetaType.CHARACTER)
      {
         Character v1 = Character.class.cast(value);
         Character v2 = Character.class.cast(sv.getValue());
         compare = v1.compareTo(v2);
      }
      else if(getMetaType() == SimpleMetaType.CHARACTER_PRIMITIVE && sv.getMetaType() == SimpleMetaType.CHARACTER_PRIMITIVE)
      {
         Character v1 = Character.class.cast(value);
         Character v2 = Character.class.cast(sv.getValue());
         compare = v1.compareTo(v2);
      }
      else if(getMetaType() == SimpleMetaType.DATE && sv.getMetaType() == SimpleMetaType.DATE)
      {
         Date v1 = Date.class.cast(value);
         Date v2 = Date.class.cast(sv.getValue());
         compare = v1.compareTo(v2);
      }
      else if(getMetaType() == SimpleMetaType.DOUBLE && sv.getMetaType() == SimpleMetaType.DOUBLE)
      {
         Double v1 = Double.class.cast(value);
         Double v2 = Double.class.cast(sv.getValue());
         compare = v1.compareTo(v2);
      }
      else if(getMetaType() == SimpleMetaType.FLOAT && sv.getMetaType() == SimpleMetaType.FLOAT)
      {
         Float v1 = Float.class.cast(value);
         Float v2 = Float.class.cast(sv.getValue());
         compare = v1.compareTo(v2);
      }
      else if(getMetaType() == SimpleMetaType.INTEGER && sv.getMetaType() == SimpleMetaType.INTEGER)
      {
         Integer v1 = Integer.class.cast(value);
         Integer v2 = Integer.class.cast(sv.getValue());
         compare = v1.compareTo(v2);
      }
      else if(getMetaType() == SimpleMetaType.LONG && sv.getMetaType() == SimpleMetaType.LONG)
      {
         Long v1 = Long.class.cast(value);
         Long v2 = Long.class.cast(sv.getValue());
         compare = v1.compareTo(v2);
      }
      else if(getMetaType() == SimpleMetaType.SHORT && sv.getMetaType() == SimpleMetaType.SHORT)
      {
         Short v1 = Short.class.cast(value);
         Short v2 = Short.class.cast(sv.getValue());
         compare = v1.compareTo(v2);
      }
      else if(getMetaType() == SimpleMetaType.STRING && sv.getMetaType() == SimpleMetaType.STRING)
      {
         String v1 = String.class.cast(value);
         String v2 = String.class.cast(sv.getValue());
         compare = v1.compareTo(v2);
      }
      else if(getMetaType() == SimpleMetaType.NAMEDOBJECT && sv.getMetaType() == SimpleMetaType.NAMEDOBJECT)
      {
         Name v1 = Name.class.cast(value);
         Name v2 = Name.class.cast(sv.getValue());
         compare = v1.compareTo(v2);
      }
      else if(getMetaType() == SimpleMetaType.VOID && sv.getMetaType() == SimpleMetaType.VOID)
      {
         compare = 0;
      }
      else if(value instanceof Number && sv.getValue() instanceof Number)
      {
         // Compare two Numbers using the most precise type
         Number n0 = (Number) value;
         Number n1 = (Number) sv.getValue();
         if(n0 instanceof BigDecimal)
         {
            BigDecimal db0 = (BigDecimal) n0;
            BigDecimal db1 = new BigDecimal(n1.doubleValue());
            compare = db0.compareTo(db1);
         }
         else if(n1 instanceof BigDecimal)
         {
            BigDecimal db1 = (BigDecimal) n1;
            BigDecimal db0 = new BigDecimal(n0.doubleValue());
            compare = db0.compareTo(db1);
         }
         else if(n0 instanceof Float || n0 instanceof Double
              || n1 instanceof Float || n1 instanceof Double)
         {
            double d0 = n0.doubleValue();
            double d1 = n1.doubleValue();
            compare = Double.compare(d0, d1);
         }
         else
         {
            long l0 = n0.longValue();
            long l1 = n1.longValue();
            if(l0 < l1)
               compare = -1;
            else if(l0 > l1)
               compare = 1;
            else
               compare = 0;
         }
      }

      return compare;
   }
   
}
