/*
 * 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.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;
  
  /**
   * 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)
    {
      e.printStackTrace();
    }
  }

  /* (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) {
            e.printStackTrace();
        } catch (ActivityDefNotFoundException e) {
            e.printStackTrace();
        } 
  }
  
  private void initializeProjectVariables(String processId) {
      this.commit();
        try {
            Set<DataFieldDefinition> vList = AccessorUtil.getQueryAPIAccessor().getQueryDefinitionAPI().getProcessDataFields(UUIDFactory.getProcessDefinitionUUID(processId));
        
    		initializeVariables(vList);
        } catch (ProcessNotFoundException e) {
            e.printStackTrace();
        }
	
  }
  
  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) {
            le.printStackTrace();
          }
        } 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) {
      }
    }
}
