/* ======================================================================
   Parts Copyright 2006 University of Leeds, Oxford University, University of the Highlands and Islands.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

====================================================================== */

package org.bodington.server.userimport.oucsir;

import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.apache.log4j.Logger;
import org.bodington.server.BuildingServerException;
import org.bodington.server.realm.Alias;
import org.bodington.server.realm.AliasEntry;
import org.bodington.server.realm.Group;
import org.bodington.server.realm.User;
import org.bodington.server.realm.WebAuthUser;
import org.bodington.server.realm.Zone;
import org.bodington.server.userimport.GeneratedUser;
import org.bodington.server.userimport.NotInitialisedException;
import org.bodington.server.userimport.UserCreationException;
import org.bodington.util.TextUtils;

class OUCSGeneratedUser implements GeneratedUser, Validator
{
    
    private static final Logger log = Logger.getLogger(OUCSGeneratedUser.class); 
    
    private String surname;
    private String displayName;
    private OUCSCollege college;
    private OUCSDepartment department;
    private String email;
    private String cardId;
    private String OSSId;
    private String personCode;
    private String barcode;
    private OUCSCourse course;
    private String username;
    private Integer courseYear;
    private OUCSStatus status;
    private String initials;
    
    private User user;
    private AliasEntry personCodeEntry;
    private WebAuthUser waUser;
    private Zone zone;
    private Alias personCodeAlias;

    private Collection groups;

    /**
     * @param personCodeAlias the personCodeAlias to set
     */
    void setPersonCodeAlias(Alias personCodeAlias)
    {
        this.personCodeAlias = personCodeAlias;
    }

    /**
     * @param zone the zone to set
     */
    void setZone(Zone zone)
    {
        this.zone = zone;
    }

    /**
     * @return the barcode
     */
    String getBarcode()
    {
        return barcode;
    }

    /**
     * @param barcode the barcode to set
     */
    void setBarcode(String barcode)
    {
        if (this.barcode != null)
            throw new IllegalStateException("barcode already set");
        this.barcode = barcode;
    }

    /**
     * @return the cardId
     */
    String getCardId()
    {
        return cardId;
    }

    /**
     * @param cardId the cardId to set
     */
    void setCardId(String cardId)
    {
        if (this.cardId != null)
            throw new IllegalStateException("cardId already set");
        this.cardId = cardId;
    }

    /**
     * @return the college
     */
    OUCSCollege getCollege()
    {
        return college;
    }

    /**
     * @param college the college to set
     */
    void setCollege(OUCSCollege college)
    {
        if (this.college != null)
            throw new IllegalStateException("college already set");
        this.college = college;
    }

    /**
     * @return the course
     */
    OUCSCourse getCourse()
    {
        return course;
    }

    /**
     * @param course the course to set
     */
    void setCourse(OUCSCourse course)
    {
        if (this.course != null)
            throw new IllegalStateException("course already set");
        this.course = course;
    }

    /**
     * @return the courseYear
     */
    int getCourseYear()
    {
        if (courseYear == null)
            return 0;
        return courseYear.intValue();
    }

    /**
     * @param courseYear the courseYear to set
     */
    void setCourseYear(int courseYear)
    {
        if (this.courseYear != null)
            throw new IllegalStateException("courseYear already set");
        this.courseYear = new Integer(courseYear);
    }

    /**
     * @return the department
     */
    OUCSDepartment getDepartment()
    {
        return department;
    }

    /**
     * @param department the department to set
     */
    void setDepartment(OUCSDepartment department)
    {
        if (this.department != null)
            throw new IllegalStateException("department already set");
        this.department = department;
    }

    /**
     * @return the displayName
     */
    String getDisplayName()
    {
        return displayName;
    }

    /**
     * @param displayName the displayName to set
     */
    void setDisplayName(String displayName)
    {
        if (this.displayName != null)
            throw new IllegalStateException("displayName already set");
        this.displayName = displayName;
    }

    /**
     * @return the email
     */
    String getEmail()
    {
        return email;
    }

    /**
     * @param email the email to set
     */
    void setEmail(String email)
    {
        if (this.email != null)
            throw new IllegalStateException("email already set");
        this.email = email;
    }

    /**
     * @return the oSSId
     */
    String getOSSId()
    {
        return this.OSSId;
    }

    /**
     * @param OSSId the oSSId to set
     */
    void setOSSId(String OSSId)
    {
        if (this.OSSId != null)
            throw new IllegalStateException("OSSId already set");
        this.OSSId = OSSId;
    }

    /**
     * @return the personCode
     */
    String getPersonCode()
    {
        return personCode;
    }

    /**
     * @param personCode the personCode to set
     */
    void setPersonCode(String personCode)
    {
        if (this.personCode != null)
            throw new IllegalStateException("personCode already set");
        this.personCode = personCode;
    }

    /**
     * @return the status
     */
    OUCSStatus getStatus()
    {
        return status;
    }

    /**
     * @param status the status to set
     */
    void setStatus(OUCSStatus status)
    {
        if (this.status != null)
            throw new IllegalStateException("status already set");
        this.status = status;
    }

    /**
     * @return the surname
     */
    String getSurname()
    {
        return surname;
    }

    /**
     * @param surname the surname to set
     */
    void setSurname(String surname)
    {
        if (this.surname != null)
            throw new IllegalStateException("surname already set");
        this.surname = surname;
    }

    /**
     * @return the username
     */
    String getUsername()
    {
        return username;
    }

    /**
     * @param username the username to set
     */
    void setUsername(String username)
    {
        if (this.username != null)
            throw new IllegalStateException("username already set");
        this.username = username;
    }

    /**
     * @return the initials
     */
    String getInitials()
    {
        return initials;
    }

    /**
     * @param initials the initials to set
     */
    void setInitials(String initials)
    {
        if (this.initials != null)
            throw new IllegalStateException("initials already set");
        this.initials = initials;
    }
    
    public void addToGroups(Collection newGroups)
        throws NotInitialisedException
    {
        if(user == null)
            throw new NotInitialisedException("User "+getUniqueID()+ 
                " is not loaded.");

        StringBuffer sb = new StringBuffer();
        for(Iterator i = newGroups.iterator(); i.hasNext();) {
            Group group = (Group)i.next();
            if(!user.isMember(group) && user.addToGroup(group)) {
                sb.append(group.getName())
                    .append(" ");
            }
        }
        if(sb.length() > 0) {
            sb.delete(sb.length()-1, sb.length());
            log.info("Added user "+getUniqueID()+" to the following groups: "+sb);
        }
    }

    public void create() throws UserCreationException
    {
        user = new User();
        user.setZone(zone);
        waUser = new WebAuthUser();
        personCodeEntry = createAliasEntry(personCodeAlias);
    }

    public String getUniqueID()
    {
        return username;
    }
    
    public boolean load() throws BuildingServerException
    {
        waUser = WebAuthUser.findWebAuthUserByUserName(username);
        if (waUser == null)
        {
            user = loadUser(personCodeAlias, personCode);
            if (user == null)
                return false;
            waUser = WebAuthUser.findWebAuthUser(user);
            if (waUser == null)
                waUser = new WebAuthUser();
        }
        else
            user = User.findUser(waUser.getUserId());
        
        if (user == null)
            return false;
        
        personCodeEntry = loadAliasEntry(personCodeAlias, user);
        
        return true;
    }
    
    private User loadUser(Alias alias, String userAlias)
        throws BuildingServerException
    {
        if (userAlias != null)
        {
            AliasEntry entry = AliasEntry.findAliasEntry(alias, userAlias);
            if (entry != null)
                return User.findUser(entry.getUserId());
        }
        return null;
    }
    
    private AliasEntry loadAliasEntry(Alias alias, User user)
        throws BuildingServerException
    {
        AliasEntry aliasEntry = AliasEntry.findAliasEntry(alias, user);
        if (aliasEntry == null)
        {
            aliasEntry = createAliasEntry(alias);
        }
        return aliasEntry;
    }
    
    private AliasEntry createAliasEntry(Alias alias)
    {
        AliasEntry aliasEntry = new AliasEntry();
        aliasEntry.setAlias(alias);
        return aliasEntry;
    }

    public String problems()
    {
        List errors = new LinkedList();
        if (surname == null || surname.length() < 1)
            errors.add("Surname cannot be empty");
        if (displayName == null || displayName.length() < 1)
            errors.add("Display name cannot be empty");
        if (username == null || username.length() < 1)
            errors.add("Username cannot be empty");
        
        return (errors.size() > 0) ? TextUtils.join(errors.iterator(),
            "\n") : null;
    }

    public void save() throws BuildingServerException, NotInitialisedException
    {
        if (user.isUnsaved())
            user.save();
        waUser.setUserId(user.getUserId());
        if (waUser.isUnsaved())
            waUser.save();
        //saveAliasEntry(cardIdEntry);
        //saveAliasEntry(barcodeEntry);
        saveAliasEntry(personCodeEntry);

    }
    
    /**
     * Check is the alias has a value and if so set the user id and save.
     */
    private void saveAliasEntry(AliasEntry aliasEntry) throws BuildingServerException
    {
        if (aliasEntry != null && aliasEntry.getUserAlias() != null)
        {
            aliasEntry.setUserId(user.getUserId());
            if (aliasEntry.isUnsaved())
            {
                AliasEntry existing = AliasEntry.findAliasEntry(aliasEntry.getAlias(), aliasEntry.getUserAlias());
                if (existing != null && !existing.getUserId().equals(aliasEntry.getUserId())) {
                    log.warn("Change of alias. Existing: "+ existing+ " New: "+ aliasEntry);
                    existing.delete();
                }
                aliasEntry.save();
            }
        }
    }

    public void update() throws BuildingServerException,
        NotInitialisedException
    {
        if (user == null || waUser == null)
            throw new NotInitialisedException();
        user.setInitials((initials == null)?"":initials);
        user.setName(displayName);
        user.setSurname(surname);
        waUser.setUserName(username);
        updateAliasEntry(personCodeEntry, personCode);
    }
    
    private void updateAliasEntry(AliasEntry aliasEntry, String value)
    {
        if (value != null)
            aliasEntry.setUserAlias(value);
    }

    Collection getGroupCollection()
    {
        return groups;
    }
    
    void setGroupCollection(Collection groups)
    {
        this.groups = groups;
    }
    
    public String toString()
    {
        return "Username: "+ username+ " Name: "+ displayName+ " Card ID: "+ cardId;
    }

}
