/**
 * Copyright (C) 2010-12 Brookhaven National Laboratory
 * All rights reserved. Use is subject to license terms.
 */
package org.epics.pvmanager.vtype;

import org.epics.vtype.VString;
import org.epics.vtype.ValueUtil;
import org.epics.vtype.VInt;
import org.epics.vtype.VTable;
import org.epics.vtype.Scalar;
import org.epics.vtype.VDouble;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.epics.pvmanager.ReadFunction;
import org.epics.vtype.ValueFactory;

/**
 *
 * @author carcassi
 */
class VTableAggregationFunction implements ReadFunction<VTable> {
    
    private final List<List<ReadFunction<?>>> functions;
    private final List<String> names;
    private static final Map<Class<?>, Class<?>> typeConversion;
    private static final Map<Class<?>, ArrayAdder> arrayAdders;
    
    static {
        typeConversion = new HashMap<Class<?>, Class<?>>();
        typeConversion.put(VString.class, String.class);
        typeConversion.put(VDouble.class, Double.TYPE);
        typeConversion.put(VInt.class, Integer.TYPE);
        
        arrayAdders = new HashMap<Class<?>, ArrayAdder>();
        arrayAdders.put(String.class, new ArrayAdder() {

            @Override
            public void addValue(Object array, int pos, Object value) {
                if (value != null)
                   ((String[]) array)[pos] = ((VString) value).getValue();
            }
        });
        arrayAdders.put(Double.TYPE, new ArrayAdder() {

            @Override
            @SuppressWarnings("unchecked")
            public void addValue(Object array, int pos, Object value) {
                double converted = Double.NaN;
                if (value != null)
                    converted = ((Number) ((Scalar) value).getValue()).doubleValue();
                ((double[]) array)[pos] = converted;
            }
        });
        arrayAdders.put(Integer.TYPE, new ArrayAdder() {

            @Override
            @SuppressWarnings("unchecked")
            public void addValue(Object array, int pos, Object value) {
                int converted = 0;
                if (value != null)
                    converted = ((Number) ((Scalar) value).getValue()).intValue();
                ((int[]) array)[pos] = converted;
            }
        });
    }

    public VTableAggregationFunction(List<List<ReadFunction<?>>> functions, List<String> names) {
        this.functions = functions;
        this.names = names;
    }

    @Override
    public VTable readValue() {
        List<Class<?>> types = new ArrayList<Class<?>>();
        List<Object> values = new ArrayList<Object>();
        
        for (List<ReadFunction<?>> columnFunctions : functions) {
            List<Object> columnValues = new ArrayList<Object>();
            Class<?> columnType = null;
            
            // Extract all values and determine column type
            for (ReadFunction<?> function : columnFunctions) {
                Object value = function.readValue();
                columnType = validateType(value, columnType, names.get(types.size()));
                
                columnValues.add(value);
            }
            
            // If no type is found, the column will be empty.
            // Default to an array of Strings
            if (columnType == null)
                columnType = String.class;
            
            // Prepare column array
            Object array = java.lang.reflect.Array.newInstance(columnType, columnValues.size());
            for (int i = 0; i < columnValues.size(); i++) {
                arrayAdders.get(columnType).addValue(array, i, columnValues.get(i));
            }
            
            // Done with this column
            types.add(columnType);
            values.add(array);
        }
        
        return ValueFactory.newVTable(types, names, values);
    }
    
    private static interface ArrayAdder {
        void addValue(Object array, int pos, Object value);
    }
    
    private Class<?> validateType(Object value, Class<?> oldType, String columnName) {
        if (value == null)
            return oldType;
        
        // Type of the final array
        Class<?> newType = typeConversion.get(ValueUtil.typeOf(value));
        if (oldType == null)
            return newType;
        
        if (newType != null) {
            if (newType.equals(oldType))
                return oldType;

            // Convert integers to double if mixed column
            if (newType.equals(Double.TYPE) && oldType.equals(Integer.TYPE))
                return newType;
            if (newType.equals(Integer.TYPE) && oldType.equals(Double.TYPE))
                return oldType;
        }
        
                
        // Types don't match
        throw new RuntimeException("Values for column " + columnName + " are not all of the same valid column type: can't convert "
                + value.getClass().getSimpleName() + " to " + oldType.getSimpleName() + " - currently only VString, VDouble and VInt).");
    }
    
}

@@ -363,24 +363,49 @@
 .ArrayList;%0A
+import java.util.Arrays;%0A
 import java.
@@ -504,16 +504,95 @@
 nction;%0A
+import org.epics.util.array.ArrayDouble;%0Aimport org.epics.util.array.ArrayInt;%0A
 import o
@@ -1527,32 +1527,178 @@
 );%0A            %7D
+%0A%0A            @Override%0A            public Object finalizeData(Object data) %7B%0A                return Arrays.asList((String%5B%5D) data);%0A            %7D
 %0A        %7D);%0A   
@@ -2114,32 +2114,180 @@
 d;%0A            %7D
+%0A%0A            @Override%0A            public Object finalizeData(Object data) %7B%0A                return new ArrayDouble((double%5B%5D) data);%0A            %7D
 %0A        %7D);%0A   
@@ -2438,37 +2438,36 @@
 addValue(Object 
-array
+data
 , int pos, Objec
@@ -2651,29 +2651,28 @@
    ((int%5B%5D) 
-array
+data
 )%5Bpos%5D = con
@@ -2684,32 +2684,187 @@
 d;%0A            %7D
+%0A%0A            @Override%0A            public Object finalizeData(Object data) %7B%0A                return new ArrayInt((int%5B%5D) data);%0A            %7D%0A            
 %0A        %7D);%0A   
@@ -3991,21 +3991,20 @@
  Object 
-array
+data
  = java.
@@ -4181,21 +4181,20 @@
 ddValue(
-array
+data
 , i, col
@@ -4339,16 +4339,57 @@
 dd(array
+Adders.get(columnType).finalizeData(data)
 );%0A     
@@ -4537,37 +4537,36 @@
 addValue(Object 
-array
+data
 , int pos, Objec
@@ -4571,24 +4571,66 @@
 ect value);%0A
+        Object finalizeData(Object data);%0A
     %7D%0A    %0A 

update to SNAPSHOT