/*
 * Created on 20-Jan-2005
 */
package org.bodington.servlet.facilities;

import java.awt.Color;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URLEncoder;
import java.util.Calendar;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
import org.apache.log4j.Logger;
import org.bodington.server.BuildingServer;
import org.bodington.server.BuildingServerException;
import org.bodington.server.realm.Acl;
import org.bodington.server.realm.AclEntry;
import org.bodington.server.realm.Permission;
import org.bodington.server.resources.Resource;
import org.bodington.servlet.Request;
import org.bodington.util.BodingtonURL;


/**
 * @author buckett
 */
public class Utils
{
    
    private static Logger log = Logger.getLogger(Utils.class.getName());

    private static final Map days = new TreeMap();
    private static final Map hours = new TreeMap();
    private static final Map minutes = new TreeMap();
    private static final String[] monthNames = {"January", "Febuary", "March", "April", "May", "June", "July", "August", "September", "October", "November", "Decemeber"};
    private static final Map months = new TreeMap();
    
    private static HttpClient httpClient;
    
    static {
    	for(int count = 0; count < 24; count++) {
    		final Integer hour = new Integer(count);
    		hours.put(hour, hour);
    	}
    	for(int count = 0; count < 60; count++) {
    		final Integer minute = new Integer(count);
    		minutes.put(minute, minute);
    	}
    	for(int count = 1; count< 32; count++) {
    		final Integer day = new Integer(count);
    		days.put(day,day);
    	}
    	for(int count = 0; count < 12; count++) {
    		final Integer month = new Integer(count);
    		months.put(month, monthNames[count]);
    		
    	}

    	MultiThreadedHttpConnectionManager manager = new MultiThreadedHttpConnectionManager();
    	HttpConnectionManagerParams httpManagerParams = new HttpConnectionManagerParams();
    	
    	httpManagerParams.setDefaultMaxConnectionsPerHost(10);
    	httpManagerParams.setConnectionTimeout(5000);
    	httpManagerParams.setSoTimeout(10000);
    	
    	manager.setParams(httpManagerParams);
        
        httpClient = new HttpClient(manager);
    	
    }

    /**
     * Attempt to convert the calendar data in the request into a calendar object.
     * If the conversion fails then null is returned.
     * @param req The servlets request to get the raw data from.
     * @param prefix The prefix that the form elements used.
     * @return The converted calendar object.
     */
    public static Calendar parseCalendarChooser(Request req, String prefix)
    {
    	Calendar calendar = Calendar.getInstance();
    	try
    {
    		calendar.set(
    			Integer.parseInt(req.getParameter(prefix+ ".year")),
    		Integer.parseInt(req.getParameter(prefix+ ".month")),
    		Integer.parseInt(req.getParameter(prefix+ ".day")),
    		Integer.parseInt(req.getParameter(prefix+ ".hour")),
    		Integer.parseInt(req.getParameter(prefix+ ".minute"))
    		
    		);
    }
    	catch (NumberFormatException nfe)
    {
    		return null;
    }
    	return calendar;
    		
    }

    public static void writeCalendarChooser(PrintWriter out, String prefix)
    {
    	writeCalendarChooser(out, prefix, Calendar.getInstance());
    }

    /**
     * Outputs a date and time chooser to the page. If no default calendar is supplied
     * the currently time/date is used. The really should be in a template which can then
     * be included into other pages so the changing to format of the calendar can be done without
     * having to hack the Java.
     * @param out Where all the HTML is written to.
     * @param prefix The prefix for all the calendar form elements to allow multiple calendars on a page.
     * @param calendar The value to set the calendar chooser to.
     * @see #writeSelect(PrintWriter, String, Map, Object)
     */
    public static void writeCalendarChooser(PrintWriter out, String prefix, Calendar calendar)
    {
    	if (calendar == null) {
    		calendar = Calendar.getInstance();
    	}
    	out.write("Time: ");
    	writeSelect(out, prefix+".hour", hours, new Integer(calendar.get(Calendar.HOUR_OF_DAY)));
    	writeSelect(out, prefix+".minute", minutes, new Integer(calendar.get(Calendar.MINUTE)));
    	out.write("<br/>");
    	out.write("Date: ");
    	writeSelect(out, prefix+".day", days, new Integer(calendar.get(Calendar.DAY_OF_MONTH)));
    	writeSelect(out, prefix+".month", months, new Integer(calendar.get(Calendar.MONTH)));
    	out.write("<input type=\"text\" name=\""+ prefix + ".year\" value=\""+ calendar.get(Calendar.YEAR)+ "\" size=\"4\"/>");
    	
    }

    /**
     * Outputs an HTML &lt;select&gt; tag with the values supplied. No sensible checking is 
     * done at the moment. The Map supplied to this function probably wants to be a
     *  SortedMap so that the items are displayed in a sensible manner. 
     * @param out Where all the HTML output is written.
     * @param name The name associated with this form element.
     * @param map A Map containing the values to be outputed. 
     * @param selected The key in the Map that should be selected.
     */
    public static void writeSelect (PrintWriter out, String name, Map map, Object selected)
    {
    	out.write("<select name=\""+ name+ "\">\n");
    	Set entries = map.entrySet();
    	Iterator entriesIt = entries.iterator();
    	while (entriesIt.hasNext())
    	{
    		final Map.Entry entry =(Map.Entry)entriesIt.next();
    		out.write("<option value=\""+ entry.getKey() +"\"");
    		if (entry.getKey().equals(selected))
    		{
    			out.write(" selected");
    		}
    		if (entry.getValue() == null) 
    		{
    			out.write("/>\n");
    		}
    		else 
    		{
    			out.write(">"+ entry.getValue()+ "</option>\n");
    		}
    	}
    	// Finish the select
    	out.write("</select>\n");		
    }
    
    /**
     * Check to see if a username should be logging in using WebAuth.
     * The username of <code>sysadmin</code> is never forced through WebAuth.
     * @param username The username to check.
     * @return True if the user should be doing WebAuth.
     * @throws BuildingServerException If something went wrong with the check.
     */
    public static boolean checkWebAuthUser(String username) throws BuildingServerException
    { 
        BufferedReader in = null;
        String url = null;
        String line = null;
        HttpMethod method = null;
        
        if (username.equals("sysadmin")) return false;
        
        try
        {
            url = new String(BuildingServer.getInstance().getProperties()
                .getProperty("bodington.webauth.url",
                    "http://admin.herald.ox.ac.uk/weblearn_status")
                + "?username=" + URLEncoder.encode(username, "UTF-8"));
            log.debug("Starting checking: "+ username);
            method = new GetMethod(url);
            httpClient.executeMethod(method);

            if (method.getStatusCode() != HttpStatus.SC_OK)
            {
                throw new BuildingServerException("WA check got bad result: "
                    + method.getStatusCode());
            }
            in = new BufferedReader(new InputStreamReader(method
                .getResponseBodyAsStream()));
            while (true)
            {
                line = in.readLine();
                if (line == null)
                {
                    break;
                }
                if (line.startsWith("Status: "))
                {
                    line = line.substring(8);
                    if (line.equals("ok"))
                        return true;
                    else if (line.equals("expired"))
                        return false;
                    else if (line.equals("not found"))
                        return false;
                    else if (line.equals("bad request")) return false;
                }

            }
        }
        catch (IllegalArgumentException e)
        {
            log.warn("Invalid URL: " + url);
            throw new BuildingServerException(e.toString());
        }
        catch (IOException e)
        {
            log.warn("IO problem");
            throw new BuildingServerException(e.toString());
        }
        finally
        {
            log.debug("Finished checking: "+ username);
            if (method != null)
            {
                method.releaseConnection();
            }
            
            if (in != null)
            {
                try
                {
                    in.close();
                }
                catch (IOException ioe)
                {
                    ;
                }
            }
            
        }

        return false;
    }
    
    public static String toCSSColor(Color color)
    {
        return "#"+ Integer.toHexString( color.getRGB() ).substring( 2 );
    }
    
    /**
     * The writes out a &lt;base&gt; tag for the current resource.
     * This is currently used in the MCQ debrief for making sure image links still work.
     * @param request The servlet request from which we get the resource.
     * @param out Where we write the tag.
     */
    public static void writeBase(Request request, PrintWriter out)
    {
        BodingtonURL url = new BodingtonURL(request);
        out.println("<base href=\""+ url.getResourceUrl(request.getResource())+ "\"/>");
    }

    /**
     * Attempt to grant some permissions to a Resource. This is often 
     * used by facilities to grant additional permissions on a resource straight after
     * it has been created.
     * @param resource The resource to add the permissions to.
     * @param grant An array of permissions to be added
     * @return True if the permissions were sucessfully added.
     */
    public static boolean grantPermissions(Resource resource, Permission[] grant)
    {
        try
        {
            Acl acl = resource.getAcl();
            if (acl != null)
            {
                AclEntry aclEntry = acl.getOwnerGroupAclEntry();
                if (aclEntry != null)
                {
                    for(int count = 0; count < grant.length; count++)
                    {
                        aclEntry.addPermission(grant[count]);
                    }
                    return true;
                }
            }
            log.warn("Resource doesn't have default ACLs: "+ resource);
        }
        catch (BuildingServerException bse)
        {
            log.warn("Could not get ACL details for: " + resource);
        }
        return false;
    }
}
