/*
 * Copyright (C) 2003-2007 eXo Platform SAS.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Affero 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/>.
 */
package org.exoplatform.services.workflow.impl.bonita;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;

import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

import org.exoplatform.container.RootContainer;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.organization.OrganizationService;
import org.exoplatform.services.organization.User;
import org.exoplatform.services.organization.UserHandler;
import org.exoplatform.services.security.ConversationState;
import org.exoplatform.services.security.Identity;
import org.exoplatform.services.security.jaas.BasicCallbackHandler;
import org.exoplatform.services.workflow.Form;
import org.ow2.bonita.facade.QueryDefinitionAPI;
import org.ow2.bonita.facade.def.dataType.DataTypeDefinition;
import org.ow2.bonita.facade.def.dataType.EnumerationTypeDefinition;
import org.ow2.bonita.facade.def.majorElement.DataFieldDefinition;
import org.ow2.bonita.facade.exception.ActivityDefNotFoundException;
import org.ow2.bonita.facade.exception.ProcessNotFoundException;
import org.ow2.bonita.facade.uuid.ActivityDefinitionUUID;
import org.ow2.bonita.facade.uuid.UUIDFactory;
import org.ow2.bonita.util.AccessorUtil;
import org.ow2.bonita.identity.auth.BonitaPrincipal;

/**
 * This class represents a Form that is automatically generated by eXo
 *
 * Created by Bull R&D
 * @author Brice Revenant
 * @author Le Gall Rodrigue <rodrigue.le-gall@bull.net>
 * Dec 30, 2005
 */
//@SuppressWarnings({"unchecked"})
public class AutomaticFormImpl implements Form {
  /** Localized Resource Bundle corresponding to this Form */
  private ResourceBundle bundle;

  /** Name of the State corresponding to this Form */
  private String activity;

  /** Submit buttons corresponding to this Form */
  private List<Map<String, Object>> submitButtons;

  /** Variables corresponding to this Form */
  private Hashtable<String, Map<String, Object>> variables;

  private static final Log LOG = ExoLogger.getExoLogger(AutomaticFormImpl.class.getName());

  /**
   * This constructor is invoked to create a new Automatic Form.
   * The Process instance identifier is not specified, which means propaged
   * properties are not displayed.
   *
   * @param processId identifies the Process to which the State belongs
   * @param activity name of the State corresponding to the Form to be created
   * @param locale    locale of the Form
   */
  public AutomaticFormImpl(String processId,
                           String activity,
                           Locale locale) {
    try {

      if("".equals(activity)) {
        // The Form corresponds to a Start Process one
        this.activity = "";
        // TODO Translate me
        this.bundle    = new AutomaticFormBundle("start");
        this.variables = new Hashtable<String, Map<String, Object>>();

        /*
         * By convention, eXo considers that a panel corresponds to a start
         * Process one when it contains no submit button. In addition there
         * needs to be a bundle string for the default button.
         */
        this.submitButtons = new ArrayList<Map<String, Object>>();
        ((AutomaticFormBundle) this.bundle).addDefaultButton();

        // Only project variables need to be processed when starting a process
        initializeProjectVariables(processId);
      }
      else {
        // The Form corresponds to an Activity. Retrieve the corresponding Node.
        this.activity   = activity;
        this.bundle      = new AutomaticFormBundle(this.activity);
        this.variables   = new Hashtable<String, Map<String, Object>>();

        /*
         * In Bonita, we consider this is the Workflow duty to determine which
         * activity comes next hence a single default button for all activities.
         */
        this.submitButtons = new ArrayList<Map<String, Object>>();
        Map<String, Object> attributes = new HashMap<String, Object>();
        attributes.put("name", "submit");
        attributes.put("transition", "");
        this.submitButtons.add(attributes);
        ((AutomaticFormBundle) this.bundle).addButton(attributes);

        // Project variables are processed last so they have a higher priority
        initializeNodeVariables(processId,activity);
        initializeProjectVariables(processId);
      }
    }
    catch(Exception e)
    {
      if (LOG.isWarnEnabled()) {
        LOG.warn(e.getMessage(), e);
      }
    }
  }

  /* (non-Javadoc)
   * @see org.exoplatform.services.workflow.Form#getCustomizedView()
   */
  public String getCustomizedView() {
    // The form does not correspond to a customized view as it is automatic
    return null;
  }

  /* (non-Javadoc)
   * @see org.exoplatform.services.workflow.Form#getIconURL()
   */
  public String getIconURL() {
    // No image is rendered when the empty string is returned
    return "";
  }

  /* (non-Javadoc)
   * @see org.exoplatform.services.workflow.Form#getResourceBundle()
   */
  public ResourceBundle getResourceBundle() {
    return this.bundle;
  }

  /* (non-Javadoc)
   * @see org.exoplatform.services.workflow.Form#getStateImageURL()
   */
  public String getStateImageURL() {
    // No image is rendered when the empty string is returned
    return "";
  }

  /* (non-Javadoc)
   * @see org.exoplatform.services.workflow.Form#getStateName()
   */
  public String getStateName() {
    return activity;
  }

  /* (non-Javadoc)
   * @see org.exoplatform.services.workflow.Form#getSubmitButtons()
   */
  public List getSubmitButtons() {
    return submitButtons;
  }

  /* (non-Javadoc)
   * @see org.exoplatform.services.workflow.Form#getVariables()
   */
  public List getVariables() {
    // Need to create a List from the Hashtable values
    List ret = new ArrayList<Map<String, Object>>();

    for(Map<String, Object> attributes : variables.values()) {
      ret.add(attributes);
    }

    return ret;
  }

  private void initializeNodeVariables(String processId,String activityId) {
        this.commit();
        try {
      QueryDefinitionAPI qAPI = AccessorUtil.getQueryAPIAccessor().getQueryDefinitionAPI();
      ActivityDefinitionUUID aUUID = qAPI.getProcessActivityId(UUIDFactory.getProcessDefinitionUUID(processId),
                                                               activityId);
            Set<DataFieldDefinition> vList = qAPI.getActivityDataFields(aUUID);

            initializeVariables(vList);
        } catch (ProcessNotFoundException e) {
          if (LOG.isWarnEnabled()) {
            LOG.warn(e.getMessage(), e);
          }
        } catch (ActivityDefNotFoundException e) {
          if (LOG.isWarnEnabled()) {
            LOG.warn(e.getMessage(), e);
          }
        }
  }

  private void initializeProjectVariables(String processId) {
      this.commit();
        try {
      Set<DataFieldDefinition> vList = AccessorUtil.getQueryAPIAccessor()
                                                   .getQueryDefinitionAPI()
                                                   .getProcessDataFields(UUIDFactory.getProcessDefinitionUUID(processId));

        initializeVariables(vList);
        } catch (ProcessNotFoundException e) {
          if (LOG.isWarnEnabled()) {
            LOG.warn(e.getMessage(), e);
          }
        }

  }

  private void initializeVariables(Set<DataFieldDefinition> vList) {
    for (DataFieldDefinition key : vList) {
      Map<String, Object> attributes = new HashMap<String, Object>();
      attributes.put("name", key.getDataFieldId());
      attributes.put("editable", "true");
      attributes.put("mandatory", "false");
      attributes.put("visiable", "true");
      if (key.getDataType().getType().equals(DataTypeDefinition.Type.EnumerationType)) {
        /*
         * In case of multiple values, the component is select and different
         * values are specified in the variable attributes, so that the bundle
         * can retrieve them. Constant variables in UITask.java cannot be
         * referenced since this class is currently not contained by a jar.
         */
        attributes.put("component", "select");
        attributes.put("possible-values",
                       (Collection<String>) ((EnumerationTypeDefinition) key.getDataType()
                                                                            .getValue()).getEnumerationValues());
      } else {
        // The default component is text
        attributes.put("component", "text");
      }
      // Include the variable in the variables list and in the bundle
      this.variables.put(key.getDataFieldId(), attributes);
      ((AutomaticFormBundle) this.bundle).addVariable(attributes);
    }
  }

  /* (non-Javadoc)
   * @see org.exoplatform.services.workflow.Form#isCustomizedView()
   */
  public boolean isCustomizedView() {
    // The form does not correspond to a customized view as it is automatic
    return false;
  }

  /* (non-Javadoc)
   * @see org.exoplatform.services.workflow.Form#isDelegatedView()
   */
  public boolean isDelegatedView() {
    // There is no delegated view as the form is automatic
    return false;
  }

    public String getMessage() {
        return null;
    }

    public boolean hasMessage() {
        return false;
    }

    private void commit(){
      LoginContext lc = null;
      OrganizationService organizationService = (OrganizationService) RootContainer.getComponent(OrganizationService.class);
      try {
        // Change for the trunk version
        Identity identity = ConversationState.getCurrent().getIdentity();
        if (identity.getSubject() != null) {
          Subject s = new Subject();
          s.getPrincipals().add(new BonitaPrincipal(identity.getUserId()));
          try {
            lc = new LoginContext("Bonita", s);
          } catch (LoginException le) {
            if (LOG.isWarnEnabled()) {
              LOG.warn(le.getMessage(), le);
            }
          }
        } else {
          UserHandler userHandler = organizationService.getUserHandler();
          User user = userHandler.findUserByName(identity.getUserId());
          char[] password = user.getPassword().toCharArray();
          BasicCallbackHandler handler = new BasicCallbackHandler(identity.getUserId(), password);
          lc = new LoginContext("gatein-domain", handler);
        }
        lc.login();
    } catch (Exception e) {
      if (LOG.isWarnEnabled()) {
        LOG.warn(e.getMessage(), e);
      }
    }
  }
}
