package org.openbravo.growthmachine;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.Map;
import java.util.Properties;

import org.hibernate.criterion.Expression;
import org.openbravo.base.exception.OBException;
import org.openbravo.base.provider.OBProvider;
import org.openbravo.base.session.OBPropertiesProvider;
import org.openbravo.dal.core.OBContext;
import org.openbravo.dal.service.OBCriteria;
import org.openbravo.dal.service.OBDal;
import org.openbravo.model.ad.process.Parameter;
import org.openbravo.model.ad.process.ProcessInstance;

public class CallProcess {

  private static CallProcess instance = new CallProcess();

  public static synchronized CallProcess getInstance() {
    return instance;
  }

  public static synchronized void setInstance(CallProcess instance) {
    CallProcess.instance = instance;
  }

  public ProcessInstance call(String processName, String recordID, Map<String, Object> parameters) {
    final OBCriteria<org.openbravo.model.ad.ui.Process> processCriteria = OBDal.getInstance()
        .createCriteria(org.openbravo.model.ad.ui.Process.class);
    processCriteria.add(Expression.eq(org.openbravo.model.ad.ui.Process.PROPERTY_PROCEDURE,
        processName));
    if (processCriteria.list().size() != 1) {
      throw new OBException("No process or more than one process found using procedurename "
          + processName);

    }
    return call(processCriteria.list().get(0), recordID, parameters);
  }

  public ProcessInstance call(org.openbravo.model.ad.ui.Process process, String recordID,
      Map<String, Object> parameters) {

    final boolean currentAdminMode = OBContext.getOBContext().setInAdministratorMode(true);
    try {
      // Create the pInstance
      final ProcessInstance pInstance = OBProvider.getInstance().get(ProcessInstance.class);
      // sets its process
      pInstance.setProcess(process);
      // must be set to true
      pInstance.setActive(true);
      if (recordID != null) {
        pInstance.setRecordID(recordID);
      } else {
        pInstance.setRecordID("0");
      }

      // get the user from the context
      pInstance.setUserContact(OBContext.getOBContext().getUser());

      // now create the parameters and set their values
      if (parameters != null) {
        int index = 0;
        for (final String key : parameters.keySet()) {
          index++;
          final Object value = parameters.get(key);
          final Parameter parameter = OBProvider.getInstance().get(Parameter.class);
          parameter.setSequenceNumber(index + "");
          parameter.setParameterName(key);
          final String valueClassName = value.getClass().getName();
          if (java.util.Date.class.getName().equals(valueClassName))
            parameter.setProcessDate((java.util.Date) value);
          else if (String.class.getName().equals(valueClassName))
            parameter.setString((String) value);

          // set both sides of the bidirectional association
          pInstance.getADParameterList().add(parameter);
          parameter.setProcessInstance(pInstance);
        }
      }

      // persist to the db
      OBDal.getInstance().save(pInstance);

      // flush, this gives pInstance an ID
      OBDal.getInstance().flush();

      // call the SP
      try {
        // first get a connection
        final Connection connection = OBDal.getInstance().getConnection();
        // connection.createStatement().execute("CALL M_InOut_Create0(?)");

        final Properties obProps = OBPropertiesProvider.getInstance().getOpenbravoProperties();
        final PreparedStatement ps;
        if (obProps.getProperty("bbdd.rdbms") != null
            && obProps.getProperty("bbdd.rdbms").equals("POSTGRE")) {
          ps = connection.prepareStatement("SELECT * FROM " + process.getProcedure() + "(?)");
        } else {
          ps = connection.prepareStatement(" CALL " + process.getProcedure() + "(?)");
        }

        ps.setString(1, pInstance.getId());
        ps.execute();
      } catch (final Exception e) {
        throw new IllegalStateException(e);
      }

      // refresh the pInstance as the SP has changed it
      OBDal.getInstance().getSession().refresh(pInstance);
      return pInstance;
    } finally {
      OBContext.getOBContext().setInAdministratorMode(currentAdminMode);
    }
  }
}
