/*
 * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source 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, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.quercus.function;

import com.caucho.quercus.env.*;
import com.caucho.quercus.module.ModuleContext;
import com.caucho.quercus.lib.file.BinaryInput;
import com.caucho.quercus.program.JavaClassDef;
import com.caucho.util.L10N;
import com.caucho.vfs.Path;

import java.io.InputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URL;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * Code for marshaling (PHP to Java) and unmarshaling (Java to PHP) arguments.
 */
public class MarshalFactory {
  private static final L10N L = new L10N(MarshalFactory.class);

  protected ModuleContext _moduleContext;

  public MarshalFactory(ModuleContext moduleContext)
  {
    _moduleContext = moduleContext;
  }

  public Marshal create(Class argType)
  {
    return create(argType, false);
  }

  public Marshal create(Class argType,
                        boolean isNotNull)
  {
    return create(argType, isNotNull, false);
  }

  public Marshal create(Class argType,
                        boolean isNotNull,
                        boolean isNullAsFalse)
  {
    final Marshal marshal;

    // optimized cases, new types should be added to JavaMarshall

    if (String.class.equals(argType)) {
      marshal = StringMarshal.MARSHAL;
    }
    else if (boolean.class.equals(argType)) {
      marshal = BooleanMarshal.MARSHAL;
    }
    else if (Boolean.class.equals(argType)) {
      marshal = BooleanObjectMarshal.MARSHAL;
    }
    else if (byte.class.equals(argType)) {
      marshal = ByteMarshal.MARSHAL;
    }
    else if (Byte.class.equals(argType)) {
      marshal = ByteObjectMarshal.MARSHAL;
    }
    else if (short.class.equals(argType)) {
      marshal = ShortMarshal.MARSHAL;
    }
    else if (Short.class.equals(argType)) {
      marshal = ShortObjectMarshal.MARSHAL;
    }
    else if (int.class.equals(argType)) {
      marshal = IntegerMarshal.MARSHAL;
    }
    else if (Integer.class.equals(argType)) {
      marshal = IntegerObjectMarshal.MARSHAL;
    }
    else if (long.class.equals(argType)) {
      marshal = LongMarshal.MARSHAL;
    }
    else if (Long.class.equals(argType)) {
      marshal = LongObjectMarshal.MARSHAL;
    }
    else if (LongValue.class.equals(argType)) {
      marshal = LongValueMarshal.MARSHAL;
    }
    else if (float.class.equals(argType)) {
      marshal = FloatMarshal.MARSHAL;
    }
    else if (Float.class.equals(argType)) {
      marshal = FloatObjectMarshal.MARSHAL;
    }
    else if (double.class.equals(argType)) {
      marshal = DoubleMarshal.MARSHAL;
    }
    else if (Double.class.equals(argType)) {
      marshal = DoubleObjectMarshal.MARSHAL;
    }
    else if (DoubleValue.class.equals(argType)) {
      marshal = DoubleValueMarshal.MARSHAL;
    }
    else if (BigDecimal.class.equals(argType)) {
      marshal = BigDecimalMarshal.MARSHAL;
    }
    else if (BigInteger.class.equals(argType)) {
      marshal = BigIntegerMarshal.MARSHAL;
    }
    else if (char.class.equals(argType)) {
      marshal = CharacterMarshal.MARSHAL;
    }
    else if (Character.class.equals(argType)) {
      marshal = CharacterObjectMarshal.MARSHAL;
    }
    else if (Path.class.equals(argType)) {
      marshal = PathMarshal.MARSHAL;
    }
    else if (Callback.class.equals(argType)) {
      marshal = CallbackMarshal.MARSHAL;
    }
    else if (StringValue.class.equals(argType)) {
      marshal = StringValueMarshal.MARSHAL;
    }
    else if (UnicodeValue.class.equals(argType)) {
      marshal = UnicodeValueMarshal.MARSHAL;
    }
    else if (BinaryValue.class.equals(argType)) {
      marshal = BinaryValueMarshal.MARSHAL;
    }
    else if (BytesValue.class.equals(argType)) {
      marshal = BinaryValueMarshal.MARSHAL;
    }
    else if (InputStream.class.equals(argType)) {
      marshal = InputStreamMarshal.MARSHAL;
    }
    else if (BinaryInput.class.equals(argType)) {
      marshal = BinaryInputMarshal.MARSHAL;
    }
    else if (ArrayValue.class.equals(argType)) {
      marshal = ArrayValueMarshal.MARSHAL;
    }
    else if (Value.class.equals(argType)) {
      marshal = ValueMarshal.MARSHAL;
    }
    else if (Value.class.isAssignableFrom(argType)) {
      marshal = new ExtValueMarshal(argType);
    }
    else if (void.class.equals(argType)) {
      marshal = VoidMarshal.MARSHAL;
    }
    else if (Calendar.class.equals(argType)){
      marshal = CalendarMarshal.MARSHAL;
    }
    else if (Date.class.equals(argType)) {
      marshal = DateMarshal.MARSHAL;
    }
    else if (URL.class.equals(argType)) {
      marshal = URLMarshal.MARSHAL;
    }
    else if (byte[].class.equals(argType)) {
      marshal = JavaByteArrayMarshal.MARSHAL;
    }
    else if (Byte[].class.equals(argType)) {
      marshal = JavaByteObjectArrayMarshal.MARSHAL;
    }
    else if (char[].class.equals(argType)) {
      marshal = JavaCharacterArrayMarshal.MARSHAL;
    }
    else if (Character[].class.equals(argType)) {
      marshal = JavaCharacterObjectArrayMarshal.MARSHAL;
    }
    else if (argType.isArray()) {
      marshal = new JavaArrayMarshal(argType);
    }
    else if (Map.class.isAssignableFrom(argType)) {
      String typeName = argType.getName();

      JavaClassDef javaDef = _moduleContext.getJavaClassDefinition(typeName);

      marshal = new JavaMapMarshal(javaDef, isNotNull, isNullAsFalse);
    }
    else if (List.class.isAssignableFrom(argType)) {
      String typeName = argType.getName();

      JavaClassDef javaDef = _moduleContext.getJavaClassDefinition(typeName);

      marshal = new JavaListMarshal(javaDef, isNotNull, isNullAsFalse);
    }
    else if (Collection.class.isAssignableFrom(argType)) {
      String typeName = argType.getName();

      JavaClassDef javaDef = _moduleContext.getJavaClassDefinition(typeName);

      marshal = new JavaCollectionMarshal(javaDef, isNotNull, isNullAsFalse);
    }
    else {
      String typeName = argType.getName();

      JavaClassDef javaDef = _moduleContext.getJavaClassDefinition(typeName);

      marshal = new JavaMarshal(javaDef, isNotNull, isNullAsFalse);
    }

    if (! isNullAsFalse)
      return marshal;
    else {
      if (Value.class.equals(argType)
	  || Boolean.class.equals(argType)
	  || Byte.class.equals(argType)
	  || Short.class.equals(argType)
	  || Integer.class.equals(argType)
	  || Long.class.equals(argType)
	  || Float.class.equals(argType)
	  || Double.class.equals(argType)
	  || Character.class.equals(argType)) {

        String shortName = argType.getSimpleName();
        throw new UnsupportedOperationException("@ReturnNullAsFalse cannot be used with return type `"+shortName+"'");
      }

      return new NullAsFalseMarshal(marshal);
    }
  }

  public Marshal createReference()
  {
    return ReferenceMarshal.MARSHAL;
  }
}

