package net.sourceforge.fenixedu.domain;

import java.lang.ref.SoftReference;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import net.sourceforge.fenixedu.dataTransferObject.comparators.IscteBeanComparator;
import net.sourceforge.fenixedu.domain.exceptions.DomainException;
import net.sourceforge.fenixedu.domain.externalServices.Service;
import net.sourceforge.fenixedu.domain.externalServices.ServiceEntry;
import net.sourceforge.fenixedu.domain.externalServices.ServiceStateEnum;
import net.sourceforge.fenixedu.domain.person.RoleType;

import org.apache.commons.collections.comparators.ComparatorChain;
import org.apache.commons.lang.StringUtils;

import pt.iscte.ci.metadata.ISCTE;
import pt.ist.fenixframework.DomainObject;
import pt.ist.fenixframework.pstm.CommitListener;
import pt.ist.fenixframework.pstm.TopLevelTransaction;

public class LoginAlias extends LoginAlias_Base implements ModifyServiceEntriesMarker {

    private static final String STANDARD_HOME_DIRECTORY_PREFIX = "\\\\areas\\home\\";

    private static final String STANDARD_HOME_DRIVE = "H:";

    public final static Comparator<LoginAlias> COMPARATOR_BY_ALIAS = new ComparatorChain();
    static {
	((ComparatorChain) COMPARATOR_BY_ALIAS).addComparator(new Comparator<LoginAlias>() {
	    public int compare(LoginAlias loginAlias1, LoginAlias loginAlias2) {
		if (loginAlias1 != null && loginAlias1.getLogin() != null && loginAlias1.isInstitutionalAlias()
			&& loginAlias2 != null && loginAlias2.getLogin() != null && loginAlias2.isInstitutionalAlias()) {
		    return 0;
		} else if (loginAlias1 != null && loginAlias1.getLogin() != null && loginAlias1.isInstitutionalAlias()) {
		    return 1;
		} else if (loginAlias2 != null && loginAlias2.getLogin() != null && loginAlias2.isInstitutionalAlias()) {
		    return -1;
		}
		return 0;
	    }
	});
	((ComparatorChain) COMPARATOR_BY_ALIAS).addComparator(new IscteBeanComparator("alias", Collator.getInstance()));
    }

    public static class ServiceEntriesCommitListenner implements CommitListener {

	public void afterCommit(TopLevelTransaction transaction) {
	    // nothing do to here
	}

	public void beforeCommit(TopLevelTransaction transaction) {
	    
	     if (transaction.isWriteTransaction()) {

		List<ModifyServiceEntriesMarker> markedEntries = new ArrayList<ModifyServiceEntriesMarker>();

		for (DomainObject domainObject : transaction.getModifiedObjects()) {
		    if (domainObject instanceof ModifyServiceEntriesMarker) {
			markedEntries.add((ModifyServiceEntriesMarker) domainObject);

		    }
		}

		for (DomainObject domainObject : transaction.getNewObjects()) {
		    if (domainObject instanceof ModifyServiceEntriesMarker) {
			markedEntries.add((ModifyServiceEntriesMarker) domainObject);
		    }
		}

		for (ModifyServiceEntriesMarker objectWithMarker : markedEntries) {
		    addServiceEntries(objectWithMarker);
		}
	    }


	}

	private void addServiceEntries(ModifyServiceEntriesMarker markedClass) {
	    LoginAlias institutionalLoginAlias = markedClass.getInstitutionalLoginAlias();
	    if (institutionalLoginAlias != null) {
		for (final Service service : RootDomainObject.getInstance().getServicesSet()) {
		    institutionalLoginAlias.addServiceEntry(service);
		}
	    }
	}
    }

    public boolean isInstitutionalAlias() {
	return getAlias().equals(this.getLogin().getUserUId());
    }

    public boolean isCandidateAlias() {
	if (!getAlias().endsWith("@iscte.pt") && getLogin().getUser().getPerson().hasRole(RoleType.CANDIDATE)) {
	    return true;
	}
	return false;
    }

    public static void createNewLoginAlias(Login login, String alias) {
	if (!aliasAlreadyExists(alias, login)) {
	    new LoginAlias(login, alias);
	}
    }

    private static boolean aliasAlreadyExists(final String username, final Login login) {
	if (login != null && username != null) {
	    for (final LoginAlias loginAlias : login.getAlias()) {
		if (loginAlias.getAlias().equals(username)) {
		    return true;
		}
	    }
	}
	return false;
    }

    private LoginAlias(Login login, String alias) {
	super();
	checkIfAliasAlreadyExists(alias, login);

	setLogin(login);
	setAlias(alias);
	setRootDomainObject(RootDomainObject.getInstance());
    }

    public void editInstitutionalAlias(String alias) {
	if (isInstitutionalAlias()) {
	    checkIfAliasAlreadyExists(alias, getLogin());
	    setAlias(alias);
	}
    }

    public void editCustomAlias(String alias) {
	if (!isInstitutionalAlias()) {
	    checkIfAliasAlreadyExists(alias, getLogin());
	    setAlias(alias);
	}
    }

    public void delete() {
	for (ServiceEntry serviceEntry : getServiceEntries()) {
	    serviceEntry.delete();
	}
	removeLogin();
	removeRootDomainObject();
	deleteDomainObject();
    }

    @jvstm.cps.ConsistencyPredicate
    protected boolean checkRequiredParameters() {
	return !StringUtils.isEmpty(getAlias());
    }

    @Override
    public void setAlias(String alias) {
	if (alias == null || StringUtils.isEmpty(alias.trim())) {
	    throw new DomainException("error.loginAlias.no.alias");
	}
	if (alias.indexOf(" ") != -1) {
	    throw new DomainException("error.loginAlias.with.spaces");
	}
	super.setAlias(alias);
    }

    private void checkAlias(Login login) {
	if (login != null && login.getInstitutionalLoginAlias() != null) {
	    throw new DomainException("error.institutionalAlias.already.exists");
	}
    }

    private void checkIfAliasAlreadyExists(String username, Login login) {
	if (login != null && username != null) {
	    final LoginAlias loginAlias = readLoginByUsername(username);
	    if (loginAlias != null && loginAlias.getLogin() != login) {
		throw new DomainException("error.alias.already.exists");
	    }
	}
    }

    public static boolean checkIfAlreadyExists(String username, Person person) {
	if (person != null && username != null) {
	    for (LoginAlias loginAlias : RootDomainObject.getInstance().getLoginAlias()) {
		if (username.equals(loginAlias.getAlias()) && !loginAlias.getLogin().getUser().getPerson().equals(person)) {
		    return true;
		}
	    }
	}
	return false;
    }

    /**
     * This map is a temporary solution until DML provides indexed relations.
     * 
     */
    private static final Map<String, SoftReference<LoginAlias>> loginAliasMap = new Hashtable<String, SoftReference<LoginAlias>>();

    public static LoginAlias readLoginByUsername(final String username) {
	// Temporary solution until DML provides indexed relations.
	final String lowerCaseUsername = username.toLowerCase();
	final SoftReference<LoginAlias> loginAliasReference = loginAliasMap.get(lowerCaseUsername);
	if (loginAliasReference != null) {
	    final LoginAlias loginAlias = loginAliasReference.get();
	    try {
		if (loginAlias != null && loginAlias.getRootDomainObject() == RootDomainObject.getInstance()
			&& loginAlias.getLogin() != null && loginAlias.getLogin().hasUsername(lowerCaseUsername)) {
		    return loginAlias;
		} else {
		    loginAliasMap.remove(lowerCaseUsername);
		}
	    } catch (NullPointerException npe) {
		loginAliasMap.remove(lowerCaseUsername);
	    }
	}
	// *** end of hack

	for (final LoginAlias loginAlias : RootDomainObject.getInstance().getLoginAlias()) {
	    // Temporary solution until DML provides indexed relations.
	    final String lowerCaseLoginUsername = loginAlias.getAlias().toLowerCase();
	    if (!loginAliasMap.containsKey(lowerCaseLoginUsername)) {
		loginAliasMap.put(lowerCaseLoginUsername, new SoftReference<LoginAlias>(loginAlias));
	    }
	    // *** end of hack

	    if (lowerCaseLoginUsername.equalsIgnoreCase(lowerCaseUsername)) {
		return loginAlias;
	    }
	}
	return null;
    }

    /*
     * Service methods start
     */

    @ISCTE(author = "Benjamin Sick", comment = "Extension to support synchronization process")
    public void setHomeDrive(String homeDrive, Service myService) {
	if (homeDrive == null) {
	    homeDrive = STANDARD_HOME_DRIVE;
	}
	for (ServiceEntry serviceEntry : getServiceEntries()) {
	    if (serviceEntry.getService().getType().equals(myService.getType())) {
		serviceEntry.setHomeDrive(homeDrive);
	    }
	}
    }

    @ISCTE(author = "Benjamin Sick", comment = "Extension to support synchronization process")
    public void setHomeDirectory(String homeDirectory, Service myService) {
	if (homeDirectory == null) {
	    if (this.getAlias().contains("@")) {
		homeDirectory = STANDARD_HOME_DIRECTORY_PREFIX + this.getAlias().substring(0, this.getAlias().indexOf("@"));
	    } else {
		homeDirectory = STANDARD_HOME_DIRECTORY_PREFIX + this.getAlias();
	    }
	}
	for (ServiceEntry serviceEntry : getServiceEntries()) {
	    if (serviceEntry.getService().getType().equals(myService.getType())) {
		serviceEntry.setHomeDirectory(homeDirectory);
	    }
	}
    }

    @ISCTE(author = "Benjamin Sick", comment = "Extension to support synchronization process")
    public void addServiceEntry(Service myService) {
	if (myService == null) {
	    throw new DomainException("error.person.empty.Service");
	}
	if (!isEntryAlreadyExistingAndUpdateEntriesWithNoExternalIdOrUpToDate(myService, getAlias())) {
	    myService.addNewEntry(this, myService, getAlias());
	}
    }

    @ISCTE(author = "Benjamin Sick", comment = "Extension to support synchronization process")
    private boolean isEntryAlreadyExistingAndUpdateEntriesWithNoExternalIdOrUpToDate(final Service service,
	    final String externalId) {
	for (final ServiceEntry serviceEntry : getServiceEntries()) {
	    if (serviceEntry.getService().equals(service)) {
		if (serviceEntry.getSystemExternalId() == null || !serviceEntry.getSystemExternalId().equals(externalId)) {
		    serviceEntry.setSystemExternalId(externalId);
		}
		if (serviceEntry.getServiceState() != null
			&& serviceEntry.getServiceState().getState().equals(ServiceStateEnum.UP_TO_DATE)) {
		    serviceEntry.setServiceState(ServiceStateEnum.TO_PROCESS);
		}
		return true;
	    }
	}
	return false;
    }

    public LoginAlias getInstitutionalLoginAlias() {
	return this;
    }

    /*
     * Service methods end
     */

}
