/*******************************************************************************
 * Copyright © 2004 VHA. All rights reserved
 ******************************************************************************/
package gov.va.med.esr.common.model.security;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.acegisecurity.GrantedAuthorityImpl;

import org.apache.commons.lang.Validate;
import org.apache.commons.lang.builder.ToStringBuilder;

import gov.va.med.fw.model.UserPrincipalImpl;
import gov.va.med.fw.model.lookup.Lookup;
import gov.va.med.fw.security.RolePrincipal;

import gov.va.med.esr.common.model.lookup.Capability;
import gov.va.med.esr.common.model.lookup.FunctionalGroup;
import gov.va.med.esr.common.model.lookup.VAFacility;

/**
 * ESRUserPrincipal implementation.
 * 
 * @author DNS   MANSOG
 * @version 1.0
 */
public class ESRUserPrincipalImpl extends UserPrincipalImpl implements ESRUserPrincipal, Comparable, Lookup {

	/**
	 * An instance of serialVersionUID
	 */
	private static final long serialVersionUID = -3232693004167806247L;

	private String fullName;

	private Date lockDate;

	private Date lastAccessDate;

	private Date inActiveDate;

	private Date initialLoginDate;

	private Date passwordChangeDate;

	private int failAttemptCount;

	private boolean passwordChangeWarningDays;

	private int daysRemainingToExpirePassword;

	private VAFacility facility;

	private String jobTitle;

	private String agreementSignatureCode;

	private Boolean signatureVerified = null;

	private Date passwordCreateDate;

	private Date passwordSuspendDate;

	private Date passwordExpireDate;

	private FunctionalGroup functionalGroup;

	private UserLastLoginInfo userLastLoginInfo = null;

	private Set internalUserRoles = null;

	private Set internalCapabilitySets = null;

	private Set internalCapabilities = null;

	public ESRUserPrincipalImpl() {
		super();
	}

	public ESRUserPrincipalImpl(String name) {
		super(name);
	}

	public ESRUserPrincipalImpl(String name, String password) {
		super(name, password);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see gov.va.med.esr.common.model.security.UserPrincipal#getFullName()
	 */
	public String getFullName() {
		if (fullName == null) {
			String givenName = getGivenName() == null ? "" : getGivenName();
			String familyName = getFamilyName() == null ? "" : getFamilyName();
			fullName = givenName + " " + familyName;
		}
		return fullName;
	}

	public void setFullName(String fullName) {
		this.fullName = fullName;
	}

	/**
	 * Returns User login id for lookups
	 */
	public String getCode() {
		return getName();
	}

	/**
	 * returns full name as description for lookups
	 */
	public String getDescription() {
		return getFullName();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see gov.va.med.fw.security.UserPrincipal#getFailAttemptCount()
	 */
	public int getFailAttemptCount() {
		return failAttemptCount;
	}

	/**
	 * @param failAttemptCount
	 */
	public void setFailAttemptCount(int failAttemptCount) {
		this.failAttemptCount = failAttemptCount;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see gov.va.med.fw.security.UserPrincipal#getLastAccessDate()
	 */
	public Date getLastAccessDate() {
		return lastAccessDate;
	}

	/**
	 * @param lastAccessDate
	 */
	public void setLastAccessDate(Date lastAccessDate) {
		this.lastAccessDate = lastAccessDate;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see gov.va.med.fw.security.UserPrincipal#getLockDate()
	 */
	public Date getLockDate() {
		return lockDate;
	}

	/**
	 * @param lockDate
	 */
	public void setLockDate(Date lockDate) {
		this.lockDate = lockDate;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see gov.va.med.fw.security.UserPrincipal#getInActiveDate()
	 */
	public Date getInActiveDate() {
		return inActiveDate;
	}

	/**
	 * @param inActiveDate
	 */
	public void setInActiveDate(Date inActiveDate) {
		this.inActiveDate = inActiveDate;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see gov.va.med.fw.security.UserPrincipal#getInitialLoginDate()
	 */
	public Date getInitialLoginDate() {
		return initialLoginDate;
	}

	/**
	 * @param initialLoginDate
	 */
	public void setInitialLoginDate(Date initialLoginDate) {
		this.initialLoginDate = initialLoginDate;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see gov.va.med.fw.security.UserPrincipal#getPasswordChangeDate()
	 */
	public Date getPasswordChangeDate() {
		return passwordChangeDate;
	}

	/**
	 * @param passwordChangeDate
	 */
	public void setPasswordChangeDate(Date passwordChangeDate) {
		this.passwordChangeDate = passwordChangeDate;
	}

	public boolean isPasswordChangeWarningDays() {
		return passwordChangeWarningDays;
	}

	public void setPasswordChangeWarningDays(boolean passwordChangeWarningDays) {
		this.passwordChangeWarningDays = passwordChangeWarningDays;
	}

	public int getDaysRemainingToExpirePassword() {
		return daysRemainingToExpirePassword;
	}

	public void setDaysRemainingToExpirePassword(
			int daysRemainingToExpirePassword) {
		this.daysRemainingToExpirePassword = daysRemainingToExpirePassword;
	}

	public String getAgreementSignatureCode() {
		return agreementSignatureCode;
	}

	public void setAgreementSignatureCode(String agreementSignatureCode) {
		this.agreementSignatureCode = agreementSignatureCode;
	}

	public VAFacility getFacility() {
		return facility;
	}

	public void setFacility(VAFacility facility) {
		this.facility = facility;
	}

	public String getJobTitle() {
		return jobTitle;
	}

	public void setJobTitle(String jobTitle) {
		this.jobTitle = jobTitle;
	}

	public Date getPasswordCreateDate() {
		return passwordCreateDate;
	}

	public void setPasswordCreateDate(Date passwordCreateDate) {
		this.passwordCreateDate = passwordCreateDate;
	}

	public Boolean getSignatureVerified() {
		return signatureVerified;
	}

	public void setSignatureVerified(Boolean signatureVerified) {
		this.signatureVerified = signatureVerified;
	}

	public FunctionalGroup getFunctionalGroup() {
		return functionalGroup;
	}

	public void setFunctionalGroup(FunctionalGroup functionalGroup) {
		this.functionalGroup = functionalGroup;
	}

	public UserLastLoginInfo getUserLastLoginInfo() {
		return userLastLoginInfo;
	}

	public void setUserLastLoginInfo(UserLastLoginInfo userLastLoginInfo) {
		this.userLastLoginInfo = userLastLoginInfo;
	}

	public Date getFailedLoginDate() {
		return userLastLoginInfo == null ? null : userLastLoginInfo
				.getFailedLoginDate();
	}

	public Date getSuccessfulLoginDate() {
		return userLastLoginInfo == null ? null : userLastLoginInfo
				.getSuccessfulLoginDate();
	}

	public Date getPasswordSuspendDate() {
		return passwordSuspendDate;
	}

	public void setPasswordSuspendDate(Date passwordSuspendDate) {
		this.passwordSuspendDate = passwordSuspendDate;
	}

	public void setPasswordExpireDate(Date passwordExpireDate) {
		this.passwordExpireDate = passwordExpireDate;
	}

	public Date getPasswordExpireDate() {
		return passwordExpireDate;
	}

	public boolean isCapabilityAssigned(Capability capability) {
		return getCapabilities().contains(capability) ? true : false;
	}

	public boolean isCapabilitySetAssigned(CapabilitySet capabilitySet) {
		return getCapabilitySets().contains(capabilitySet) ? true : false;
	}

	public boolean isRoleAssigned(RolePrincipal rolePrincipal) {
		return findUserRole(rolePrincipal) == null ? false : true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see gov.va.med.fw.security.UserPrincipal#getUserRoles()
	 */
	public Set getUserRoles() {
		Set roles = new HashSet();
		for (Iterator iter = getInternalUserRoles().iterator(); iter.hasNext();) {
			UserRole userRole = (UserRole) iter.next();
			RolePrincipal rolePrincipal = userRole.getRolePrincipal();
			if (rolePrincipal != null) {
				roles.add(rolePrincipal);
			}
		}
		return roles;
	}

	/**
	 * Helper method to add the RolePrincipal to a this user.
	 * 
	 * @param usersRole
	 */
	public void addUserRole(RolePrincipal rolePrincipal) {
		Validate.notNull(rolePrincipal, "Can not add null RolePrincipal");
		if (findUserRole(rolePrincipal) == null) {
			UserRole userRole = new UserRole(this, rolePrincipal);
			getInternalUserRoles().add(userRole);
		}
	}

	/**
	 * Helper method to remove RolePrincipal.
	 * 
	 * @param rolePrincipal
	 */
	public void removeUserRole(RolePrincipal rolePrincipal) {
		Validate.notNull(rolePrincipal, "Can not remove null RolePrincipal");
		UserRole userRoleToDelete = findUserRole(rolePrincipal);
		if (userRoleToDelete != null) {
			getInternalUserRoles().remove(userRoleToDelete);
		}
	}

	private Set getInternalUserRoles() {
		if (internalUserRoles == null) {
			internalUserRoles = new HashSet();
		}
		return internalUserRoles;
	}

	private void setInternalUserRoles(Set internalUserRoles) {
		this.internalUserRoles = internalUserRoles;
	}

	public Set getCapabilities() {
		Set capabilities = new HashSet();
		for (Iterator i = getInternalCapabilities().iterator(); i.hasNext();) {
			capabilities.add(((UserCapability) i.next()).getCapability());
		}

		return capabilities;
	}
    
    public List getActiveRoles () {
        List activeRoles = new ArrayList ();
        Set assignedRoles = getUserRoles();
        for (Iterator i=assignedRoles.iterator(); i.hasNext();) {
            ESRRolePrincipalImpl role = (ESRRolePrincipalImpl) i.next();
            if (role.isActive()) {
                activeRoles.add(role);
            }
        }
        return activeRoles;
    }
    
	public List getActiveCapabilities() {
		List capabilities = new ArrayList();

		// Check whether the capability is active or not
		for (Iterator i = getInternalCapabilities().iterator(); i.hasNext();) {
			UserCapability userCapability = (UserCapability) i.next();
			if (userCapability.isActive()){					
				capabilities.add(userCapability.getCapability());
			}			
		}

		// Add the capabilities from the active capability sets
		for (Iterator i = getCapabilitySets().iterator(); i.hasNext();) {
			capabilities.addAll(((CapabilitySet) i.next()).getActiveCapabilities());
		}

		// Add the capabilities from the active roles
		for (Iterator i = getUserRoles().iterator(); i.hasNext();) {
			capabilities.addAll(((ESRRolePrincipalImpl) i.next()).getActiveCapabilities());
		}

		return capabilities;
	}

	public Set getCapabilitySets() {
		Set capabilitySets = new HashSet();

		// Add Capabilities from Sets
		for (Iterator i = getInternalCapabilitySets().iterator(); i.hasNext();) {
			capabilitySets.add(((UserCapabilitySet) i.next()).getCapabilitySet());
		}

		return capabilitySets;
	}

	public Set getUserCapabilities() {
		return Collections.unmodifiableSet(getInternalCapabilities());
	}

	public void addCapability(Capability capability, Date activeDate,
			Date inactiveDate) {
		UserCapability userCapability = findUserCapability(capability);
		// create new if not found
		if (userCapability == null) {
			userCapability = new UserCapability(this, capability, activeDate,
					inactiveDate);
			getInternalCapabilities().add(userCapability);
		}
		// update the existing one if already assigned
		else {
			userCapability.setActiveDate(activeDate);
			userCapability.setInactiveDate(inactiveDate);
		}
	}

	public void removeCapability(UserCapability userCapability) {
		getInternalCapabilities().remove(userCapability);
	}

	public void removeCapability(Capability capability) {
		UserCapability userCapability = findUserCapability(capability);
		if (userCapability != null) {
			getInternalCapabilities().remove(userCapability);
		}
	}

	private Set getInternalCapabilities() {
		if (internalCapabilities == null) {
			internalCapabilities = new HashSet();
		}
		return internalCapabilities;
	}

	private void setInternalCapabilities(Set internalCapabilities) {
		this.internalCapabilities = internalCapabilities;
	}

	public Set getUserCapabilitySets() {
		return Collections.unmodifiableSet(getInternalCapabilitySets());
	}

	public void addCapabilitySet(CapabilitySet capabilitySet) {
		if (findUserCapabilitySet(capabilitySet) == null) {
			UserCapabilitySet userCapabilitySet = new UserCapabilitySet(this,
					capabilitySet);
			getInternalCapabilitySets().add(userCapabilitySet);
		}
	}

	public void removeCapabilitySet(CapabilitySet capabilitySet) {
		UserCapabilitySet userCapabilitySet = findUserCapabilitySet(capabilitySet);
		if (userCapabilitySet != null) {
			getInternalCapabilitySets().remove(userCapabilitySet);
		}
	}

	public Set getAuthorities() {
		Set authorities = new HashSet();
		// Add the active Capability Codes for User, Roles, Capability Sets
		for (Iterator i = getActiveCapabilities().iterator(); i.hasNext();) {
			String roleName = ((Capability) i.next()).getCode();
			authorities.add(new GrantedAuthorityImpl(roleName));
		}
        
        //Add roles names as many application functions are defined based on role               
        for (Iterator j=getActiveRoles().iterator(); j.hasNext();) {
            String roleName = ((ESRRolePrincipal) j.next()).getName();
            authorities.add(new GrantedAuthorityImpl(roleName));
        }
		return authorities;
	}

    public int compareTo(Object o) {
        if( o != null && o instanceof ESRUserPrincipalImpl )
            return getFullName().compareTo(( (ESRUserPrincipalImpl)o ).getFullName());
         else
            return 1;
    }
	private Set getInternalCapabilitySets() {
		if (internalCapabilitySets == null) {
			internalCapabilitySets = new HashSet();
		}
		return internalCapabilitySets;
	}

	private void setInternalCapabilitySets(Set internalCapabilitySets) {
		this.internalCapabilitySets = internalCapabilitySets;
	}

	/**
	 * Find a UserRole from a collection that have given RolePrincipal.
	 * 
	 * @param rolePrincipal
	 * @return UserRole
	 */
	private UserRole findUserRole(RolePrincipal rolePrincipal) {
		for (Iterator iter = getInternalUserRoles().iterator(); iter.hasNext();) {
			UserRole userRole = (UserRole) iter.next();
			// compare only ids
			if (rolePrincipal.getEntityKey().getKeyValueAsString()
					.equals(
							userRole.getRolePrincipal().getEntityKey()
									.getKeyValueAsString())) {
				return userRole;
			}
		}
		return null;
	}

	/**
	 * Find Capability from an internal collection of capabilities
	 * 
	 * @param capability
	 * @return
	 */
	public UserCapability findUserCapability(Capability capability) {
		for (Iterator iter = getInternalCapabilities().iterator(); iter.hasNext();) {
			UserCapability userCapability = (UserCapability) iter.next();
			if (capability.equals(userCapability.getCapability())) {
				return userCapability;
			}
		}
		return null;
	}

	private UserCapabilitySet findUserCapabilitySet(CapabilitySet capabilitySet) {
		for (Iterator iter = getInternalCapabilitySets().iterator(); iter
				.hasNext();) {
			UserCapabilitySet userCapabilitySet = (UserCapabilitySet) iter.next();

			// compare only ids
			if (capabilitySet.getEntityKey().getKeyValueAsString().equals(
					userCapabilitySet.getCapabilitySet().getEntityKey()
							.getKeyValueAsString())) {
				return userCapabilitySet;
			}
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#finalize()
	 */
	protected void finalize() throws Throwable {
		super.finalize();

		this.fullName = null;
		this.lockDate = null;
		this.inActiveDate = null;
		this.passwordChangeDate = null;
		this.failAttemptCount = 0;
		this.internalUserRoles = null;
		this.facility = null;
		this.jobTitle = null;
		this.agreementSignatureCode = null;
		this.signatureVerified = null;
		this.passwordCreateDate = null;
		this.functionalGroup = null;
		this.passwordSuspendDate = null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.VHA.fw.model.AbstractEntity#buildToString()
	 */
	protected void buildToString(ToStringBuilder builder) {
		super.buildToString(builder);
		builder.append("inActiveDate", this.inActiveDate);
		builder.append("lockDate", this.lockDate);
		builder.append("failAttemptCount", this.failAttemptCount);
		builder.append("passwordChangeDate", this.passwordChangeDate);
		builder.append("facility", this.facility);
		builder.append("jobTitle", this.jobTitle);
		builder.append("agreementSignatureCode", this.agreementSignatureCode);
		builder.append("signatureVerified", this.signatureVerified);
		builder.append("passwordCreateDate", this.passwordCreateDate);
		builder.append("passwordSuspendDate", this.passwordSuspendDate);
		builder.append("passwordExpireDate", this.passwordExpireDate);
		builder.append("functionalGroup", this.functionalGroup);
	}

}