<%
	import java.util.StringTokenizer;

	void printMetaDataInformation()
	{
		String metadataMode = readUserVariableAsString(USER_VAR_METADATA_DISPLAY_MODE, imageDirectory, DEFAULT_METADATA_DISPLAY_MODE);
	    SmartResourceBundle cameraResource = getResourceBundle(PHOTO_METADATA_RESOURCE_BUNDLE_NAME, CACHE_KEY_PHOTO_METADATA_RESOURCE_BUNDLE);

		if( metadataMode.equalsIgnoreCase("all") )
		{
		    printMetaDataInformationImpl(getFromResourceBundle("meta-general-tags"), null, false, cameraResource, false);
		    printMetaDataInformationImpl(getFromResourceBundle("meta-maker-tags"), "Makernote", true, cameraResource, false);
		    printMetaDataInformationImpl(getFromResourceBundle("meta-iptc-tags"), "Iptc", false, cameraResource, false);
		    printMetaDataInformationImpl(getFromResourceBundle("meta-jpeg-tags"), "Jpeg", false, cameraResource, false);
		}
		else if( metadataMode.equalsIgnoreCase("verbose") )
		{
		    printMetaDataInformationImpl(getFromResourceBundle("meta-general-tags"), null, false, cameraResource, true);
		    printMetaDataInformationImpl(getFromResourceBundle("meta-maker-tags"), "Makernote", false, cameraResource, true);
		    printMetaDataInformationImpl(getFromResourceBundle("meta-iptc-tags"), "Iptc", false, cameraResource, true);
		    printMetaDataInformationImpl(getFromResourceBundle("meta-jpeg-tags"), "Jpeg", false, cameraResource, true);
		}
		else if( metadataMode.equalsIgnoreCase("iptc-only") )
		{
		    printMetaDataInformationImpl(getFromResourceBundle("meta-iptc-tags"), "Iptc", false, cameraResource, true);
		}
		else if( metadataMode.equalsIgnoreCase("custom") )
		{
			String customFields = readUserVariableAsString(USER_VAR_CUSTOM_METADATA_FIELDS, imageDirectory, DEFAULT_CUSTOM_METADATA_FIELDS);
			StringTokenizer parser = new StringTokenizer(customFields, "|");

			while (parser.hasMoreTokens())
			{
				String key = parser.nextToken();

				if( key.indexOf("<title>")!=-1 )
				{
					String[] args = new String[] {key.substring("<title>".length())};

					out.print( MessageFormat.format(PHOTO_METADATA_HEADER_TEMPLATE, args) );
				}
				else if( meta.containsKey(key) )
				{
					String[] args = new String[] {transformMetadataKey(cameraResource, key), transformMetadataValue(key, meta.get(key))};

					out.print( MessageFormat.format(PHOTO_METADATA_LINE_TEMPLATE, args) );
				}
			}
		}
	}

	void printMetaDataInformationImpl(String seriesHeader, String seriesName, boolean includeUnknown, SmartResourceBundle cameraResource, boolean transformKey)
	{
        Iterator it = meta.entrySet().iterator();
        boolean headerPrinted = false;

        while( it.hasNext() )
        {
            Map.Entry entry = (Map.Entry)it.next();
            String key = entry.getKey().toString();

            if( seriesName!=null )
            {
                if( key.toLowerCase().indexOf(seriesName.toLowerCase())==-1 )
                    continue;
            }
            else // Only general tags!
            {
                if( key.indexOf(".")!=-1 )
                    continue;
            }

            if( !includeUnknown && key.indexOf("Unknwon")!=-1 )
                continue;

            if( !headerPrinted )
            {
				out.print( MessageFormat.format(PHOTO_METADATA_HEADER_TEMPLATE, new String[] {seriesHeader + ":"}) );
                headerPrinted = true;
            }
            if( transformKey )
                key = transformMetadataKey(cameraResource, key);
			out.print( MessageFormat.format(PHOTO_METADATA_LINE_TEMPLATE, new String[] {key, entry.getValue()}) );
        }
	}

	String transformMetadataValue(String key, String val)
	{
		if( key.equalsIgnoreCase("Flash") )
		{
			if (flash == 0x0000)
			  return "Flash did not fire";
			else if (flash == 0x0001)
			  return "Flash fired";
			else if (flash == 0x0005)
			  return "Strobe return light not detected";
			else if (flash == 0x0007)
			  return "Strobe return light detected";
			else if (flash == 0x0009)
			  return "Flash fired, compulsory flash mode";
			else if (flash == 0x000D)
			  return "Flash fired, compulsory flash mode, return light not detected";
			else if (flash == 0x000F)
			  return "Flash fired, compulsory flash mode, return light detected";
			else if (flash == 0x0010)
			  return "Flash did not fire, compulsory flash mode";
			else if (flash == 0x0018)
			  return "Flash did not fire, auto mode";
			else if (flash == 0x0019)
			  return "Flash fired, auto mode";
			else if (flash == 0x001D)
			  return "Flash fired, auto mode, return light not detected";
			else if (flash == 0x001F)
			  return "Flash fired, auto mode, return light detected";
			else if (flash == 0x0020)
			  return "No flash function";
			else if (flash == 0x0041)
			  return "Flash fired, red-eye reduction mode";
			else if (flash == 0x0045)
			  return "Flash fired, red-eye reduction mode, return light not detected";
			else if (flash == 0x0047)
			  return "Flash fired, red-eye reduction mode, return light detected";
			else if (flash == 0x0049)
			  return "Flash fired, compulsory flash mode, red-eye reduction mode";
			else if (flash == 0x004D)
			  return "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected";
			else if (flash == 0x004F)
			  return "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected";
			else if (flash == 0x0059)
			  return "Flash fired, auto mode, red-eye reduction mode";
			else if (flash == 0x005D)
			  return "Flash fired, auto mode, return light not detected, red-eye reduction mode";
			else if (flash == 0x005F)
			  return "Flash fired, auto mode, return light detected, red-eye reduction mode";
			else
			  return "Unknown flash mode";
		}
		else if( key.equalsIgnoreCase("Canon Makernote.Subject Distance") )
		{
			int dist = convertToInteger(val);

			if (dist == 6553)
				return "Infinity";
			else
				return (dist/100.0) + "m";
		}
		else if( key.equalsIgnoreCase("Focal Length") )
		{
			int equivalent = calculateFocus35mm();
			
			if( equivalent!=0 )
				return val + " (35mm equivalent: " + equivalent + "mm)";
		}

		return val;
	}

	// Remove the extra prefixes from the EXIF key for display!
	String transformMetadataKey(SmartResourceBundle cameraResource, String val)
	{
	    String result = null;

		try
		{
		    result = cameraResource.getString(val);
		}
		catch( MissingResourceException e )
		{
		    String strippedVal;

            strippedVal = stripString(val, "Makernote.");
            strippedVal = stripString(strippedVal, "Iptc.");
            strippedVal = stripString(strippedVal, "Jpeg.");

            try
            {
                result = cameraResource.getString(strippedVal);
            }
            catch( MissingResourceException e )
            {
    		    result = strippedVal;
    		}
        }
		return result;
	}

	String stripString(String text, String strToStrip)
	{
		int pos = text.toLowerCase().indexOf(strToStrip.toLowerCase());

		if( pos!=-1 )
		    return text.substring(pos + strToStrip.length());
		return text;
	}

	String stripFirstChar(String str)
	{
	    if( isEmptyString(str) || str.length()<2 )
	        return "";
	    else
		    return str.substring(1);
	}

	double getCameraInfo(String cameraModel, String informationName)
	{
		Properties prop = getFolderConfig(skinDirectory, CAMERA_INFORMATION);
		String info = prop.get(cameraModel + "." + informationName);

		return convertToDouble(info);
	}

	String getCameraModel()
	{
	    String model = (String) meta.get("Model");
	    String make = (String) meta.get("Make");

	    if( isEmptyString(make) )
	        return model;
	    else if( isEmptyString(model) )
	        return make;

	    if( model.startsWith(make) )
	        return model;
	    else
	        return make + " " + model;
	}

	int calculateFocus35mm()
	{
		double f = convertToDouble(focalLength);

		// There are 2 ways of computing the 35mm equivalent focus:
		// 1. Read the conversion parameters from cameras.properties
		String cameraModel = getCameraModel();

		if( !isEmptyString(cameraModel) )
		{
			double multiplier = getCameraInfo(cameraModel, "focalLengthMultiplier");
			double offset = getCameraInfo(cameraModel, "focalLengthOffset");

			if( multiplier>0.0 )
			    return (int)(f * multiplier + offset + 0.5);
		}

		// 2. Use the Focal Plane X/Y resolution (if present)
		// Problem: if image is already rotated, the ccdWidth will return the
		//          actual ccdHeight and render the calculation invalid
		// Solution: take height if height > width
		if( exifImageHeight() > exifImageWidth() )
		{
			double ccdHeight = ccdHeight();

			if( ccdHeight>0.001 )
				return (int)(f * 35.0 / ccdHeight + 0.5);
		}
		else
		{
			double ccdWidth = ccdWidth();

			if( ccdWidth>0.001 )
				return (int)(f * 35.0 / ccdWidth + 0.5);
		}

		// Fallback:
		return 0;
	}

	// the following functions assume that the meta object exists.
	// this will ususally be the case, as this will only be called if it exists.
	double exifImageWidth()
	{
		return exifImageSizeImpl(true);
	}

	double exifImageHeight()
	{
		return exifImageSizeImpl(false);
	}

	double exifImageSizeImpl(boolean isWidth)
	{
		String eis;

		//exif image size; complete string
		if( isWidth )
			eis = (String) meta.get("Exif Image Width");
		else
			eis = (String) meta.get("Exif Image Height");

		if( eis==null )
			return 0.0;

		//just the number
		StringTokenizer t = new StringTokenizer(eis);

		return convertToDouble( t.nextToken() );
	}

	// return the width of the ccd in mm
	double ccdWidth()
	{
		return ccdSizeImpl(true);
	}

	// return the height of the ccd in mm
	double ccdHeight()
	{
		return ccdSizeImpl(false);
	}

	double ccdSizeImpl(boolean isWidth)
	{
		String ccdr;

		if( isWidth )
			ccdr = (String) meta.get("Focal Plane X Resolution");
		else
			ccdr = (String) meta.get("Focal Plane Y Resolution");

		if( ccdr==null )
			return 0.0;

		// the resolution is a string, containing a fraction
		// [num]/[den] [unit]
		// for Canon camera's, [unit] is "inches".
		// Are there any camera's whose unit is "mm"?

		StringTokenizer t = new StringTokenizer(ccdr);

		// decode the resolution string
		double num = convertToDouble( t.nextToken("/") );
		double den = convertToDouble( stripFirstChar(t.nextToken(" ")) );
		String unit = t.nextToken();
		double imageLength = isWidth ? exifImageWidth() : exifImageHeight();

		// convert from inches to mm, if needed. this is experimental
		if (unit.equalsIgnoreCase("mm"))
			return (double) (num / den * imageLength);
		else if (unit.equalsIgnoreCase("cm"))
			return (double) (num / den * 10 * imageLength);
		else // the default is "inches"
			return (double) (num / den * 25.4 * imageLength);
	}
%>