
import se.datadosen.jalbum.*;

import java.awt.*;
import java.awt.image.*;
import java.io.*;
import java.net.MalformedURLException;
import java.util.*;
import java.util.List;

public class BPPFrameHandler extends Common implements JAFilter, ModifiesSize
{
	private static final String KEY_PREFIX = "filter";

	public static final int APPLY_LEVEL_SLIDE = 2;
	public static final int APPLY_LEVEL_THUMBNAIL = 3;
	public static final int APPLY_LEVEL_FOLDER = 4;
	public static final int APPLY_LEVEL_MOVIE_SLIDE = 5;

	private Map filterGroupMap = new HashMap();
	private int applyLevel = -1;
	private int applyRatio = 100;
	private File configDir;

	public BPPFrameHandler(AlbumBean engine, int applyLevel)
	{
		this( engine, applyLevel, null );
	}

	private BPPFrameHandler(AlbumBean engine, int applyLevel, File configDir)
	{
		super( engine );

		if( configDir==null )
			configDir = new File(skinDirectory, Constants.SKIN_FRAME_CONFIG_DIR);

		if( !configDir.isDirectory() )
			throw new IllegalArgumentException("The 'configDir' parameter must be a directory!");

		if( applyLevel!=APPLY_LEVEL_FOLDER && applyLevel!=APPLY_LEVEL_SLIDE && applyLevel!=APPLY_LEVEL_THUMBNAIL && applyLevel!=APPLY_LEVEL_MOVIE_SLIDE)
			throw new IllegalArgumentException("The 'applyLevel' parameter is not valid!");

		this.configDir = configDir;
		this.applyLevel = applyLevel;

		switch( applyLevel )
		{
			case APPLY_LEVEL_FOLDER:
				applyRatio = getFolderThumbnailFrameRatio(rootImageDirectory);
				break;
			case APPLY_LEVEL_SLIDE:
				applyRatio = getSlideFrameRatio(rootImageDirectory);
				break;
			case APPLY_LEVEL_THUMBNAIL:
				applyRatio = getSlideThumbnailFrameRatio(rootImageDirectory);
				break;
			case APPLY_LEVEL_MOVIE_SLIDE:
				applyRatio = getMovieSlideFrameRatio(rootImageDirectory);
				break;
			default:
				throw new IllegalArgumentException("The 'applyLevel' parameter is not valid!");
		}
	}

	public void updateVars()
	{
		super.updateVars();

		Iterator it = filterGroupMap.values().iterator();

		while( it.hasNext() )
			((BPPFilterGroup) it.next()).updateVars();
	}

	private void processFilterGroup(File file) throws Exception
	{
		String key = file.getName().substring(0, file.getName().toLowerCase().indexOf(Constants.SKIN_FRAME_CONFIG_EXTENSION));
		BPPFilterGroup result = new BPPFilterGroup(engine, key, applyLevel);

		if( file.exists() )
		{
			Iterator it = buildPropertiesList(file).iterator();

			while( it.hasNext() )
				result.addFilter((BPPFilterProperties) it.next());
		}

		filterGroupMap.put(key, result);
	}

	private List buildPropertiesList(File file) throws IOException
	{
		BufferedReader reader = new BufferedReader(new FileReader(file));
		List result = new LinkedList();
		String line, key, value, prop_key;
		int equal_index, dot_index;

		while( reader.ready() )
		{
			BPPFilterProperties prop = null;

			line = reader.readLine();

			if( line==null )
				break;
			if( line.startsWith("#") )
				continue;
			equal_index = line.indexOf('=');

			if( equal_index<=0 || equal_index==line.length() - 1 )
				continue;

			key = line.substring(0, equal_index);
			value = line.substring(equal_index + 1);

			if( key.toLowerCase().indexOf(KEY_PREFIX)!=0 )
				continue;

			dot_index = key.indexOf('.');
			if( equal_index<=KEY_PREFIX.length() || equal_index==key.length() - 1 )
				continue;

			prop_key = key.substring(0, dot_index).toLowerCase();
			for( int i=0; i<result.size(); i++ )
			{
				BPPFilterProperties temp = (BPPFilterProperties)result.get(i);

				if( temp.getName().equals(prop_key) )
				{
					prop = temp;
					break;
				}
			}
			if( prop==null )
			{
				prop = new BPPFilterProperties(prop_key);
				result.add(prop);
			}

			key = key.substring(dot_index + 1);
			prop.put(key, transformValue(key, value).trim());
		}

		return result;
	}

	private String transformValue(String key, String value)
	{
		if( key.equalsIgnoreCase(BPPFilterProperties.FILTER_BACKGROUND_FILE_ATTRIBUTE_NAME) )
		{
			if( !value.equalsIgnoreCase(BPPFilterProperties.STYLE_BACKGROUND_FILE) )
				return new File(configDir, value).getAbsolutePath();
		}
		else if( key.equalsIgnoreCase("src") )
		{
			File srcFile = new File(configDir, value);

			try
			{
				return srcFile.toURL().toString();
			}
			catch( MalformedURLException e )
			{
				return srcFile.getAbsolutePath();
			}
		}
		else if( applyRatio!=100 )
		{
			if( key.toLowerCase().indexOf("width")!=-1 || key.toLowerCase().indexOf("height")!=-1 ||
				key.toLowerCase().indexOf("thick")!=-1 || key.toLowerCase().indexOf("margin")!=-1 ||
				key.indexOf("X")!=-1 || key.indexOf("Y")!=-1 )
			{
				StringTokenizer st = new StringTokenizer(value, ",");
				String result = "";

				while( st.hasMoreTokens() )
				{
					String token = st.nextToken().trim();

					if( isNumber(token) )
					{
						double numValue = Double.parseDouble(token.trim());
						long convertedValue = (long) ((numValue * applyRatio) / 100);

						if( convertedValue==0 && numValue!=0 )
							convertedValue = (long) numValue;
						result += String.valueOf(convertedValue);
					}
					if( st.hasMoreTokens() )
						result += ",";
				}
				return result;
			}
		}

		return value;
	}

	private boolean isNumber( String value )
	{
		if( isEmptyString(value) )
			return false;

		for( int i=0; i<value.length(); i++ )
		{
			char ch = value.charAt(i);

			if( !Character.isDigit( ch ) && ch!='.' )
				return false;
		}
		return true;
	}

	private File getCurrentFile(Map vars)
	{
		File[] files = (File[]) vars.get("files");
		if( files==null )
			return null;
		int imageNum = ((Integer) vars.get("imageNum")).intValue();

		return files[imageNum - 1];
	}

	private BPPFilterGroup fetchFiltersToApply(File currentFile)
	{
		String frameType = "";

		switch( applyLevel )
		{
			case APPLY_LEVEL_FOLDER:
				frameType = getFolderThumbnailFrameType((currentFile!=null) ? currentFile.getParentFile() : null);
				break;
			case APPLY_LEVEL_SLIDE:
				frameType = getSlideFrameType((currentFile!=null) ? currentFile.getParentFile() : null);
				break;
			case APPLY_LEVEL_THUMBNAIL:
				frameType = getSlideThumbnailFrameType((currentFile!=null) ? currentFile.getParentFile() : null);
				break;
			case APPLY_LEVEL_MOVIE_SLIDE:
				frameType = getMovieSlideFrameType((currentFile!=null) ? currentFile.getParentFile() : null);
				break;
		}

		if( !filterGroupMap.containsKey(frameType) )
		{
			File file = new File(configDir, frameType + Constants.SKIN_FRAME_CONFIG_EXTENSION);

			try
			{
				processFilterGroup(file);
			}
			catch( Exception e )
			{
				log(e);
			}
		}

		return (BPPFilterGroup)filterGroupMap.get(frameType);
	}

	private String getSlideFrameType(File dir)
	{
		return readUserVariableAsString(Constants.USER_VAR_SLIDE_FRAME_TYPE, dir, Constants.DEFAULT_SLIDE_FRAME_TYPE);
	}

	private int getSlideFrameRatio(File dir)
	{
		return readUserVariableAsInteger(Constants.USER_VAR_SLIDE_FRAME_RATIO, dir, 100);
	}

	private String getMovieSlideFrameType(File dir)
	{
		return readUserVariableAsString(Constants.USER_VAR_MOVIE_SLIDE_FRAME_TYPE, dir, Constants.DEFAULT_MOVIE_SLIDE_FRAME_TYPE);
	}

	private int getMovieSlideFrameRatio(File dir)
	{
		return readUserVariableAsInteger(Constants.USER_VAR_MOVIE_SLIDE_FRAME_RATIO, dir, 100);
	}

	private String getSlideThumbnailFrameType(File dir)
	{
		return readUserVariableAsString(Constants.USER_VAR_SLIDE_THUMBNAIL_FRAME_TYPE, dir, Constants.DEFAULT_SLIDE_THUMBNAIL_FRAME_TYPE);
	}

	private int getSlideThumbnailFrameRatio(File dir)
	{
		return readUserVariableAsInteger(Constants.USER_VAR_SLIDE_THUMBNAIL_FRAME_RATIO, dir, 100);
	}

	private String getFolderThumbnailFrameType(File dir)
	{
		return readUserVariableAsString(Constants.USER_VAR_FOLDER_THUMBNAIL_FRAME_TYPE, dir, Constants.DEFAULT_FOLDER_THUMBNAIL_FRAME_TYPE);
	}

	private int getFolderThumbnailFrameRatio(File dir)
	{
		return readUserVariableAsInteger(Constants.USER_VAR_FOLDER_THUMBNAIL_FRAME_RATIO, dir, 100);
	}

	public String getDescription()
	{
		return "BPP Frame Delegation Filter";
	}

	public String getName()
	{
		return "BPP Frame Filter";
	}

	public BufferedImage filter(BufferedImage bufferedImage, Map vars)
	{
		BPPFilterGroup filterGroup = fetchFiltersToApply( getCurrentFile(vars) );

		if( filterGroup==null )
			return bufferedImage;
		else
			return filterGroup.filter(bufferedImage, vars);
	}

	public Dimension getModifiedSize(Dimension dimension, Map vars)
	{
		BPPFilterGroup filterGroup = fetchFiltersToApply( getCurrentFile(vars) );

		if( filterGroup==null )
			return dimension;
		else
			return filterGroup.getModifiedSize(dimension, vars);
	}
}
