/*
 * $Id: EditAclForm.java,v 1.26 2005/09/15 13:43:58 marbobo Exp $
 * 
 * Copyright (c) Siemens AG 2008 
 *           (c) Universität Paderborn 2008 
 * Licensed under EUPL 1.1. 
 * 
 * $Log: EditAclForm.java,v $
 * Revision 1.26  2005/09/15 13:43:58  marbobo
 * + Sprachabhängige Sortierung implementiert
 *
 * Revision 1.25  2005/09/02 11:21:20  marbobo
 * + Bugfix (NPE)
 *
 * Revision 1.24  2005/01/18 16:17:01  jonyp
 * added is AdminUser method
 *
 * Revision 1.23  2004/11/18 10:22:24  jonyp
 * set/getCreated: changed from date to Timestamp
 *
 * Revision 1.22  2004/11/10 15:02:47  koppy
 * Projektweite Überarbeitung von Error-Logs, Stacktraces,..
 *
 * Revision 1.21  2004/10/26 13:40:39  vchris
 * *** empty log message ***
 *
 * Revision 1.20  2004/10/25 14:20:44  vchris
 * *** empty log message ***
 *
 * Revision 1.19  2004/10/22 13:54:18  vchris
 * Erweiterungen LDAP
 *
 * Revision 1.18  2004/07/08 15:13:57  marbobo
 * + Mergen von Acls
 *
 * Revision 1.17  2004/06/27 17:39:28  marbobo
 * + Quellcode maintenance
 *
 * Revision 1.16  2004/05/25 21:08:53  marbobo
 * + getTargetId() setTargetId() getSubTargetId() setSubTargetId()
 *
 * Revision 1.15  2004/03/24 13:10:37  bigmac
 * Handling für Rücksprung in Projektsicht eingefügt
 *
 * Revision 1.14  2004/03/15 10:33:59  bigmac
 * Anpassungen an VKCPermissionSerializer und VKCAclTypeSerializer
 *
 * Revision 1.13  2004/03/13 20:46:14  bigmac
 * Weitere Refactorings ...
 *
 * Revision 1.12  2004/03/13 19:15:11  marbobo
 * Unbenutze lokale Variablen projektweit entsorgt
 *
 * Revision 1.11  2004/03/11 20:17:16  bigmac
 * Anpassungen an VKCUserSerializer
 *
 * Revision 1.10  2004/02/04 15:28:01  bigmac
 * u.U. inkorrekter Kategoriepfad im ACL-Dialog behoben
 *
 * Revision 1.9  2004/01/29 15:35:07  jonyp
 * new getter/setter categoryId
 *
 * Revision 1.8  2003/12/24 10:11:54  bigmac
 * Projektweite Import-Optimierung
 *
 * Revision 1.7  2003/11/19 11:34:10  vchris
 * Änderungen für geändertes Verhalten des DefaultACL Editors
 *
 * Revision 1.6  2003/11/04 16:52:01  vchris
 * *** empty log message ***
 *
 * Revision 1.5  2003/11/04 16:19:44  vchris
 * *** empty log message ***
 *
 * Revision 1.4  2003/10/27 17:00:10  vchris
 * *** empty log message ***
 *
 * 
 */
package vkc.view.form;

import java.text.CollationKey;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.sql.Timestamp;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Vector;

import javax.servlet.http.HttpServletRequest;

import org.apache.struts.Globals;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.util.MessageResources;

import vkc.db.entities.VKCAclEntry;
import vkc.db.entities.VKCAclType;
import vkc.db.entities.VKCCategory;
import vkc.db.entities.VKCPermission;
import vkc.db.entities.VKCUser;
import vkc.db.serializer.VKCAclTypeSerializer;
import vkc.db.serializer.VKCGroupSerializer;
import vkc.db.serializer.VKCPermissionSerializer;
import vkc.db.serializer.VKCUserSerializer;
import vkc.exception.DBException;
import vkc.ldap.db.entities.VKCLdapGroup;
import vkc.ldap.db.entities.VKCLdapServer;
import vkc.ldap.db.serializer.VKCLdapGroupSerializer;
import vkc.ldap.db.serializer.VKCLdapServerSerializer;
import vkc.misc.VKCLogManager;
import vkc.security.AccessorInfo;
import vkc.security.VKCAcl;
import vkc.util.VKCStringUtils;
import vkc.view.PageId;
import vkc.view.WebShare;
import vkc.view.bean.GroupInfo;
import vkc.view.bean.UserInfo;


/**
 * DOCUMENT ME!!!
 * 
 * @author vchris
 */
public abstract class EditAclForm extends ActionForm {

	public static final int ACTION_INIT					= 0;
	public static final int ACTION_SAVE 				= 1;
	public static final int ACTION_DELETE 				= 2;
	public static final int ACTION_ADDACCESSOR 			= 3;
	public static final int ACTION_ADDANYOTHER 			= 4;
	public static final int ACTION_INHERIT_FROM_PARENT	= 5;
	public static final int ACTION_ADDLDAPACCESSOR      = 6;
	public static final int ACTION_CHANGE_SELECT 		= -1;

	public static final int SELECT_NONE					= 1;
	public static final int SELECT_MERGE_TO_SUBCATS		= 2;
	public static final int SELECT_OVERWRITE_TO_SUBCATS	= 3;

	public static final String KEY_FOR_THIS_USER 		= "vkc.acl.for_this_user";
	public static final String KEY_FOR_THIS_GROUP 		= "vkc.acl.for_this_group";
	public static final String KEY_FOR_THIS_LDAP_GROUP  = "vkc.acl.for_this_ldap_group";
	public static final String KEY_FOR_ANYOTHER 		= "vkc.acl.for_anyother";
	public static final String KEY_FOR_OWNER 			= "vkc.acl.for_owner";

	protected Integer sourcePageId = PageId.CATEGORY_VIEW;
	protected Integer targetId = null;
	protected Integer subTargetId = null;
	protected Integer selectionMode = new Integer( SELECT_NONE );

	/**
	 * Variable zur internen Verwaltung der über die JSP Seite ausgewählten Aktion
	 */
	private int ctrlAction = -1;
	private int addAccessorType;
	private int addAccessorId;
	private int ldapServerId;
	private String groupRdn;
	private boolean adminUser = false;
	
	/** 
	 * Speichert den Wert des in der Select-Box ausgewählten Wertes
	 */
	private String accessorSelect;

	private ArrayList users 			= new ArrayList();
	private ArrayList groups 			= new ArrayList();
	private ArrayList ldapGroups		= new ArrayList();
	private ArrayList anyothers 		= new ArrayList();
	
	private VKCAcl acl 					= null;
	private Map permissions 			= null;
	private Map aclTypes				= null;
	private AccessorInfo actualAi		= null;
	private VKCCategory category  		= null;

	/*
	 * Die folgenden Variablen und Methodenmüssen bei Vererbung für spezielle ACL angepasst werden
	 * (Im Default-Konstruktor der Unterklasse)
	 */
	protected String permissionsContext = null;
	protected char[] permTokens = null;
	
	abstract protected void initEnabledPermissions(Map permissions);
	abstract public void setAllCheckboxesToValue(boolean value);
	abstract protected boolean[] getAllCheckboxValues();
	abstract protected void setCheckboxValue(VKCAclEntry c);
	
	/*
	 * 
	 */
	private Integer categoryId = null; 
		
	/*
	 * Die nachfolgenden Methoden müssen nicht überschrieben werden
	 */
	public void setAcl(VKCAcl acl, HttpServletRequest request) throws DBException {
		// Verschiedene Objekte für methodenweite Verwendung holen
		MessageResources mRes 	= (MessageResources)(request.getAttribute(Globals.MESSAGES_KEY));
		Locale locale 			= (Locale)(request.getSession().getAttribute(Globals.LOCALE_KEY));		
		
		// ACL in dieser form speichern
		this.acl = acl;
		
		// Benutzer, Gruppen, LDAPGruppen, und Others in vier ArrayListen mit AccessorInfo Objekten extrahieren
		users.clear();
		groups.clear();
		ldapGroups.clear();				
		anyothers.clear();
		Collection aclEntries = acl.getAclEntries();
		for (Iterator it = aclEntries.iterator(); it.hasNext(); ) {
			VKCAclEntry c = (VKCAclEntry)it.next();
			if (c.getAclType().getAclTypeId().intValue() == VKCAclType.ANYOTHER_TYPE) {
				AccessorInfo ai = new AccessorInfo(
					mRes.getMessage(locale,"vkc.anyother"),
					new Integer(VKCAclType.ANYOTHER_TYPE), 
					c.getAccessorId());
				if (!anyothers.contains(ai)) anyothers.add(ai);
			}
			else if (c.getAclType().getAclTypeId().intValue() == VKCAclType.OWNER_TYPE) {
				AccessorInfo ai = new AccessorInfo(
					mRes.getMessage(locale,"vkc.owner"),
					new Integer(VKCAclType.OWNER_TYPE),
					c.getAccessorId());
				if (!anyothers.contains(ai)) anyothers.add(ai);
			}
			else if (c.getAclType().getAclTypeId().intValue() == VKCAclType.USER_TYPE) {
				try {
					UserInfo userinfo = VKCUserSerializer.retrieveUserInfo(c.getAccessorId());
					AccessorInfo ai = new AccessorInfo(userinfo.getLastnameLabel(), new Integer (VKCAclType.USER_TYPE), c.getAccessorId());
					if (!users.contains(ai)) users.add(ai);
				}
				catch (DBException e) {
					VKCLogManager.errorLog(this,"","Error retrieving userinfo in method setAcl()",e);
				}
			}
			else if (c.getAclType().getAclTypeId().intValue() == VKCAclType.GROUP_TYPE) {
				try {
					GroupInfo groupinfo = VKCGroupSerializer.retrieveGroupInfo(c.getAccessorId());
					AccessorInfo ai = new AccessorInfo(groupinfo.getName(), new Integer(VKCAclType.GROUP_TYPE), c.getAccessorId());
					if (!groups.contains(ai)) groups.add(ai);
				}
				catch(DBException e) {
					VKCLogManager.errorLog(this,"","Error retrieving groupinfo in method setAcl()",e);					
				}
			}
			else if (c.getAclType().getAclTypeId().intValue() == VKCAclType.LDAP_GROUP_TYPE) {
				// TODO: Hier Ldap Group Infos hinterlegen!
				try {
					VKCLdapGroup ldapGroup = VKCLdapGroupSerializer.retrieveLdapGroup(c.getAccessorId());
					VKCLdapServer ldapServer = VKCLdapServerSerializer.retrieveLdapServer(ldapGroup.getLdapServerId());
					
					AccessorInfo ai = new AccessorInfo(ldapServer.getDescription() + ": " + ldapGroup.getLdapGroupRDN(), new Integer(VKCAclType.LDAP_GROUP_TYPE), c.getAccessorId());
					if (!ldapGroups.contains(ai)) ldapGroups.add(ai);
				} catch (DBException e) {
					VKCLogManager.errorLog(this,"","Error retrieving groupinfo in method setAcl()");					
				}
			}
		}

		final Collator collator = Collator.getInstance(locale);

		// sortieren
		Comparator accessorComparator = new Comparator () {
		  public int compare (Object o1, Object o2) {
			AccessorInfo u1 = (AccessorInfo)o1;
			AccessorInfo u2 = (AccessorInfo)o2;
			
        	CollationKey key1 = collator.getCollationKey(VKCStringUtils.decodeXmlEntities(u1.getAccessorName()));
        	CollationKey key2 = collator.getCollationKey(VKCStringUtils.decodeXmlEntities(u2.getAccessorName()));
        	
        	return key1.compareTo(key2);
		  }
		};

		AccessorInfo[] aiUsers = (AccessorInfo[])users.toArray(new AccessorInfo[0]);
		Arrays.sort(aiUsers, accessorComparator);
		List uList = Arrays.asList(aiUsers);
		users.clear();
		users = new ArrayList(uList);

		AccessorInfo[] aiGroups = (AccessorInfo[])groups.toArray(new AccessorInfo[0]);
		Arrays.sort(aiGroups, accessorComparator);
		List gList = Arrays.asList(aiGroups);
		groups.clear();
		groups = new ArrayList(gList);

		// Status der Permissions (enabled/disabled) in lokalen Variablen speichern
		initEnabledPermissions(getPermissions());
	}
	
	/**
	 * @return
	 */
	public VKCAcl getAcl() {
		return acl;
	}

	/**
	 * Liefert des Key für die Anzeige "für diesen Benutzer/User/Jeden/Besitzer" im Dialog 
	 * abhängig vom aktuell ausgewählten Accessor zurück
	 * 
	 * @return accessorTypeKey
	 */
	public String getAccessorTypeKey() {
		
		if (actualAi == null) 	return KEY_FOR_ANYOTHER;		
		Integer type = actualAi.getAccessorTypeId();		
		if (type == null) 		return KEY_FOR_ANYOTHER;
				
		if (type.intValue() == VKCAclType.ANYOTHER_TYPE) 	return KEY_FOR_ANYOTHER;
		if (type.intValue() == VKCAclType.OWNER_TYPE) 		return KEY_FOR_OWNER;
		if (type.intValue() == VKCAclType.USER_TYPE) 		return KEY_FOR_THIS_USER;
		if (type.intValue() == VKCAclType.GROUP_TYPE) 		return KEY_FOR_THIS_GROUP;
		if (type.intValue() == VKCAclType.LDAP_GROUP_TYPE)	return KEY_FOR_THIS_LDAP_GROUP;

		return KEY_FOR_ANYOTHER;
	}		 
		
	/**
	 * Liefert die Permissions im gegebenen Kontext als Map zurück.
	 * Beim ersten Mal wird eine Datenbankabfrage gestartet, bei jedem 
	 * weiteren Aufruf wird auf das im Speicher befindliche Objekt zurückgegriffen.
	 * 
	 * @return permissions
	 */
	protected Map getPermissions() throws DBException {

		if (permissions != null) {
			return permissions;
		} 

		return permissions = VKCPermissionSerializer.getPermissions(permissionsContext);
	}
	
	/**
	 * Liefert die ACL-Types als Map zurück.
	 * Beim ersten Mal wird eine Datenbankabfrage gestartet, bei jedem 
	 * weiteren Aufruf wird auf das im Speicher befindliche Objekt zurückgegriffen.
	 * 
	 * @return
	 */
	private Map getAclTypes() throws DBException {
		
		if (aclTypes != null) {
			return aclTypes;
		} 

		return aclTypes = VKCAclTypeSerializer.retrieveAclTypes();
	}
	
	/**
	 * Aktualisiert die ACL gemäß des Werte der Form
	 * 
	 * @param request
	 * @param acl
	 */
	public void updateAcl(HttpServletRequest request, VKCAcl acl) 
	throws DBException {
		
		// User-Objekt holen (benötigt als Creator der ACL)
		VKCUser aclCreator = (VKCUser)request.getSession().getAttribute(WebShare.USER_KEY);
		permissions = getPermissions();
		aclTypes = getAclTypes();
		
		// Falls keine ACL übergeben, Verarbeitung hier beenden
		if (acl == null) return;
		if (actualAi == null) return;
		
		Vector aclEntries = acl.getAclEntries();
		// Falls ACL keine Einträge enthält, Verarbeitung hier beenden
		if (aclEntries.size() == 0) return;
		
		boolean gefunden = false;
		int i = 0;
		VKCAclEntry c = new VKCAclEntry();
		
		while (!gefunden && i < aclEntries.size()) {
			// aktuellen Eintrag wiederfinden
			c = (VKCAclEntry) aclEntries.elementAt(i);
			if (c.getAccessorId().equals(actualAi.getAccessorId()) &&
				c.getAclType().getAclTypeId().equals(actualAi.getAccessorTypeId())) gefunden = true;
			i++;
		}
		
		boolean[] newPerms = getAllCheckboxValues();

		for (int pindex = 0; pindex < permTokens.length; pindex++)
		{
			VKCAclEntry newCatAcl = new VKCAclEntry();
			newCatAcl.setAccessorId(actualAi.getAccessorId());
			newCatAcl.setAccessorId(c.getAccessorId());
			newCatAcl.setAclId(c.getAclId());
			newCatAcl.setAclType(c.getAclType());
			newCatAcl.setPermission((VKCPermission)(permissions.get(String.valueOf(permTokens[pindex]))));
			boolean enthalten = acl.contains(newCatAcl);
			// In Acl enthalten, aber checked="false" --> Entfernen!
			if (enthalten && !newPerms[pindex]) acl.deleteAclEntry ( newCatAcl );				
			// In Acl nicht enthalten und checked="true" --> Hinzufügen!
			if (!enthalten && newPerms[pindex]) {
				newCatAcl.setCreated(new Timestamp(System.currentTimeMillis()));
				newCatAcl.setCreatorId(aclCreator.getUserId());
				try
				{
					acl.addAclEntry( newCatAcl );				
				}
				catch (Exception ex) {}
			}
		}
	}	
	
	/**
	 * Überprüft, ob es in der übergebenen AccessorListe einen Accessor
	 * vom Typ "type" mit AccessorId "id" gibt. 
	 * 
	 * @param arrayList
	 * @param type
	 * @param id
	 * @return
	 */
	private AccessorInfo searchForAccessor(ArrayList accessorList, String type, Integer id) {
		Iterator it = accessorList.iterator();
		while (it.hasNext()) {
			AccessorInfo c = (AccessorInfo)it.next();
			if (c.getAccessorType().equals(type) && c.getAccessorId().equals(id)) return c;
		}
		return null;
	}
	
	public void locateEntry(String idString, boolean setCheckboxes) {
		
		Vector v = VKCStringUtils.stringToVector(idString, "_");
		
		String type;
		Integer id;
		AccessorInfo ai = null;
		actualAi = null;
		
		if (v.isEmpty()) {
			return;
		}
		
		try {
			type = (String)v.elementAt(0);
			id = new Integer ( Integer.parseInt( (String)v.elementAt(1) ) );
		} catch (Exception ex) {
			VKCLogManager.errorLog(this,"","Could not resolve Accessor Type oder ID from ID-String.",ex);
			
			return;
		}
	
		// Die ArrayListen users, groups, und anyothers nach dem Accessor durchsuchen
		ai = searchForAccessor(anyothers, type, id);
		if (ai == null) ai = searchForAccessor(users, type, id);
		if (ai == null) ai = searchForAccessor(groups, type, id);
		if (ai == null) ai = searchForAccessor(ldapGroups, type, id);

		// den gefundenen Accessor als aktuellen Accessor merken 
		actualAi = ai;
					
		// die Checkboxen entsprechend setzen
		if (setCheckboxes) setAllCheckboxesToValue(false);
		
		VKCAclEntry c = null;
		boolean gefunden = false;
		if (ai != null) {
			// ... nur ausführen, falls der Accessor auch gefunden wurde
			Iterator it = acl.getAclEntries().iterator();
			while (it.hasNext()) {
				c = (VKCAclEntry)it.next();
				if (c.getAccessorId().equals(ai.getAccessorId()) && c.getAclType().getAclTypeId().equals(ai.getAccessorTypeId())) {
					
					/*
					// AccessorId und TypeId merken
					actualAccessorId  = c.getAccessorId();
					actualTypeId = c.getAclType().getAclTypeId();
					*/
					
					gefunden = true;
					accessorSelect = idString;
					if (setCheckboxes) setCheckboxValue(c);
				}
			}
		}
		if (!gefunden || ai == null) VKCLogManager.errorLog(this,"","ACL Entry not found.");
	}
	
	public String getAclCreator() {
		return null;
	}
	
	/**
	 * @return
	 */
	public String getAccessorSelect() {
		return accessorSelect;
	}

	/**
	 * @return
	 */
	public ArrayList getAnyothers() {
		return anyothers;
	}

	/**
	 * @return
	 */
	public int getCtrlAction() {
		return ctrlAction;
	}

	/**
	 * @return
	 */
	public ArrayList getGroups() {
		return groups;
	}

	/**
	 * @return
	 */
	public ArrayList getUsers() {
		return users;
	}

	/**
	 * @param string
	 */
	public void setAccessorSelect(String string) {
		accessorSelect = string;
	}

	/**
	 * @param i
	 */
	public void setCtrlAction(int i) {
		ctrlAction = i;
	}

	/**
	 * @return
	 */
	public AccessorInfo getActualAi() {
		return actualAi;
	}

	/**
	 * @param info
	 */
	public void setActualAi(AccessorInfo info) {
		actualAi = info;
	}
	
	/**
	 * @param id
	 */
	public void setActualAccessorId(Integer id) {
		if (actualAi != null) actualAi.setAccessorId(id);
	}

	/**
	 * @param id
	 */
	public void setActualTypeId(Integer id) {
		if (actualAi != null) actualAi.setAccessorTypeId(id);
	}
	
	/**
	 * @return
	 */
	public Integer getActualTypeId() {
		if (actualAi != null) return actualAi.getAccessorTypeId();
		return null;
	}

	public String getActualAccessorType() {
		if (actualAi == null) return null;
		if (actualAi.getAccessorTypeId().intValue() == VKCAclType.OWNER_TYPE) return "vkc.acl.for_owner";
		if (actualAi.getAccessorTypeId().intValue() == VKCAclType.USER_TYPE) return "vkc.acl.for_this_user";
		if (actualAi.getAccessorTypeId().intValue() == VKCAclType.GROUP_TYPE) return "vkc.acl.for_this_group";
		if (actualAi.getAccessorTypeId().intValue() == VKCAclType.LDAP_GROUP_TYPE) return "vkc.acl.for_this_ldap_group";
		return "vkc.acl.for_anyother";
	}
	
	/**
	 * @return
	 */
	public Integer getActualAccessorId() {
		if (actualAi != null) return actualAi.getAccessorId();
		return null;
	}
	
	/**
	 * @return
	 */
	public int getAddAccessorId() {
		return addAccessorId;
	}

	/**
	 * @return
	 */
	public int getAddAccessorType() {
		return addAccessorType;
	}

	/**
	 * @param i
	 */
	public void setAddAccessorId(int i) {
		addAccessorId = i;
	}

	/**
	 * @param i
	 */
	public void setAddAccessorType(int i) {
		addAccessorType = i;
	}

	/**
	 * @return
	 */
	public Integer getCategoryId() {
		return categoryId;
	}

	/**
	 * @param integer
	 */
	public void setCategoryId(Integer integer) {
		categoryId = integer;
	}

	public VKCCategory getCategory() {
		return category;
	}

	public void setCategory(VKCCategory category) {
		this.category = category;
	}

	public Integer getSourcePageId() {
		return sourcePageId;
	}

	public void setSourcePageId(Integer integer) {
		sourcePageId = integer;
	}
	

	public void reset(ActionMapping mapping, HttpServletRequest request)
	{
		super.reset( mapping, request );
		
		sourcePageId = PageId.CATEGORY_VIEW;
		targetId = null;
		subTargetId = null;
		selectionMode = new Integer( SELECT_NONE );
	}
	
	public Integer getSubTargetId() {
		return subTargetId;
	}

	public Integer getTargetId() {
		return targetId;
	}

	public void setSubTargetId(Integer integer) {
		subTargetId = integer;
	}

	public void setTargetId(Integer integer) {
		targetId = integer;
	}

	public Integer getSelectionMode() {
        return selectionMode;
    }
    
	public void setSelectionMode( Integer i ) {
        selectionMode = i;
    }

	public String getGroupRdn() {
		return groupRdn;
	}

	public int getLdapServerId() {
		return ldapServerId;
	}

	public void setGroupRdn(String string) {
		groupRdn = string;
	}

	public void setLdapServerId(int i) {
		ldapServerId = i;
	}

	public ArrayList getLdapGroups() {
		return ldapGroups;
	}

    public boolean isAdminUser() {
        return adminUser;
    }
    public void setAdminUser(boolean adminUser) {
        this.adminUser = adminUser;
    }
}