/*
 * Copyright (c) 2001-2004 Caucho Technology, Inc.  All rights reserved.
 *
 * The Apache Software License, Version 1.1
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Caucho Technology (http://www.caucho.com/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "Burlap", "Resin", and "Caucho" must not be used to
 *    endorse or promote products derived from this software without prior
 *    written permission. For written permission, please contact
 *    info@caucho.com.
 *
 * 5. Products derived from this software may not be called "Resin"
 *    nor may "Resin" appear in their names without prior written
 *    permission of Caucho Technology.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author Scott Ferguson
 */

package com.caucho.hessian4.io;

import java.io.IOException;
import java.util.Date;

/**
 * Serializing an object for known object types.
 */
public class BasicSerializer extends AbstractSerializer
  implements ObjectSerializer
{
  public static final int NULL = 0;
  public static final int BOOLEAN = NULL + 1;
  public static final int BYTE = BOOLEAN + 1;
  public static final int SHORT = BYTE + 1;
  public static final int INTEGER = SHORT + 1;
  public static final int LONG = INTEGER + 1;
  public static final int FLOAT = LONG + 1;
  public static final int DOUBLE = FLOAT + 1;
  public static final int CHARACTER = DOUBLE + 1;
  public static final int CHARACTER_OBJECT = CHARACTER + 1;
  public static final int STRING = CHARACTER_OBJECT + 1;
  public static final int STRING_BUILDER = STRING + 1;
  public static final int DATE = STRING_BUILDER + 1;
  public static final int NUMBER = DATE + 1;
  public static final int OBJECT = NUMBER + 1;
  
  public static final int BOOLEAN_ARRAY = OBJECT + 1;
  public static final int BYTE_ARRAY = BOOLEAN_ARRAY + 1;
  public static final int SHORT_ARRAY = BYTE_ARRAY + 1;
  public static final int INTEGER_ARRAY = SHORT_ARRAY + 1;
  public static final int LONG_ARRAY = INTEGER_ARRAY + 1;
  public static final int FLOAT_ARRAY = LONG_ARRAY + 1;
  public static final int DOUBLE_ARRAY = FLOAT_ARRAY + 1;
  public static final int CHARACTER_ARRAY = DOUBLE_ARRAY + 1;
  public static final int STRING_ARRAY = CHARACTER_ARRAY + 1;
  public static final int OBJECT_ARRAY = STRING_ARRAY + 1;
  
  public static final int BYTE_HANDLE = OBJECT_ARRAY + 1;
  public static final int SHORT_HANDLE = BYTE_HANDLE + 1;
  public static final int FLOAT_HANDLE = SHORT_HANDLE + 1;

  private static final BasicSerializer BYTE_HANDLE_SERIALIZER
    = new BasicSerializer(BYTE_HANDLE);

  private static final BasicSerializer SHORT_HANDLE_SERIALIZER
    = new BasicSerializer(SHORT_HANDLE);

  private static final BasicSerializer FLOAT_HANDLE_SERIALIZER
    = new BasicSerializer(FLOAT_HANDLE);

  private int _code;

  public BasicSerializer(int code)
  {
    _code = code;
  }

  public Serializer getObjectSerializer()
  {
    switch (_code) {
    case BYTE:
      return BYTE_HANDLE_SERIALIZER;
    case SHORT:
      return SHORT_HANDLE_SERIALIZER;
    case FLOAT:
      return FLOAT_HANDLE_SERIALIZER;
    default:
      return this;
    }
  }
  
  public void writeObject(Object obj, AbstractHessianOutput out)
    throws IOException
  {
    switch (_code) {
    case BOOLEAN:
      out.writeBoolean(((Boolean) obj).booleanValue());
      break;
      
    case BYTE:
    case SHORT:
    case INTEGER:
      out.writeInt(((Number) obj).intValue());
      break;

    case LONG:
      out.writeLong(((Number) obj).longValue());
      break;

    case FLOAT:
    case DOUBLE:
      out.writeDouble(((Number) obj).doubleValue());
      break;
      
    case CHARACTER:
    case CHARACTER_OBJECT:
      out.writeString(String.valueOf(obj));
      break;
      
    case STRING:
      out.writeString((String) obj);
      break;
      
    case STRING_BUILDER:
      out.writeString(((StringBuilder) obj).toString());
      break;
      
    case DATE:
      out.writeUTCDate(((Date) obj).getTime());
      break;
      
    case BOOLEAN_ARRAY:
    {
      if (out.addRef(obj))
        return;
      
      boolean []data = (boolean []) obj;
      boolean hasEnd = out.writeListBegin(data.length, "[boolean");
      for (int i = 0; i < data.length; i++)
        out.writeBoolean(data[i]);

      if (hasEnd)
        out.writeListEnd();
      
      break;
    }

    case BYTE_ARRAY:
    {
      byte []data = (byte []) obj;
      out.writeBytes(data, 0, data.length);
      break;
    }

    case SHORT_ARRAY:
    {
      if (out.addRef(obj))
        return;
      
      short []data = (short []) obj;
      boolean hasEnd = out.writeListBegin(data.length, "[short");
      
      for (int i = 0; i < data.length; i++)
        out.writeInt(data[i]);

      if (hasEnd)
        out.writeListEnd();
      break;
    }

    case INTEGER_ARRAY:
    {
      if (out.addRef(obj))
        return;
      
      int []data = (int []) obj;
      
      boolean hasEnd = out.writeListBegin(data.length, "[int");
      
      for (int i = 0; i < data.length; i++)
        out.writeInt(data[i]);

      if (hasEnd)
        out.writeListEnd();
      
      break;
    }

    case LONG_ARRAY:
    {
      if (out.addRef(obj))
        return;
      
      long []data = (long []) obj;
      
      boolean hasEnd = out.writeListBegin(data.length, "[long");
      
      for (int i = 0; i < data.length; i++)
        out.writeLong(data[i]);

      if (hasEnd)
        out.writeListEnd();
      break;
    }

    case FLOAT_ARRAY:
    {
      if (out.addRef(obj))
        return;
      
      float []data = (float []) obj;
      
      boolean hasEnd = out.writeListBegin(data.length, "[float");
      
      for (int i = 0; i < data.length; i++)
        out.writeDouble(data[i]);

      if (hasEnd)
        out.writeListEnd();
      break;
    }

    case DOUBLE_ARRAY:
    {
      if (out.addRef(obj))
        return;
      
      double []data = (double []) obj;
      boolean hasEnd = out.writeListBegin(data.length, "[double");
      
      for (int i = 0; i < data.length; i++)
        out.writeDouble(data[i]);

      if (hasEnd)
        out.writeListEnd();
      break;
    }

    case STRING_ARRAY:
    {
      if (out.addRef(obj))
        return;
      
      String []data = (String []) obj;
      
      boolean hasEnd = out.writeListBegin(data.length, "[string");
      
      for (int i = 0; i < data.length; i++) {
        out.writeString(data[i]);
      }

      if (hasEnd)
        out.writeListEnd();
      break;
    }

    case CHARACTER_ARRAY:
    {
      char []data = (char []) obj;
      out.writeString(data, 0, data.length);
      break;
    }

    case OBJECT_ARRAY:
    {
      if (out.addRef(obj))
        return;
      
      Object []data = (Object []) obj;
      
      boolean hasEnd = out.writeListBegin(data.length, "[object");
      
      for (int i = 0; i < data.length; i++) {
        out.writeObject(data[i]);
      }

      if (hasEnd)
        out.writeListEnd();
      break;
    }
    
    case NULL:
      out.writeNull();
      break;
    
    case OBJECT:
      ObjectHandleSerializer.SER.writeObject(obj, out);
      break;

    case BYTE_HANDLE:
      out.writeObject(new ByteHandle((Byte) obj));
      break;

    case SHORT_HANDLE:
      out.writeObject(new ShortHandle((Short) obj));
      break;

    case FLOAT_HANDLE:
      out.writeObject(new FloatHandle((Float) obj));
      break;

    default:
      throw new RuntimeException(_code + " unknown code for " + obj.getClass());
    }
  }
}
