/* ======================================================================
   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.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;

import org.apache.log4j.Logger;
import org.bodington.server.BuildingServerException;
import org.bodington.server.realm.Alias;
import org.bodington.server.realm.Zone;
import org.bodington.server.userimport.ConfigurationException;
import org.bodington.server.userimport.GroupManager;
import org.bodington.server.userimport.InformationResource;
import org.bodington.server.userimport.SelectionException;
import org.xmlpull.v1.XmlPullParserException;

public class OUCSInformationResource implements InformationResource
{
    
    private static final Logger log = Logger.getLogger(OUCSInformationResource.class);
    
    /**
     * URL of the users file. Must be defined.
     */
    public static final String USERS_FILE = "usrgrpgen.oucsir.users";
    /**
     * URL of the colleges file. Must be defined.
     */
    public static final String COLLEGES_FILE = "usrgrpgen.oucsir.colleges";
    /**
     * URL of the departments file. Must be defined.
     */
    public static final String DEPARTMENTS_FILE = "usrgrpgen.oucsir.depts";
    /**
     * URL of the courses file. Must be defined.
     */
    public static final String COURSES_FILE = "usrgrpgen.oucsir.courses";
    
    public static final String ZONE = "usrgrpgen.oucsir.zone";
    public static final String BARCODE = "usrgrpgen.oucsir.barcode";
    public static final String PERSON_CODE = "usrgrpgen.oucsir.personcode";
    public static final String CARD = "usrgrpgen.oucsir.card";

    private OUCSGroupManager groupManager;
    private OUCSUsersParser usersParser;

    private Map colleges;

    private Map departments;

    private Map courses;
    
    public OUCSInformationResource()
    {
        
    }
    

    public GroupManager getGroupManager(Properties props)
        throws ConfigurationException
    {
        return groupManager;
    }

    public void init(Properties props) throws ConfigurationException
    {
        // Look for the internal stuff first.
        String zoneName = props.getProperty(ZONE, "sys");
        Zone zone = null;
        try
        {
            zone = Zone.findZoneByPrefix(zoneName);
        }
        catch (BuildingServerException e)
        {
            throw new ConfigurationException("Error loading zone: "+ zoneName, e);
        }
        if (zone == null)
            throw new ConfigurationException("Couldn't find zone: "+ zoneName);
        
        Alias barcodeAlias = findOrCreateAlias(props, BARCODE, zone);
        Alias personCodeAlias = findOrCreateAlias(props, PERSON_CODE, zone);
        Alias cardAlias = findOrCreateAlias(props, CARD, zone);
        
        // Need to find our files....
        String usersFile = getProp(props, USERS_FILE);
        String collegesFile = getProp(props, COLLEGES_FILE);
        String departmentsFile = getProp(props, DEPARTMENTS_FILE);
        String coursesFile = getProp(props, COURSES_FILE);
        
        colleges = convertToMap(new OUCSCollegeParser(), collegesFile);
        departments = convertToMap(new OUCSDepartmentParser(), departmentsFile);
        courses = convertToMap(new OUCSCourseParser(), coursesFile);
        
        usersParser = new OUCSUsersParser();
        try
        {
            usersParser.setReader(openUrl(usersFile));
        }
        catch (XmlPullParserException e)
        {
        	String message = "Cannot read users file: "+ usersFile;
        	log.error(message, e);
            throw new ConfigurationException(message, e);
        }
        
        usersParser.setColleges(colleges);
        usersParser.setCourses(courses);
        usersParser.setDepartments(departments);
        usersParser.setStatuses(OUCSStatus.getStatuses());
        
        usersParser.setZone(zone);
        usersParser.setBarcodeAlias(barcodeAlias);
        usersParser.setCardIdAlias(cardAlias);
        usersParser.setPersonCodeAlias(personCodeAlias);
        
        groupManager = new OUCSGroupManager(props, this);
        
    }
    
    
    /**
     * Utility method to find an alias or create it.
     * Aliases created are secondary.
     * @param props The properties to look for the name of the alias in.
     * @param key The key to lookup the value in the properties object
     * @param zone The zone to create the aliases in.
     * @return An Alias.
     * @throws ConfigurationException If the configuration isn't there, we can't
     * load the alias or we can't create the alias.
     */
    private static Alias findOrCreateAlias(Properties props, String key, Zone zone)
        throws ConfigurationException
    {
        String value = props.getProperty(key);
        if (value == null)
            throw new ConfigurationException(key+ " not defined in configuration");
        Alias alias = null;
        try
        {
            alias = Alias.findAlias(value, zone);
        }
        catch (BuildingServerException e)
        {
            throw new ConfigurationException("Error loading alias :"+ value);
        }
        if (alias == null)
        {
            alias = new Alias();
            alias.setAliasName(value);
            alias.setAliasType(new Integer(Alias.TYPE_SECONDARY));
            alias.setZone(zone);
            try
            {
                alias.save();
                log.debug("Created Alias "+ alias.getPrimaryKey()+ " for "+ value);
            }
            catch (BuildingServerException e)
            {
                throw new ConfigurationException("Problem creating a new alias: "+ value, e);
            }
        }
        else
        {
            log.debug("Found Alias "+ alias.getPrimaryKey()+ " for "+ value);
        }
        
        return alias;
    }


    private static Map convertToMap(XppIterator parser, String url)
        throws ConfigurationException
    {
        try
        {
            parser.setReader(openUrl(url));
        }
        catch (XmlPullParserException e)
        {
            throw new ConfigurationException("Problem reading XML from :"+ url, e);
        }
        Map map = Utils.createMap(new ValidatingIterator(parser));
        if (log.isDebugEnabled())
            log.debug("Loaded "+ map.size()+ " entries from "+ url);
        return map;
    }
    
    /**
     * Utility method to get a value from the properties or throw an exception.
     * @param props The properties object to look in.
     * @param key The key to use for the lookup in the properties
     * @return The value of the lookup.
     * @throws ConfigurationException If the value isn't defined.
     */
    private static String getProp(Properties props, String key) 
        throws ConfigurationException
    {
        String value = props.getProperty(key);
        if (value == null)
            throw new ConfigurationException(key + " is not defined");
        return value;
    }
    
    /**
     * Utility method to connect to a URL and wrap up the exceptions.
     * @param url The URL to connect to.
     * @return A Reader connected to the URL.
     * @throws ConfigurationException If there is a problem with the URL or 
     * connecting to the source.
     */
    private static Reader openUrl(String url) throws ConfigurationException
    {
        try
        {
            return new InputStreamReader(new URL(url).openStream());
        }
        catch (MalformedURLException e)
        {
            throw new ConfigurationException(url+ " doesn't seem to be a URL.", e);
        }
        catch (IOException e)
        {
            throw new ConfigurationException("Problem accessing "+ url, e);
        }
    }

    public Iterator userIterator() throws SelectionException
    {
        return new ValidatingIterator(usersParser);
    }


    String getCollegeName(String code)
    {
        return ((OUCSCollege)colleges.get(code)).getName();
    }


    String getDepartmentName(String code)
    {
        return ((OUCSDepartment)departments.get(code)).getName();
    }


    String getCourseName(String code)
    {
        return ((OUCSCourse)courses.get(code)).getDescription();
    }

}
