/*
 * Copyright (C) 2003-2008 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.wcm.webui.dialog.permission;

import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.jcr.AccessDeniedException;
import javax.jcr.Node;
import javax.jcr.Session;

import org.exoplatform.commons.utils.ObjectPageList;
import org.exoplatform.ecm.webui.form.UIFormInputSetWithAction;
import org.exoplatform.ecm.webui.selector.UIGroupMemberSelector;
import org.exoplatform.ecm.webui.selector.UISelectable;
import org.exoplatform.services.jcr.access.AccessControlEntry;
import org.exoplatform.services.jcr.access.PermissionType;
import org.exoplatform.services.jcr.access.SystemIdentity;
import org.exoplatform.services.jcr.core.ExtendedNode;
import org.exoplatform.services.wcm.core.NodeLocation;
import org.exoplatform.wcm.webui.Utils;
import org.exoplatform.wcm.webui.dialog.UIContentDialogForm;
import org.exoplatform.wcm.webui.selector.account.UIUserContainer;
import org.exoplatform.web.application.ApplicationMessage;
import org.exoplatform.webui.config.annotation.ComponentConfig;
import org.exoplatform.webui.config.annotation.EventConfig;
import org.exoplatform.webui.core.UIGrid;
import org.exoplatform.webui.core.UIPopupContainer;
import org.exoplatform.webui.core.lifecycle.UIFormLifecycle;
import org.exoplatform.webui.event.Event;
import org.exoplatform.webui.event.EventListener;
import org.exoplatform.webui.form.UIForm;
import org.exoplatform.webui.form.UIFormCheckBoxInput;
import org.exoplatform.webui.form.UIFormStringInput;

/**
 * Created by The eXo Platform SAS
 * Author : Phan Le Thanh Chuong
 *          chuong.phan@exoplatform.com, phan.le.thanh.chuong@gmail.com
 * Oct 29, 2009  
 */
@ComponentConfig (
    lifecycle = UIFormLifecycle.class,
    template = "classpath:groovy/wcm/webui/dialog/permission/UIPermissionManager.gtmpl", 
    events = {
      @EventConfig(listeners = UIPermissionManager.DeleteActionListener.class, confirm = "UIPermissionManagerGrid.msg.confirm-delete-permission"),
      @EventConfig(listeners = UIPermissionManager.EditActionListener.class),
      @EventConfig(listeners = UIPermissionManager.SaveActionListener.class),
      @EventConfig(listeners = UIPermissionManager.ClearActionListener.class),
      @EventConfig(listeners = UIPermissionManager.SelectUserActionListener.class),
      @EventConfig(listeners = UIPermissionManager.SelectMemberActionListener.class),
      @EventConfig(listeners = UIPermissionManager.AddAnyActionListener.class) 
    }
)
public class UIPermissionManager extends UIForm implements UISelectable {
	
	/** The Constant PERMISSION_MANAGER_GRID. */
	public static final String PERMISSION_MANAGER_GRID    = "UIPermissionManagerGrid";
	
  /** The Constant PERMISSION_INPUT_SET. */
  public static final String PERMISSION_INPUT_SET    		= "UIPermissionInputSetWithAction";
  
  /** The Constant PERMISSION_STRING_INPUT. */
  public static final String PERMISSION_STRING_INPUT 		= "UIPermissionStringInput";

  /** The Constant ACCESSIBLE_CHECKBOX_INPUT. */
  public static final String ACCESSIBLE_CHECKBOX_INPUT  = "UIAccessibleCheckboxInput";
  
  /** The Constant EDITABLE_CHECKBOX_INPUT. */
  public static final String EDITABLE_CHECKBOX_INPUT    = "UIEditableCheckboxInput";

  public static final String USER_SELECTOR_POPUP_WINDOW = "UIUserSelectorPopupWindow";
  
  public static final String GROUP_SELECTOR_POPUP_WINDOW = "UIGroupSelectorPopupWindow";
  
  private String popupId;
  
	public String getPopupId() {
		return popupId;
	}

	public void setPopupId(String popupId) {
		this.popupId = popupId;
	}

	/**
   * Instantiates a new uI permission info.
   * 
   * @throws Exception the exception
   */
  public UIPermissionManager() throws Exception {
    UIGrid uiGrid = createUIComponent(UIGrid.class, null, PERMISSION_MANAGER_GRID);
    uiGrid.setLabel(PERMISSION_MANAGER_GRID);
    uiGrid.configure("owner", new String[] {"owner", "accessible", "editable"}, new String[] {"Edit", "Delete"});
    addChild(uiGrid);
    
    UIFormInputSetWithAction permissionInputSet = new UIFormInputSetWithAction(PERMISSION_INPUT_SET);       
    UIFormStringInput formStringInput = new UIFormStringInput(PERMISSION_STRING_INPUT, PERMISSION_STRING_INPUT, null);
    formStringInput.setEditable(false);
    permissionInputSet.addChild(formStringInput);
    permissionInputSet.setActionInfo(PERMISSION_STRING_INPUT, new String[] {"SelectUser", "SelectMember", "AddAny"});
    permissionInputSet.showActionInfo(true);
    addChild(permissionInputSet);
    addChild(new UIFormCheckBoxInput<String>(ACCESSIBLE_CHECKBOX_INPUT, ACCESSIBLE_CHECKBOX_INPUT, null));
    addChild(new UIFormCheckBoxInput<String>(EDITABLE_CHECKBOX_INPUT, EDITABLE_CHECKBOX_INPUT, null));
    setActions(new String[] {"Save", "Clear"});
  }
  
  /**
   * Update grid.
   * 
   * @throws Exception the exception
   */
  public void updateGrid() throws Exception {
  	// Get node
  	UIContentDialogForm contentDialogForm = getAncestorOfType(UIPopupContainer.class).getChild(UIContentDialogForm.class);
  	NodeLocation webcontentNodeLocation = contentDialogForm.getWebcontentNodeLocation();
    Node node = NodeLocation.getNodeByLocation(webcontentNodeLocation);
    ExtendedNode webcontent = (ExtendedNode) node;
    
    // Convert permission entries to map
    List<UIPermissionConfig> permissionConfigs = new ArrayList<UIPermissionConfig>();
    Map<String, List<String>> permissionMap = new HashMap<String, List<String>>();
    List<AccessControlEntry> accessControlEntries = webcontent.getACL().getPermissionEntries();
    for (AccessControlEntry accessControlEntry : accessControlEntries) {
      String identity = accessControlEntry.getIdentity();
      String permission = accessControlEntry.getPermission();
      List<String> currentPermissions = permissionMap.get(identity);
      if (!permissionMap.containsKey(identity)) {
        permissionMap.put(identity, null);
      }
      if (currentPermissions == null)
        currentPermissions = new ArrayList<String>();
      if (!currentPermissions.contains(permission)) {
        currentPermissions.add(permission);
      }
      permissionMap.put(identity, currentPermissions);
    }
    
    // Add owner's permission
    String owner = SystemIdentity.SYSTEM;
    if (webcontent.hasProperty("exo:owner"))
      owner = webcontent.getProperty("exo:owner").getString();
    UIPermissionConfig permissionConfig = new UIPermissionConfig();
    if (!permissionMap.containsKey(owner)) {
      permissionConfig.setOwner(owner);
      permissionConfig.setAccessible(true);
      permissionConfig.setEditable(true);
      permissionConfigs.add(permissionConfig);
    }

    // Add node's permission
    Iterator<String> permissionIterator = permissionMap.keySet().iterator();
    while (permissionIterator.hasNext()) {
      String identity = (String) permissionIterator.next();
      List<String> userPermissions = permissionMap.get(identity);
      UIPermissionConfig permBean = new UIPermissionConfig();
      permBean.setOwner(identity);
      int numberPermission = 0;
      for (String p : PermissionType.ALL) {
        if (!userPermissions.contains(p)) break;
        numberPermission++;
      }
      if (numberPermission == PermissionType.ALL.length) {
        permBean.setEditable(true);
        permBean.setAccessible(true);
      } else {
        permBean.setAccessible(true);
      }
      permissionConfigs.add(permBean);
    }
    ObjectPageList objPageList = new ObjectPageList(permissionConfigs, 10);
    UIGrid uiGrid = getChildById(PERMISSION_MANAGER_GRID);
    uiGrid.getUIPageIterator().setPageList(objPageList);
  }

  /* (non-Javadoc)
   * @see org.exoplatform.ecm.webui.selector.UISelectable#doSelect(java.lang.String, java.lang.Object)
   */
  public void doSelect(String selectField, Object value) throws Exception {
  	UIFormInputSetWithAction permissionInputSet = getChildById(PERMISSION_INPUT_SET);
    permissionInputSet.getUIStringInput(PERMISSION_STRING_INPUT).setValue(value.toString());
    Utils.closePopupWindow(this, popupId);
  }
  
  /**
  * Checks for change permission right.
  * 
  * @param node the node
  * 
  * @return true, if successful
  * 
  * @throws Exception the exception
  */
  private boolean hasChangePermissionRight(ExtendedNode node) throws Exception {
   try {
     node.checkPermission(PermissionType.ADD_NODE);
     node.checkPermission(PermissionType.REMOVE);
     node.checkPermission(PermissionType.SET_PROPERTY);
     return true;
   } catch (AccessControlException e) {
     return false;
   }
  }
  
  /**
   * The listener interface for receiving deleteAction events.
   * The class that is interested in processing a deleteAction
   * event implements this interface, and the object created
   * with that class is registered with a component using the
   * component's <code>addDeleteActionListener<code> method. When
   * the deleteAction event occurs, that object's appropriate
   * method is invoked.
   * 
   * @see DeleteActionEvent
   */
  public static class DeleteActionListener extends EventListener<UIPermissionManager> {

    /* (non-Javadoc)
     * @see org.exoplatform.webui.event.EventListener#execute(org.exoplatform.webui.event.Event)
     */
    public void execute(Event<UIPermissionManager> event) throws Exception {
    	UIPermissionManager permissionManager = event.getSource();
      UIContentDialogForm contentDialogForm = permissionManager.getAncestorOfType(UIPopupContainer.class).getChild(UIContentDialogForm.class);
    	NodeLocation webcontentNodeLocation = contentDialogForm.getWebcontentNodeLocation();
      Node node = NodeLocation.getNodeByLocation(webcontentNodeLocation);
      ExtendedNode webcontent = (ExtendedNode) node;
      Session session = webcontent.getSession();
      
      String name = event.getRequestContext().getRequestParameter(OBJECTID);
      String nodeOwner = webcontent.getProperty("exo:owner").getString();
      if (name.equals(nodeOwner)) {
        Utils.createPopupMessage(permissionManager, "UIPermissionManagerGrid.msg.no-permission-remove", null, ApplicationMessage.WARNING);
        return;
      }
      if (permissionManager.hasChangePermissionRight(webcontent)) {
        if (webcontent.canAddMixin("exo:privilegeable")) {
        	webcontent.addMixin("exo:privilegeable");
        	webcontent.setPermission(nodeOwner, PermissionType.ALL);
        }
        try {
        	webcontent.removePermission(name);
          session.save();
          permissionManager.updateGrid();
        } catch (AccessControlException e) {
        	Object[] args = {webcontent.getPath()};
        	Utils.createPopupMessage(permissionManager, "UIPermissionManagerGrid.msg.node-locked", args, ApplicationMessage.WARNING);
        	return;
        } catch (AccessDeniedException ace) {
        	Utils.createPopupMessage(permissionManager, "UIPermissionManagerGrid.msg.access-denied", null, ApplicationMessage.WARNING);
          return;
        }
      }      
    }
  }

  /**
   * The listener interface for receiving editAction events.
   * The class that is interested in processing a editAction
   * event implements this interface, and the object created
   * with that class is registered with a component using the
   * component's <code>addEditActionListener<code> method. When
   * the editAction event occurs, that object's appropriate
   * method is invoked.
   * 
   * @see EditActionEvent
   */
  public static class EditActionListener extends EventListener<UIPermissionManager> {

    /* (non-Javadoc)
     * @see org.exoplatform.webui.event.EventListener#execute(org.exoplatform.webui.event.Event)
     */
    public void execute(Event<UIPermissionManager> event) throws Exception {
    	UIPermissionManager permissionManager = event.getSource();
      UIContentDialogForm contentDialogForm = permissionManager.getAncestorOfType(UIPopupContainer.class).getChild(UIContentDialogForm.class);
    	NodeLocation webcontentNodeLocation = contentDialogForm.getWebcontentNodeLocation();
      Node node = NodeLocation.getNodeByLocation(webcontentNodeLocation);
      ExtendedNode webcontent = (ExtendedNode) node;
      String name = event.getRequestContext().getRequestParameter(OBJECTID);

      UIFormInputSetWithAction permissionInputSet = permissionManager.getChildById(PERMISSION_INPUT_SET);
      permissionInputSet.getUIStringInput(PERMISSION_STRING_INPUT).setValue(name);
      String owner = node.getProperty("exo:owner").getString();
      if (name.equals(owner)) {
      	permissionManager.getUIFormCheckBoxInput(ACCESSIBLE_CHECKBOX_INPUT).setChecked(true);
      	permissionManager.getUIFormCheckBoxInput(EDITABLE_CHECKBOX_INPUT).setChecked(true);
      	permissionManager.setActions(new String[] {"Clear"});
      	permissionInputSet.setActionInfo(PERMISSION_STRING_INPUT, null);
      } else {
        List<AccessControlEntry> permsList = webcontent.getACL().getPermissionEntries();
        StringBuilder userPermission = new StringBuilder();
        for (AccessControlEntry accessControlEntry : permsList) {
          if (name.equals(accessControlEntry.getIdentity())) {
            userPermission.append(accessControlEntry.getPermission()).append(" ");
          }
        }
        int numPermission = 0;
        for (String perm : PermissionType.ALL) {
          if (userPermission.toString().contains(perm))
            numPermission++;
        }
        if (numPermission == PermissionType.ALL.length) {
        	permissionManager.getUIFormCheckBoxInput(ACCESSIBLE_CHECKBOX_INPUT).setChecked(true);
        	permissionManager.getUIFormCheckBoxInput(EDITABLE_CHECKBOX_INPUT).setChecked(true);
        } else {
        	permissionManager.getUIFormCheckBoxInput(ACCESSIBLE_CHECKBOX_INPUT).setChecked(true);
        	permissionManager.getUIFormCheckBoxInput(EDITABLE_CHECKBOX_INPUT).setChecked(false);
        }
        permissionManager.setActions(new String[] {"Save", "Clear"});
        permissionInputSet.setActionInfo(PERMISSION_STRING_INPUT, new String[] {"SelectUser", "SelectMember", "AddAny"});
      }
    }
  }

  /**
  * The listener interface for receiving saveAction events.
  * The class that is interested in processing a saveAction
  * event implements this interface, and the object created
  * with that class is registered with a component using the
  * component's <code>addSaveActionListener<code> method. When
  * the saveAction event occurs, that object's appropriate
  * method is invoked.
  * 
  * @see SaveActionEvent
  */
  public static class SaveActionListener extends EventListener<UIPermissionManager> {
  
   /* (non-Javadoc)
    * @see org.exoplatform.webui.event.EventListener#execute(org.exoplatform.webui.event.Event)
    */
   public void execute(Event<UIPermissionManager> event) throws Exception {
  	 UIPermissionManager permissionManager = event.getSource();
  	 UIContentDialogForm contentDialogForm = permissionManager.getAncestorOfType(UIPopupContainer.class).getChild(UIContentDialogForm.class);
  	 NodeLocation webcontentNodeLocation = contentDialogForm.getWebcontentNodeLocation();
     Node node = NodeLocation.getNodeByLocation(webcontentNodeLocation);
     ExtendedNode webcontent = (ExtendedNode) node;
     Session session = webcontent.getSession();
     
     UIFormInputSetWithAction formInputSet = permissionManager.getChildById(PERMISSION_INPUT_SET);
     String identity = ((UIFormStringInput) formInputSet.getChildById(PERMISSION_STRING_INPUT)).getValue();
     List<String> permsList = new ArrayList<String>();
     if (!webcontent.isCheckedOut()) {
       Utils.createPopupMessage(permissionManager, "UIPermissionManagerGrid.msg.node-checkedin", null, ApplicationMessage.WARNING);
       return;
     }
     if (permissionManager.getUIFormCheckBoxInput(ACCESSIBLE_CHECKBOX_INPUT).isChecked()) {
       permsList.clear();
       permsList.add(PermissionType.READ);
     }
     if (permissionManager.getUIFormCheckBoxInput(EDITABLE_CHECKBOX_INPUT).isChecked()) {
       permsList.clear();
       for (String perm : PermissionType.ALL)
         permsList.add(perm);
     }
     if (identity == null || identity.trim().length() == 0) {
    	 Utils.createPopupMessage(permissionManager, "UIPermissionManagerGrid.msg.userOrGroup-required", null, ApplicationMessage.WARNING);
       return;
     }
     if (permsList.size() == 0) {
    	 Utils.createPopupMessage(permissionManager, "UIPermissionManagerGrid.msg.checkbox-require", null, ApplicationMessage.WARNING);
       return;
     }
     String[] permsArray = permsList.toArray(new String[permsList.size()]);
     if (webcontent.canAddMixin("exo:privilegeable")) {
    	 webcontent.addMixin("exo:privilegeable");
    	 webcontent.setPermission(webcontent.getProperty("exo:owner").getString(), PermissionType.ALL);
     }
     try {
	    webcontent.setPermission(identity, permsArray);
    } catch (AccessControlException e) {
    	Object[] args = {webcontent.getPath()};
    	Utils.createPopupMessage(permissionManager, "UIPermissionManagerGrid.msg.node-locked", args, ApplicationMessage.WARNING);
    	return;
    }
     session.save();
     
     permissionManager.updateGrid();
     UIFormInputSetWithAction permissionInputSet = permissionManager.getChildById(PERMISSION_INPUT_SET);
     ((UIFormStringInput) permissionInputSet.getChildById(PERMISSION_STRING_INPUT)).setValue("");
     permissionManager.getUIFormCheckBoxInput(ACCESSIBLE_CHECKBOX_INPUT).setChecked(false);
     permissionManager.getUIFormCheckBoxInput(EDITABLE_CHECKBOX_INPUT).setChecked(false);
   }
  }

  
  /**
  * The listener interface for receiving resetAction events.
  * The class that is interested in processing a resetAction
  * event implements this interface, and the object created
  * with that class is registered with a component using the
  * component's <code>addResetActionListener<code> method. When
  * the resetAction event occurs, that object's appropriate
  * method is invoked.
  * 
  * @see ResetActionEvent
  */
  public static class ClearActionListener extends EventListener<UIPermissionManager> {
  
   /* (non-Javadoc)
    * @see org.exoplatform.webui.event.EventListener#execute(org.exoplatform.webui.event.Event)
    */
   public void execute(Event<UIPermissionManager> event) throws Exception {
  	 UIPermissionManager permissionManager = event.getSource();
     UIFormInputSetWithAction permissionInputSet = permissionManager.getChildById(PERMISSION_INPUT_SET);
     ((UIFormStringInput) permissionInputSet.getChildById(PERMISSION_STRING_INPUT)).setValue("");
     permissionManager.getUIFormCheckBoxInput(ACCESSIBLE_CHECKBOX_INPUT).setChecked(false);
     permissionManager.getUIFormCheckBoxInput(EDITABLE_CHECKBOX_INPUT).setChecked(false);
     permissionManager.setActions(new String[] {"Save", "Clear"});
     permissionInputSet.setActionInfo(PERMISSION_STRING_INPUT, new String[] {"SelectUser", "SelectMember", "AddAny"});
   }
  }

  /**
  * The listener interface for receiving selectUserAction events.
  * The class that is interested in processing a selectUserAction
  * event implements this interface, and the object created
  * with that class is registered with a component using the
  * component's <code>addSelectUserActionListener<code> method. When
  * the selectUserAction event occurs, that object's appropriate
  * method is invoked.
  * 
  * @see SelectUserActionEvent
  */
  public static class SelectUserActionListener extends EventListener<UIPermissionManager> {
  
   /* (non-Javadoc)
    * @see org.exoplatform.webui.event.EventListener#execute(org.exoplatform.webui.event.Event)
    */
   public void execute(Event<UIPermissionManager> event) throws Exception {
  	 UIPermissionManager permissionManager = event.getSource();
  	 UIUserContainer userContainer = permissionManager.createUIComponent(UIUserContainer.class, null, null);
  	 userContainer.setSelectable(permissionManager);
  	 userContainer.setSourceComponent(PERMISSION_STRING_INPUT);
  	 Utils.createPopupWindow(permissionManager, userContainer, USER_SELECTOR_POPUP_WINDOW, 740);
  	 permissionManager.setPopupId(USER_SELECTOR_POPUP_WINDOW);
   }
  }
  
  /**
  * The listener interface for receiving selectMemberAction events.
  * The class that is interested in processing a selectMemberAction
  * event implements this interface, and the object created
  * with that class is registered with a component using the
  * component's <code>addSelectMemberActionListener<code> method. When
  * the selectMemberAction event occurs, that object's appropriate
  * method is invoked.
  * 
  * @see SelectMemberActionEvent
  */
  public static class SelectMemberActionListener extends EventListener<UIPermissionManager> {
  
  	/* (non-Javadoc)
  	 * @see org.exoplatform.webui.event.EventListener#execute(org.exoplatform.webui.event.Event)
  	 */
  	public void execute(Event<UIPermissionManager> event) throws Exception {
  		UIPermissionManager permissionManager = event.getSource();
  		UIGroupMemberSelector groupContainer = permissionManager.createUIComponent(UIGroupMemberSelector.class, null, null);
  		groupContainer.setShowAnyPermission(false);
  		groupContainer.setSourceComponent(permissionManager, new String[] {PERMISSION_STRING_INPUT});
  		Utils.createPopupWindow(permissionManager, groupContainer, GROUP_SELECTOR_POPUP_WINDOW, 600);
  		permissionManager.setPopupId(GROUP_SELECTOR_POPUP_WINDOW);
  	}
  }
  
  /**
   * The listener interface for receiving addAnyAction events.
   * The class that is interested in processing a addAnyAction
   * event implements this interface, and the object created
   * with that class is registered with a component using the
   * component's <code>addAddAnyActionListener<code> method. When
   * the addAnyAction event occurs, that object's appropriate
   * method is invoked.
   * 
   * @see AddAnyActionEvent
   */
  public static class AddAnyActionListener extends EventListener<UIPermissionManager> {
  
    /* (non-Javadoc)
     * @see org.exoplatform.webui.event.EventListener#execute(org.exoplatform.webui.event.Event)
     */
  	public void execute(Event<UIPermissionManager> event) throws Exception {
  		UIPermissionManager permissionManager = event.getSource();
      UIFormInputSetWithAction permisionInputSet = permissionManager.getChildById(PERMISSION_INPUT_SET);
      ((UIFormStringInput) permisionInputSet.getChildById(PERMISSION_STRING_INPUT)).setValue(SystemIdentity.ANY);
      permissionManager.getUIFormCheckBoxInput(ACCESSIBLE_CHECKBOX_INPUT).setChecked(true);
      permissionManager.getUIFormCheckBoxInput(EDITABLE_CHECKBOX_INPUT).setChecked(false);
    }
  }

}