content: package com.almalence.plugins.export.ExifDriver;
  
  import java.util.Arrays;
  
  import android.content.Context;
  
  import com.almalence.plugins.export.ExifDriver.Values.ExifValue;
  import com.almalence.plugins.export.ExifDriver.Values.UndefinedValueAccessException;
  import com.almalence.plugins.export.ExifDriver.Values.ValueByteArray;
  import com.almalence.plugins.export.ExifDriver.Values.ValueNumber;
  import com.almalence.plugins.export.ExifDriver.Values.ValueRationals;
  
  public class ExifManager
  {
  	ExifDriver	driver;
  	Context		context;
  
  	public ExifManager(ExifDriver _driver, Context _context)
  	{
  		driver = _driver;
  		context = _context;
  	}
  
  	/*
  	 * Here go the user-space set/get methods
  	 */
  	/**
  	 * Get photographer/editor copyright pair string. In case, that only editor
  	 * is specified, photographer copyright will contain space character, so it
  	 * is recommended to trim it, or use specialized getPhotographerCopyright()
  	 * method
  	 * 
  	 * @return Array of Strings. Item 0 is photographer copyright, 1 holds the
  	 *         editor copyright
  	 */
  	public String[] getCopyright()
  	{
  		byte[][] result = new byte[2][];
  		result[0] = new byte[] { 0 };
  		result[1] = new byte[] { 0 };
  		ExifValue exifValue = driver.getIfd0().get(ExifDriver.TAG_COPYRIGHT);
  		if (exifValue != null && exifValue.getDataType() == ExifDriver.FORMAT_UNDEFINED)
  		{
  			byte[] values;
  			try
  			{
  				values = exifValue.getBytes();
  			} catch (UndefinedValueAccessException e)
  			{
  				e.printStackTrace();
  				return new String[] { "Error" };
  			}
  			int copyrightIndex = 0;
  			result[0] = new byte[values.length];
  			Arrays.fill(result[0], (byte) 0);
  			result[1] = new byte[values.length];
  			Arrays.fill(result[1], (byte) 0);
  			int index = 0;
  			for (int i = 0; i < values.length && copyrightIndex < 2; i++)
  			{
  				if (values[i] != 0)
  				{
  					result[copyrightIndex][index] = values[i];
  					index++;
  				} else
  				{
  					copyrightIndex++;
  					index = 0;
  				}
  			}
  		}
  		return new String[] { new String(result[0]).trim(), new String(result[1]).trim() };
  	}
  
  	/**
  	 * Photographer copyright in one single string
  	 * 
  	 * @return Photographer copyright
  	 */
  	public String getPhotographerCopyright()
  	{
  		return getCopyright()[0];
  	}
  
  	/**
  	 * Editor copyright in one single string
  	 * 
  	 * @return Editor copyright
  	 */
  	public String getEditorCopyright()
  	{
  		return getCopyright()[1];
  	}
  
  	/**
  	 * Get marker note - the marker specific (possibly binary) data. User should
  	 * know, what the tag holds.
  	 * 
  	 * @return Marker note byte[] or null if Marker note could not be found
  	 */
  	public byte[] getMarkerNote()
  	{
  		ExifValue exifValue = driver.getIfdExif().get(ExifDriver.TAG_MARKER_NOTE);
  		if (exifValue != null && exifValue.getDataType() == ExifDriver.FORMAT_UNDEFINED)
  		{
  			try
  			{
  				return exifValue.getBytes();
  			} catch (UndefinedValueAccessException e)
  			{
  				e.printStackTrace();
  				return new byte[0];
  			}
  		} else
  		{
  			return null;
  		}
  	}
  
  	/**
  	 * Get artist, which is actually an author of the image
  	 * 
  	 * @return Name of artist or null if the tag could not be found
  	 */
  	public String getArtist()
  	{
  		ExifValue exifValue = driver.getIfd0().get(ExifDriver.TAG_ARTIST);
  		if (exifValue != null && exifValue.getDataType() == ExifDriver.FORMAT_ASCII_STRINGS)
  		{
  			try
  			{
  				return new String(exifValue.getBytes());
  			} catch (UndefinedValueAccessException e)
  			{
  				e.printStackTrace();
  				return "Error";
  			}
  		} else
  		{
  			return null;
  		}
  	}
  
  	/**
  	 * 
  	 * @return
  	 */
  	public String getImageDescription()
  	{
  		ExifValue exifValue = driver.getIfd0().get(ExifDriver.TAG_IMAGE_DESCRIPTION);
  		if (exifValue != null && exifValue.getDataType() == ExifDriver.FORMAT_ASCII_STRINGS)
  		{
  			try
  			{
  				return new String(exifValue.getBytes());
  			} catch (UndefinedValueAccessException e)
  			{
  				e.printStackTrace();
  				return "Error";
  			}
  		} else
  		{
  			return null;
  		}
  	}
  
  	/**
  	 * Get user comment it differs from image description - it can hold wide
  	 * characters
  	 * 
  	 * @return
  	 */
  	public String getUserComment()
  	{
  		ExifValue exifValue = driver.getIfdExif().get(ExifDriver.TAG_USER_COMMENT);
  		if (exifValue != null && exifValue.getDataType() == ExifDriver.FORMAT_UNDEFINED)
  		{
  			try
  			{
  				return new String(exifValue.getBytes());
  			} catch (UndefinedValueAccessException e)
  			{
  				e.printStackTrace();
  				return "Error";
  			}
  		} else
  		{
  			return null;
  		}
  	}
  
  	/**
  	 * Set the Marker note. the value can be whatever byte array
  	 * 
  	 * @param _value
  	 *            byte array - binary or text information
  	 */
  	public void setMarkerNote(byte[] _value)
  	{
  		ValueByteArray baValue = new ValueByteArray(ExifDriver.FORMAT_UNDEFINED);
  		baValue.setBytes(_value);
  		driver.getIfdExif().put(ExifDriver.TAG_MARKER_NOTE, baValue);
  	}
  
  	/**
  	 * Artist - or author - should be an ASCII string, but sometimes even
  	 * unicode works fine
  	 * 
  	 * @param _artist
  	 *            Name of the arist
  	 */
  	public void setArtist(String _artist)
  	{
  		ValueByteArray baValue = new ValueByteArray(ExifDriver.FORMAT_ASCII_STRINGS);
  		baValue.setBytes(_artist.getBytes());
  		driver.getIfd0().put(ExifDriver.TAG_ARTIST, baValue);
  	}
  
  	/**
  	 * Copyright for the photographer and editor in common it is used internally
  	 * by specialized methods for setting photographer and editor copyright
  	 * separately
  	 * 
  	 * @param _author
  	 *            Photographer copyright can be null
  	 * @param _editor
  	 *            Editor copyright can be null
  	 */
  	public void setCopyright(String _author, String _editor)
  	{
  		boolean editorPresented = false;
  		String author = _author;
  		if (author == null)
  		{
  			author = "";
  		}
  		author = author.trim();
  		if (author.equals(""))
  		{
  			author = " ";
  		}
  		String editor = _editor;
  		if (editor == null)
  		{
  			editor = "";
  		}
  		editor = editor.trim();
  		editorPresented = !(editor.equals(""));
  		byte[] authorBytes = author.getBytes();
  		byte[] editorBytes = _editor.getBytes();
  		int size = authorBytes.length + 1;
  		if (editorPresented)
  		{
  			size += editorBytes.length + 1;
  		}
  		byte[] value = new byte[size];
  		System.arraycopy(authorBytes, 0, value, 0, authorBytes.length);
  		value[authorBytes.length] = 0;
  		if (editorPresented)
  		{
  			System.arraycopy(editorBytes, 0, value, authorBytes.length + 1, editorBytes.length);
  			value[value.length - 1] = 0;
  		}
  		ValueByteArray baValue = new ValueByteArray(ExifDriver.FORMAT_ASCII_STRINGS);
  		baValue.setBytes(value);
  		driver.getIfd0().put(ExifDriver.TAG_COPYRIGHT, baValue);
  	}
  
  	/**
  	 * Copyright for the photographer (editor) ASCII string
  	 * 
  	 * @param _copyright
  	 *            Copyright string
  	 */
  	public void setPhotographerCopyright(String _copyright)
  	{
  		setCopyright(_copyright, getEditorCopyright());
  	}
  
  	/**
  	 * Copyright for the editor ASCII string
  	 * 
  	 * @param _copyright
  	 *            Copyright string
  	 */
  	public void setEditorCopyright(String _copyright)
  	{
  		setCopyright(getPhotographerCopyright(), _copyright);
  	}
  
  	/**
  	 * Some nice ASCII image description, like "Picnic in the summer A.D. 2012"
  	 * 
  	 * @param _desc
  	 *            Description of the image
  	 */
  	public void setImageDescription(String _desc)
  	{
  		ValueByteArray baValue = new ValueByteArray(ExifDriver.FORMAT_ASCII_STRINGS);
  		baValue.setBytes(_desc.getBytes());
  		driver.getIfd0().put(ExifDriver.TAG_IMAGE_DESCRIPTION, baValue);
  	}
  
  	/**
  	 * User comment - alternative to image description. Difference is, that this
  	 * tag is expected to hold wide characters, so UTF-8 string is possible to
  	 * store here
  	 * 
  	 * @param _comment
  	 */
  	public void setUserComment(String _comment)
  	{
  		ValueByteArray baValue = new ValueByteArray(ExifDriver.FORMAT_UNDEFINED);
  		baValue.setBytes(_comment.getBytes());
  		driver.getIfdExif().put(ExifDriver.TAG_USER_COMMENT, baValue);
  	}
  
  	private int[][] toDdMmSs(double _value)
  	{
  		double value = Math.abs(_value);
  		int[][] ddmmss = new int[3][2];
  		ddmmss[0][0] = (int) Math.floor(value);
  		ddmmss[0][1] = 1;
  		value -= Math.floor(value);
  		value *= 60;
  		ddmmss[1][0] = (int) Math.floor(value);
  		ddmmss[1][1] = 1;
  		value -= Math.floor(value);
  		value *= 60000;
  		ddmmss[2][0] = (int) Math.floor(value);
  		ddmmss[2][1] = 1000;
  		return ddmmss;
  	}
  
  	private void setGpsVersion()
  	{
  		ValueNumber version = new ValueNumber(ExifDriver.FORMAT_UNSIGNED_BYTE);
  		version.setIntegers(new int[] { 2, 2, 0, 0 });
  		driver.getIfdExif().put(ExifDriver.TAG_GPS_VERSION_ID, version);
  	}
  
  	public void setGPSLocation(double _lat, double _lon, double _alt)
  	{
  		setGpsVersion();
  		// Latitude
  		ValueByteArray latRef = new ValueByteArray(ExifDriver.FORMAT_ASCII_STRINGS);
  		ValueRationals lat = new ValueRationals(ExifDriver.FORMAT_UNSIGNED_RATIONAL);
  		lat.setRationals(toDdMmSs(_lat));
  		if (_lat > 0)
  		{
  			latRef.setBytes(new byte[] { 'N' });
  		} else
  		{
  			latRef.setBytes(new byte[] { 'S' });
  		}
  		driver.getIfdGps().put(ExifDriver.TAG_GPS_LATITUDE, lat);
  		driver.getIfdGps().put(ExifDriver.TAG_GPS_LATITUDE_REF, latRef);
  		// Longitude
  		ValueByteArray lonRef = new ValueByteArray(ExifDriver.FORMAT_ASCII_STRINGS);
  		ValueRationals lon = new ValueRationals(ExifDriver.FORMAT_UNSIGNED_RATIONAL);
  		lon.setRationals(toDdMmSs(_lon));
  		if (_lon > 0)
  		{
  			lonRef.setBytes(new byte[] { 'E' });
  		} else
  		{
  			lonRef.setBytes(new byte[] { 'W' });
  		}
  		driver.getIfdGps().put(ExifDriver.TAG_GPS_LONGITUDE, lon);
  		driver.getIfdGps().put(ExifDriver.TAG_GPS_LONGITUDE_REF, lonRef);
  		// Altitude
  		ValueNumber altRef = new ValueNumber(ExifDriver.FORMAT_UNSIGNED_BYTE);
  		ValueRationals alt = new ValueRationals(ExifDriver.FORMAT_UNSIGNED_RATIONAL);
  		int[][] altValue = new int[1][];
  		altValue[0] = new int[] { (int) Math.abs(_alt), 1 };
  		alt.setRationals(altValue);
  		if (_alt >= 0)
  		{
  			altRef.setIntegers(new int[] { 0 });
  		} else
  		{
  			altRef.setIntegers(new int[] { 1 });
  		}
  		driver.getIfdGps().put(ExifDriver.TAG_GPS_ALTITUDE, alt);
  		driver.getIfdGps().put(ExifDriver.TAG_GPS_ALTITUDE_REF, altRef);
  	}
  
  	// Convert string like "123/456" or "1.23" to Rational (2 integers).
  	public static int[][] stringToRational(String string)
  	{
  		int[][] res = null;
  		String[] splited = string.split("/");
  		if (splited.length == 2)
  		{
  			res = new int[1][2];
  			res[0][0] = Integer.parseInt(splited[0]);
  			res[0][1] = Integer.parseInt(splited[1]);
  			return res;
  		}
  
  		splited = string.split("\\.");
  		if (splited.length == 2)
  		{
  			res = new int[1][2];
  			res[0][0] = Integer.parseInt(splited[0] + splited[1]);
  			res[0][1] = 10;
  			for (int i = 0; i < splited[1].length() - 1; i++)
  			{
  				res[0][1] *= 10;
  			}
  			return res;
  		}
  
  		return res;
  	}
  }
  