// Path: src/main/java/com/feedzai/fos/impl/weka/exception/Data2ConfigurationMismatch.java
// public class Data2ConfigurationMismatch extends WekaClassifierException {
// 
//     /**
//      * Creates a new configuration with the given message.
//      *
//      * @param message the configuration problem description
//      */
//     public Data2ConfigurationMismatch(String message) {
//         super(message);
//     }
// }
// 
// Path: src/main/java/com/feedzai/fos/impl/weka/exception/WekaClassifierException.java
// public class WekaClassifierException extends FOSException {
//     /**
//      * Builds a new exception from the given throwable.
//      *
//      * @param e the underlying throwable
//      */
//     public WekaClassifierException(Throwable e) {
//         super(e);
//     }
// 
//     /**
//      * Create a new exception with the given message.
//      *
//      * @param msg the message that explains the exception
//      */
//     public WekaClassifierException(String msg) {
//         super(msg);
//     }
// 
//     /**
//      * Create a new exception with the given message and cause.
//      *
//      * @param msg the message that explains the exception
//      * @param cause the original exception that originated this one
//      */
//     public WekaClassifierException(String msg, Throwable cause) {
//         super(msg, cause);
//     }
// }
// 
// Path: src/main/java/com/feedzai/fos/impl/weka/utils/setter/InstanceSetter.java
// public interface InstanceSetter {
//     /**
//      * Sets the given <code>attribute</code> to the <code>value</code>.
//      *
//      * @param instance  the instance where to set the value
//      * @param attribute the attribute to set
//      * @param value     the value to set
//      */
//     void set(Instance instance,Attribute attribute, Object value) throws FOSException;
// }


import weka.core.Instances;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
import com.feedzai.fos.api.*;
import com.feedzai.fos.common.validation.NotNull;
import com.feedzai.fos.common.validation.Nullable;
import com.feedzai.fos.impl.weka.exception.Data2ConfigurationMismatch;
import com.feedzai.fos.impl.weka.exception.WekaClassifierException;
import com.feedzai.fos.impl.weka.utils.setter.InstanceSetter;
import weka.classifiers.Classifier;
import weka.core.FastVector;
import weka.core.Instance;
/*
 * $#
 * FOS Weka
 *  
 * Copyright (C) 2013 Feedzai SA
 *  
 * 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 3 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.
 * 
 * You should have received a copy of the GNU General Public 
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #$
 */
package com.feedzai.fos.impl.weka.utils;




/**
 * Utilities to map from weka specific objects to classification-api objects.
 * <p/>
 * These utilities are centralized where for easier maintenance.
 *
 * @author Marco Jorge (marco.jorge@feedzai.com)
 */
public class WekaUtils {

    /**
     * Converts the given instance fields into a fast vector with <code>Attributes</code>.
     *
     * @param classIndex     classifier index
     * @param instanceFields the list of instance fields that will generate the <code>Attributes</code>
     * @return a vector with one <code>Attribute</code> for each instanceFields (in the same order).
     */
    @NotNull
    public static FastVector instanceFields2Attributes(int classIndex, List<? extends Attribute> instanceFields) throws FOSException {
        checkNotNull(instanceFields, "Instance fields cannot be null");

        FastVector result = new FastVector(instanceFields.size());
        classIndex = classIndex == -1 ? instanceFields.size() - 1 : classIndex;

        int idx = 0;
        for (Attribute instanceField : instanceFields) {
            weka.core.Attribute attribute;

            Class<?> type = instanceField.getClass();
            if (type == CategoricalAttribute.class) {
                CategoricalAttribute categorical = (CategoricalAttribute) instanceField;
                List<String> instances = categorical.getCategoricalInstances();

                FastVector categoricalInstances = new FastVector(instances.size());
                for (String categoricalInstance : instances) {
                    categoricalInstances.addElement(categoricalInstance);
                }

                attribute = new weka.core.Attribute(instanceField.getName(), categoricalInstances, idx);

            } else if (type == NumericAttribute.class) {
                attribute = new weka.core.Attribute(instanceField.getName(), idx);
            } else {
                throw new FOSException("Unknown instance class");
            }
            result.addElement(attribute);
            idx++;
        }

        return result;
    }

    /**
     * Converts from the received Object[] to a weka <code>Instance</code>.
     *
     * @param data       the Object[] to convert (the scorable)
     * @param setters    the setters that will copy the data to the instance
     * @param attributes the list of attributes to copy over
     * @return an <code>Instance</code> with all data elements copied over from the Object[]
     * @throws WekaClassifierException when the setters and the data do not have the same size (more data than setters, or more setters than data)
     */

// Path: src/main/java/com/feedzai/fos/impl/weka/exception/Data2ConfigurationMismatch.java
// public class Data2ConfigurationMismatch extends WekaClassifierException {
// 
//     /**
//      * Creates a new configuration with the given message.
//      *
//      * @param message the configuration problem description
//      */
//     public Data2ConfigurationMismatch(String message) {
//         super(message);
//     }
// }
// 
// Path: src/main/java/com/feedzai/fos/impl/weka/exception/WekaClassifierException.java
// public class WekaClassifierException extends FOSException {
//     /**
//      * Builds a new exception from the given throwable.
//      *
//      * @param e the underlying throwable
//      */
//     public WekaClassifierException(Throwable e) {
//         super(e);
//     }
// 
//     /**
//      * Create a new exception with the given message.
//      *
//      * @param msg the message that explains the exception
//      */
//     public WekaClassifierException(String msg) {
//         super(msg);
//     }
// 
//     /**
//      * Create a new exception with the given message and cause.
//      *
//      * @param msg the message that explains the exception
//      * @param cause the original exception that originated this one
//      */
//     public WekaClassifierException(String msg, Throwable cause) {
//         super(msg, cause);
//     }
// }
// 
// Path: src/main/java/com/feedzai/fos/impl/weka/utils/setter/InstanceSetter.java
// public interface InstanceSetter {
//     /**
//      * Sets the given <code>attribute</code> to the <code>value</code>.
//      *
//      * @param instance  the instance where to set the value
//      * @param attribute the attribute to set
//      * @param value     the value to set
//      */
//     void set(Instance instance,Attribute attribute, Object value) throws FOSException;
// }


// Path: src/main/java/com/feedzai/fos/impl/weka/utils/WekaUtils.java
import weka.core.Instances;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
import com.feedzai.fos.api.*;
import com.feedzai.fos.common.validation.NotNull;
import com.feedzai.fos.common.validation.Nullable;
import com.feedzai.fos.impl.weka.exception.Data2ConfigurationMismatch;
import com.feedzai.fos.impl.weka.exception.WekaClassifierException;
import com.feedzai.fos.impl.weka.utils.setter.InstanceSetter;
import weka.classifiers.Classifier;
import weka.core.FastVector;
import weka.core.Instance;

/*
 * $#
 * FOS Weka
 *  
 * Copyright (C) 2013 Feedzai SA
 *  
 * 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 3 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.
 * 
 * You should have received a copy of the GNU General Public 
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #$
 */
package com.feedzai.fos.impl.weka.utils;




/**
 * Utilities to map from weka specific objects to classification-api objects.
 * <p/>
 * These utilities are centralized where for easier maintenance.
 *
 * @author Marco Jorge (marco.jorge@feedzai.com)
 */
public class WekaUtils {

    /**
     * Converts the given instance fields into a fast vector with <code>Attributes</code>.
     *
     * @param classIndex     classifier index
     * @param instanceFields the list of instance fields that will generate the <code>Attributes</code>
     * @return a vector with one <code>Attribute</code> for each instanceFields (in the same order).
     */
    @NotNull
    public static FastVector instanceFields2Attributes(int classIndex, List<? extends Attribute> instanceFields) throws FOSException {
        checkNotNull(instanceFields, "Instance fields cannot be null");

        FastVector result = new FastVector(instanceFields.size());
        classIndex = classIndex == -1 ? instanceFields.size() - 1 : classIndex;

        int idx = 0;
        for (Attribute instanceField : instanceFields) {
            weka.core.Attribute attribute;

            Class<?> type = instanceField.getClass();
            if (type == CategoricalAttribute.class) {
                CategoricalAttribute categorical = (CategoricalAttribute) instanceField;
                List<String> instances = categorical.getCategoricalInstances();

                FastVector categoricalInstances = new FastVector(instances.size());
                for (String categoricalInstance : instances) {
                    categoricalInstances.addElement(categoricalInstance);
                }

                attribute = new weka.core.Attribute(instanceField.getName(), categoricalInstances, idx);

            } else if (type == NumericAttribute.class) {
                attribute = new weka.core.Attribute(instanceField.getName(), idx);
            } else {
                throw new FOSException("Unknown instance class");
            }
            result.addElement(attribute);
            idx++;
        }

        return result;
    }

    /**
     * Converts from the received Object[] to a weka <code>Instance</code>.
     *
     * @param data       the Object[] to convert (the scorable)
     * @param setters    the setters that will copy the data to the instance
     * @param attributes the list of attributes to copy over
     * @return an <code>Instance</code> with all data elements copied over from the Object[]
     * @throws WekaClassifierException when the setters and the data do not have the same size (more data than setters, or more setters than data)
     */

    public static Instance objectArray2Instance(Object[] data, InstanceSetter[] setters, FastVector attributes) throws FOSException {