
import java.util.*;

import se.datadosen.jalbum.AlbumBean;
import se.datadosen.util.SmartResourceBundle;

public class PhotographicMetaInformation extends Base
{
	//private ImageInfoFormatter formatter = new ImageInfoFormatter();

	public PhotographicMetaInformation(AlbumBean engine)
	{
		super(engine);
	}

	protected String transformMetadataValue(String key, Object val)
	{
		if( isEmptyString(val) )
			return "";

		if( key.equalsIgnoreCase("Flash") )
		{
			double flash = convertToDouble(val);

			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)";
		}

		/*try
		{
			if( key.equalsIgnoreCase("Flash") )
				return formatter.format(val, ExifDirectory.TAG_FLASH).toString();
			else if( key.equalsIgnoreCase("Exposure Time") )
				return formatter.format(val, ExifDirectory.TAG_EXPOSURE_TIME).toString();
		}
		catch( Exception e )
		{
			log("Can not format the metadata information '" + key + "'!", e);
		}*/

		return val.toString();
	}

	// Remove the extra prefixes from the EXIF key for display!
	protected 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 ex )
            {
    		    result = strippedVal;
    		}
        }
		return result;
	}

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

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

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

	private double getCameraInfo(String cameraModel, String informationName)
	{
		Properties prop = getFolderConfig(skinDirectory, Constants.SKIN_CAMERA_INFORMATION);
		String info = prop.getProperty(cameraModel + "." + informationName);

		return convertToDouble(info);
	}

	private 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;
	}

	private int calculateFocus35mm()
	{
		double fl = convertToDouble(meta.get("Focal Length"));

		// 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)(fl * 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)(fl * 35.0 / ccdHeight + 0.5);
		}
		else
		{
			double ccdWidth = ccdWidth();

			if( ccdWidth>0.001 )
				return (int)(fl * 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.
	private double exifImageWidth()
	{
		return exifImageSizeImpl(true);
	}

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

	private 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
	private double ccdWidth()
	{
		return ccdSizeImpl(true);
	}

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

	private 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);
	}
}
