/*
   D-Bus Java Implementation
   Copyright (c) 2005-2006 Matthew Johnson

   This program is free software; you can redistribute it and/or modify it
   under the terms of either the GNU Lesser General Public License Version 2 or the
   Academic Free Licence Version 2.1.

   Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;

import static org.freedesktop.dbus.Gettext._;

import java.lang.reflect.Array;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.List;

import cx.ath.matthew.debug.Debug;

class ArrayFrob
{
   static Hashtable<Class<? extends Object>, Class<? extends Object>> primitiveToWrapper = new Hashtable<Class<? extends Object>, Class<? extends Object>>();
   static Hashtable<Class<? extends Object>, Class<? extends Object>> wrapperToPrimitive = new Hashtable<Class<? extends Object>, Class<? extends Object>>();
   static {
      primitiveToWrapper.put( Boolean.TYPE, Boolean.class );
      primitiveToWrapper.put( Byte.TYPE, Byte.class );
      primitiveToWrapper.put( Short.TYPE, Short.class );
      primitiveToWrapper.put( Character.TYPE, Character.class );
      primitiveToWrapper.put( Integer.TYPE, Integer.class );
      primitiveToWrapper.put( Long.TYPE, Long.class );
      primitiveToWrapper.put( Float.TYPE, Float.class );
      primitiveToWrapper.put( Double.TYPE, Double.class );
      wrapperToPrimitive.put( Boolean.class, Boolean.TYPE );
      wrapperToPrimitive.put( Byte.class, Byte.TYPE );
      wrapperToPrimitive.put( Short.class, Short.TYPE );
      wrapperToPrimitive.put( Character.class, Character.TYPE );
      wrapperToPrimitive.put( Integer.class, Integer.TYPE );
      wrapperToPrimitive.put( Long.class, Long.TYPE );
      wrapperToPrimitive.put( Float.class, Float.TYPE );
      wrapperToPrimitive.put( Double.class, Double.TYPE );

   }
   @SuppressWarnings("unchecked")
   public static <T> T[] wrap(Object o) throws IllegalArgumentException
   {
         Class<? extends Object> ac = o.getClass();
         if (!ac.isArray()) throw new IllegalArgumentException(_("Not an array"));
         Class<? extends Object> cc = ac.getComponentType();
         Class<? extends Object> ncc = primitiveToWrapper.get(cc);
         if (null == ncc) throw new IllegalArgumentException(_("Not a primitive type"));
         T[] ns = (T[]) Array.newInstance(ncc, Array.getLength(o));
         for (int i = 0; i < ns.length; i++)
            ns[i] = (T) Array.get(o, i);
         return ns;
   }
   @SuppressWarnings("unchecked")
   public static <T> Object unwrap(T[] ns) throws IllegalArgumentException
   {
      Class<? extends T[]> ac = (Class<? extends T[]>) ns.getClass();
      Class<T> cc = (Class<T>) ac.getComponentType();
      Class<? extends Object> ncc = wrapperToPrimitive.get(cc);
      if (null == ncc) throw new IllegalArgumentException(_("Not a wrapper type"));
      Object o = Array.newInstance(ncc, ns.length);
      for (int i = 0; i < ns.length; i++)
         Array.set(o, i, ns[i]);
      return o;
   }
   public static <T> List<T> listify(T[] ns) throws IllegalArgumentException
   {
      return Arrays.asList(ns);
   }
   @SuppressWarnings("unchecked")
   public static <T> List<T> listify(Object o) throws IllegalArgumentException
   {
      if (o instanceof Object[]) return listify((T[]) o);
      if (!o.getClass().isArray()) throw new IllegalArgumentException(_("Not an array"));
      List<T> l = new ArrayList<T>(Array.getLength(o));
      for (int i = 0; i < Array.getLength(o); i++)
         l.add((T)Array.get(o, i));
      return l;
   }
   @SuppressWarnings("unchecked")
   public static <T> T[] delist(List<T> l, Class<T> c) throws IllegalArgumentException
   {
      return l.toArray((T[]) Array.newInstance(c, 0));
   }
   public static <T> Object delistprimitive(List<T> l, Class<T> c) throws IllegalArgumentException
   {
      Object o = Array.newInstance(c, l.size());
      for (int i = 0; i < l.size(); i++)
         Array.set(o, i, l.get(i));
      return o;
   }
   @SuppressWarnings("unchecked")
   public static Object convert(Object o, Class<? extends Object> c) throws IllegalArgumentException
   {
      /* Possible Conversions:
       *
       ** List<Integer> -> List<Integer>
       ** List<Integer> -> int[]
       ** List<Integer> -> Integer[]
       ** int[] -> int[]
       ** int[] -> List<Integer>
       ** int[] -> Integer[]
       ** Integer[] -> Integer[]
       ** Integer[] -> int[]
       ** Integer[] -> List<Integer>
       */
      try {
         // List<Integer> -> List<Integer>
         if (List.class.equals(c) 
               && o instanceof List)
            return o;

         // int[] -> List<Integer>
         // Integer[] -> List<Integer>
         if (List.class.equals(c) 
               && o.getClass().isArray()) 
            return listify(o);

         // int[] -> int[]
         // Integer[] -> Integer[]
         if (o.getClass().isArray() 
               && c.isArray() 
               && o.getClass().getComponentType().equals(c.getComponentType()))
            return o;

         // int[] -> Integer[]
         if (o.getClass().isArray() 
               && c.isArray() 
               && o.getClass().getComponentType().isPrimitive()) 
            return wrap(o);

         // Integer[] -> int[]
         if (o.getClass().isArray()
               && c.isArray() 
               && c.getComponentType().isPrimitive()) 
            return unwrap((Object[]) o);

         // List<Integer> -> int[]
         if (o instanceof List 
               && c.isArray() 
               && c.getComponentType().isPrimitive()) 
            return delistprimitive((List<Object>) o, (Class<Object>) c.getComponentType());

         // List<Integer> -> Integer[]
         if (o instanceof List 
                && c.isArray()) 
             return delist((List<Object>) o, (Class<Object>) c.getComponentType());

         if (o.getClass().isArray()
               && c.isArray())
            return type((Object[]) o, (Class<Object>) c.getComponentType());
      
      } catch (Exception e) {
         if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e);
         throw new IllegalArgumentException(e);
      }

      throw new IllegalArgumentException(MessageFormat.format(_("Not An Expected Convertion type from {0} to {1}"), new Object[] { o.getClass(), c}));
   }
   public static Object[] type(Object[] old, Class<Object> c)
   {
      Object[] ns = (Object[]) Array.newInstance(c, old.length);
      for (int i = 0; i < ns.length; i++)
         ns[i] = old[i];
      return ns;
   }
}
