/* Copyright (C) 1999-2003 by Peter Eastman

   This program 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.

   This program 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 General Public License for more details. */

package artofillusion.datahandling;

import java.lang.reflect.*;
import java.util.*;

/** Base class for operations on arrays or List like adding, modifying, removing
 *  of items. This version is focused on a copy operation instead of inplace
 *  modification
 *
 * @author  Michael Butscher
 * @see AbstractInplaceListOperation
 */
public abstract class AbstractCopyListOperation implements ListOperation
{
  /** Returns an array of the same type as parameter array
    really has but of length len.
    <P>
    Example: int[] newint = (int[]) AbstractListOperation.createArrayOfType(new int[8], 10);
  */
  public static Object[] createArrayOfType(Object[] array, int len)
  {
    Class cmpclass = array.getClass().getComponentType();
    return (Object[]) Array.newInstance(cmpclass, len);
  }
  
  
  /** Changes data according to the type of subclass of AbstractListOperation
    and possibly additional data. This method calls apply(Object[] array) for the 
    actual list operation.
    
    @see #apply(Object[])
  */
  
  public void apply(List data)
  {
    Object[] array = data.toArray();
    Object[] resulto = apply(array);
    data.clear();
    data.addAll(Arrays.asList(resulto));
  }
  
  
  /** Apply specified list operation to arr. The returned array type
    can be casted to the same type the parameter array had.
    The returned array may be identical to the parameter array if length
    is not changed during operation. Returns null if array is null.
    <P>
    This is the only method which must be overridden.
  */
    
  public abstract Object[] apply(Object[] array);
  
  
  
  /** Specialized version for boolean arrays. This default version converts
    all array elements between primitive type and 
    Boolean and calls apply(Object[] array) for the actual list
    operation.
    <P>
    This method may be overridden, mainly for speed reasons.
    
    @see #apply(Object[])
  */
  
  public boolean[] apply(boolean[] array)  // All lines with //! must be changed between different types
  {
    if (array == null)
      return null;
    Object[] op = new Object[array.length];
    for (int i = 0; i < array.length; ++i)
      op[i] = new Boolean(array[i]);                //!
    
    Object[] resulto = apply(op);
    
    boolean[] result;                                   //!
    if (resulto.length != array.length)
      result = new boolean[resulto.length];             //!
    else
      result = array;
    
    for (int i = 0; i < resulto.length; ++i)
      result[i] = ((Boolean)resulto[i]).booleanValue();  //!
    
    return result;
  }
  
  
  /** Specialized version for int arrays. This default version converts
    all array elements between primitive type and appropriate
    Number subclass and calls apply(Object[] array) for the actual list
    operation.
    <P>
    This method may be overridden, mainly for speed reasons.
    
    @see #apply(Object[])
  */
  
  public int[] apply(int[] array)  // All lines with //! must be changed between different types
  {
    if (array == null)
      return null;
    Object[] op = new Object[array.length];
    for (int i = 0; i < array.length; ++i)
      op[i] = new Integer(array[i]);                //!
    
    Object[] resulto = apply(op);
    
    int[] result;                                   //!
    if (resulto.length != array.length)
      result = new int[resulto.length];             //!
    else
      result = array;
    
    for (int i = 0; i < resulto.length; ++i)
      result[i] = ((Number)resulto[i]).intValue();  //!
    
    return result;
  }
  
  
  /** Specialized version for float arrays. This default version converts
    all array elements between primitive type and appropriate
    Number subclass and calls apply(Object[] array) for the actual list
    operation.
    <P>
    This method may be overridden, mainly for speed reasons.
    
    @see #apply(Object[])
  */
  
  public float[] apply(float[] array)  // All lines with //! must be changed between different types
  {
    if (array == null)
      return null;
    Object[] op = new Object[array.length];
    for (int i = 0; i < array.length; ++i)
      op[i] = new Float(array[i]);                //!
    
    Object[] resulto = apply(op);
    
    float[] result;                                   //!
    if (resulto.length != array.length)
      result = new float[resulto.length];             //!
    else
      result = array;
    
    for (int i = 0; i < resulto.length; ++i)
      result[i] = ((Number)resulto[i]).floatValue();  //!
    
    return result;
  }
  
  
  /** Specialized version for double arrays. This default version converts
    all array elements between primitive type and appropriate
    Number subclass and calls apply(Object[] array) for the actual list
    operation.
    <P>
    This method may be overridden, mainly for speed reasons.
    
    @see #apply(Object[])
  */
  
  public double[] apply(double[] array)  // All lines with //! must be changed between different types
  {
    if (array == null)
      return null;
    Object[] op = new Object[array.length];
    for (int i = 0; i < array.length; ++i)
      op[i] = new Double(array[i]);                //!
    
    Object[] resulto = apply(op);
    
    double[] result;                                   //!
    if (resulto.length != array.length)
      result = new double[resulto.length];             //!
    else
      result = array;
    
    for (int i = 0; i < resulto.length; ++i)
      result[i] = ((Number)resulto[i]).doubleValue();  //!
    
    return result;
  }
}
