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

import java.awt.Color;
import java.io.PrintWriter;
import java.text.DecimalFormat;
import java.util.Calendar;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import org.apache.log4j.Logger;
import org.bodington.server.BuildingServerUserException;
import org.bodington.servlet.EscapedHtmlWriter;
import org.bodington.servlet.Request;
import org.bodington.util.BodingtonURL;


/**
 * @author buckett
 */
public class Utils
{

    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 DecimalFormat formatBytes = new DecimalFormat("###B");
    private static DecimalFormat formatKiloBytes = new DecimalFormat("##0.0kB");
    private static DecimalFormat formatMegaBytes = new DecimalFormat("##0.0MB");
    
    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]);
    		
    	}
    	
    }

    /**
     * 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");		
    }
    
    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())+ "\"/>");
    }
    
    /**
     * Writes out the Exception in HTML
     * @param bsue The Exception to format.
     * @param out The Writer to send the HTML to.
     */
    public static void writeUserExceptionHTML(BuildingServerUserException bsue, PrintWriter out)
    {
        out.println("<div class='errors'>");
        out.println("<span class='message'>");
        out.println(bsue.getMessage());
        out.println("</span>");
        Iterator messages = bsue.iterator();
        if (messages.hasNext())
        {
            out.println("<ul>");
            EscapedHtmlWriter escapedOut = new EscapedHtmlWriter(out);
            while(messages.hasNext())
            {
                out.print("<li>");
                escapedOut.print((String)messages.next());
                out.println("</li>");
            }
            out.println("</ul>");
        }
        out.println("</div>");
    }
    
    public static void writeError(String error, PrintWriter out)
    {
        out.println("<div class='errors'>");
        out.println("<ul><li>");
        out.println(error);
        out.println("</li></ul>");
        out.println("</div>");
    }
    
    public static void writeMessage(String message, PrintWriter out)
    {
        out.println("<div class='message'>");
        out.println("<ul><li>");
        out.println(message);
        out.println("</li></ul>");
        out.println("</div>");
    }
    
    /**
     * Writes out the Exception in text.
     * @param bsue The Exception to format.
     * @param out The Writer to send the text to.
     */
    public static void writeUserExceptionRaw(BuildingServerUserException bsue, PrintWriter out)
    {
        out.println(bsue.getMessage());
        Iterator messages = bsue.iterator();
        EscapedHtmlWriter escapedOut = new EscapedHtmlWriter(out);
        while(messages.hasNext())
        {
            out.print(" - ");
            escapedOut.print((String)messages.next());
            out.println();
        }
        
    }
    
    /**
     * Quickly create a map from a 2D array.
     */
    public static Map createMap(Object[][] data)
    {
        Map map = new TreeMap();
        for (int i = 0; i < data.length; i++)
            map.put(data[i][0], data[i][1]);
        return map;
    }
    
    /**
     * Formats file sizes in a more human reabable form.
     * @param size The size of the file in bytes.
     * @return A human readable file size.
     */
    public static String toHumanReadableSize(long size)
    {
        if (size < 1000)
            return formatBytes.format(size);
        if (size < 1000000)
            return formatKiloBytes.format(size/1000.0);
        return formatMegaBytes.format(size/1000000.0);
    }

}
