/* ======================================================================
   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.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);
    }

}
