package es.stratebi.civi;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;

import es.stratebi.civi.util.CiviField;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.exception.KettleValueException;
import org.pentaho.di.core.row.ValueMetaInterface;
import org.pentaho.di.i18n.BaseMessages;
import org.pentaho.di.trans.Trans;
import org.pentaho.di.trans.TransMeta;
import org.pentaho.di.trans.step.BaseStep;
import org.pentaho.di.trans.step.StepDataInterface;
import org.pentaho.di.trans.step.StepInterface;
import org.pentaho.di.trans.step.StepMeta;
import org.pentaho.di.trans.step.StepMetaInterface;

/*
 * 
 * Esta es la clase principal y es la que lleva la logica del plugin, tiene un metodo init llamado
 * al inicio para definir los valores de inicializacion del paso y luego internamente en la clase
 * BaseStep se hacen llamadas repetitivas al metodo processRow hasta que no tengan mas filas que
 * procesar. En el proyecto faltan aun las validaciones como la del chequeo de conexion y la
 * definicion y conversion de los tipos de datos para los campos que devuelve CiviCRM, que es el
 * proximo paso antes de tomar la salida
 */

public abstract class CiviStep extends BaseStep implements StepInterface {
    protected Object civiData;
    protected Object civiMeta;
    static int row = 0;

    protected static Class<?> PKG = CiviStep.class; // for i18n purposes

    public CiviStep(StepMeta s, StepDataInterface stepDataInterface, int c, TransMeta t, Trans dis) {
        super(s, stepDataInterface, c, t, dis);
    }

    /**
     * Este metodo es el encargado de procesar las filas que se envian hacia el paso siguiente.
     * 
     * Note que como lo que realizamos son varias llamadas llamadas a la API REST de CIVI se
     * implementa a traves del metodo readOneRow
     */
    public abstract boolean processRow(StepMetaInterface smi, StepDataInterface sdi) throws KettleException;

    /**
     * Leemos solo una fila de la lista y la devolvemos para que sea enviada hacia el paso siguiente
     * Deberiamos hacer las conversiones de tipo aqui, pero debo analizar esta parte,
     * de momento son tratados como String
     * @throws KettleException 
     */
    
    protected abstract Object[] readOneRow() throws KettleException;
    
    protected Object getObjectValue(String field, String object) {
        try {
            if (object == null || object.equals("")) {
                return null;
            }

            CiviField cf = ((CiviMeta) civiMeta).getCiviCrmListingFields().get(field);

            int metaType =  ValueMetaInterface.TYPE_STRING;
            if (cf != null)
               metaType = cf.getMetaInterfaceType();

            switch (metaType) {
            case ValueMetaInterface.TYPE_INTEGER:
                return Long.parseLong(object);
            case ValueMetaInterface.TYPE_STRING:
                return object.toString();
            case ValueMetaInterface.TYPE_NUMBER:
                return Double.parseDouble(object);
            case ValueMetaInterface.TYPE_DATE:
                SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
                return formatter.parse(object);
            case ValueMetaInterface.TYPE_BIGNUMBER:
                return new BigDecimal(object.toString());
            case ValueMetaInterface.TYPE_BOOLEAN:
                return Boolean.parseBoolean(object);
            case ValueMetaInterface.TYPE_BINARY:
                throw new KettleValueException(toString() + " : I don't know how to convert binary values to integers.");
            case ValueMetaInterface.TYPE_SERIALIZABLE:
                throw new KettleValueException(toString()
                        + " : I don't know how to convert serializable values to integers.");
            default:
                throw new KettleValueException(toString() + " : Unknown type " + metaType + " specified.");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /*
     * Este metodo es el primero en se llamado cuando se inicia el paso, en caso de retornar false e
     * interrumpe el mismo y finaliza la transformacion.
     * 
     * Tener en cuenta que los valores de StepMetaInterface son los que se obtienen de leer los
     * datos desde el repositorio donde esta la transformacion guardada. Por tanto antes deben
     * haberse inicializado StepMetaInterface y StepDataInterface. La excepcion es cuando se esta
     * haciendo una previsualizacion la cual no lee estos del repositorio y pasa directamente a
     * processRow tomando los valores que tiene en memoria. De esta forma la secuencia seria la
     * siguiente:
     */
    // ..... Previusualizando: .......... Ejecutando:
    // ..... --- No se ejecuta ---- ..... CiviInputMeta.loadXml()
    // ..... CiviInput.init(...) ........ CiviInput.init(...)
    // ..... CiviInput.processRow(...) .. CiviInput.processRow(...)
    public boolean init(StepMetaInterface smi, StepDataInterface sdi) {
        civiMeta = smi;
        civiData = sdi;

        /*
         * Aqui garantizamos seguir el procesamiento de las filas extraidas de CiviCRM, si fallara
         * algo deberiamos retornar false y kettle detendria el paso y la transformacion
         */
        boolean passed = true;
        if (super.init(smi, sdi)) {
            if (Const.isEmpty(environmentSubstitute(((CiviMeta) civiMeta).getCiviCrmApiKey()))) {
                logError(BaseMessages.getString(PKG, "CiviCrmStep.Error.EmptyApiKey"));
                passed = false;
            }
            if (passed && Const.isEmpty(environmentSubstitute(((CiviMeta) civiMeta).getCiviCrmSiteKey()))) {
                logError(BaseMessages.getString(PKG, "CiviCrmStep.Error.EmptySiteKey"));
                passed = false;
            }
            if (passed && Const.isEmpty(environmentSubstitute(((CiviMeta) civiMeta).getCiviCrmRestUrl()))) {
                logError(BaseMessages.getString(PKG, "CiviCrmStep.Error.EmptyRestUrl"));
                passed = false;
            }
            if (passed && Const.isEmpty(environmentSubstitute(((CiviMeta) civiMeta).getCiviCrmEntity()))) {
                logError(BaseMessages.getString(PKG, "CiviCrmStep.Error.EmptyEntity"));
                passed = false;
            }
        }

        return passed;
    }

    public void dispose(StepMetaInterface smi, StepDataInterface sdi) {
        super.dispose(smi, sdi);
    }
}