
import java.awt.*;
import java.io.File;
import java.text.MessageFormat;
import java.util.*;

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

public class AbstractSlide extends Common
{
	private static final String PHOTO_METADATA_LINE_TEMPLATE = "<tr valign=\"top\"><td class=\"metainfo-name\">&nbsp;&nbsp;&#x25AA; {0}:</td><td>&nbsp;&nbsp;</td><td class=\"metainfo-value\">{1}</td></tr>";
	private static final String PHOTO_METADATA_HEADER_TEMPLATE = "<tr><td colspan=\"3\" class=\"metainfo-header\">{0}</td></tr>";

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

	public String getMetaDataInformation()
	{
		String metadataMode = readUserVariableAsString(Constants.USER_VAR_METADATA_DISPLAY_MODE, imageDirectory, Constants.DEFAULT_METADATA_DISPLAY_MODE);
	    SmartResourceBundle cameraResource = getResourceBundle(Constants.SKIN_PHOTO_METADATA_RESOURCE_BUNDLE_NAME, Constants.CACHE_KEY_PHOTO_METADATA_RESOURCE_BUNDLE);
		StringBuffer buffer = new StringBuffer();

		if( metadataMode.equalsIgnoreCase("all") )
		{
		    buffer.append( getMetaDataInformationImpl("meta-general-tags", null, false, cameraResource, false) );
		    buffer.append( getMetaDataInformationImpl("meta-maker-tags", "Makernote", true, cameraResource, false) );
		    buffer.append( getMetaDataInformationImpl("meta-iptc-tags", "Iptc", false, cameraResource, false) );
		    buffer.append( getMetaDataInformationImpl("meta-jpeg-tags", "Jpeg", false, cameraResource, false) );
		}
		else if( metadataMode.equalsIgnoreCase("verbose") )
		{
		    buffer.append( getMetaDataInformationImpl("meta-general-tags", null, false, cameraResource, true) );
		    buffer.append( getMetaDataInformationImpl("meta-maker-tags", "Makernote", false, cameraResource, true) );
		    buffer.append( getMetaDataInformationImpl("meta-iptc-tags", "Iptc", false, cameraResource, true) );
		    buffer.append( getMetaDataInformationImpl("meta-jpeg-tags", "Jpeg", false, cameraResource, true) );
		}
		else if( metadataMode.equalsIgnoreCase("iptc-only") )
		{
		    buffer.append( getMetaDataInformationImpl("meta-iptc-tags", "Iptc", false, cameraResource, true) );
		}
		else if( metadataMode.equalsIgnoreCase("custom") )
		{
			String customFields = readUserVariableAsString(Constants.USER_VAR_CUSTOM_METADATA_FIELDS, imageDirectory, Constants.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())};

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

					buffer.append( MessageFormat.format(PHOTO_METADATA_LINE_TEMPLATE, args) );
				}
			}
		}

		return buffer.toString();
	}

	private String getMetaDataInformationImpl(String seriesHeaderKey, String seriesName, boolean includeUnknown, SmartResourceBundle cameraResource, boolean transformKey)
	{
		String seriesHeader = texts.getString(seriesHeaderKey);
        Iterator it = meta.entrySet().iterator();
        boolean headerPrinted = false;
		StringBuffer buffer = new StringBuffer();

        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("Unknown")!=-1 )
                continue;

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

		return buffer.toString();
	}

	public String getSlideTopPathBar()
	{
		Map vars = getVariableContainer(imageDirectory);

		if( !vars.containsKey(Constants.CACHE_KEY_SLIDE_TOP_PATH_BAR) )
			vars.put(Constants.CACHE_KEY_SLIDE_TOP_PATH_BAR, getSlideTopPathBarImpl(imageDirectory, ""));

		return (String)vars.get(Constants.CACHE_KEY_SLIDE_TOP_PATH_BAR);
	}

	private String getSlideTopPathBarImpl(File dir, String prefix)
	{
		StringBuffer buffer = new StringBuffer();
		String title = getDirectoryTitle(dir);
		int max_length = getMaximumFolderLengthOnTopPathBar();

		if( !isTopLevelFolder(dir) )
		{
			buffer.append( getSlideTopPathBarImpl(dir.getParentFile(), prefix + "../") );
			buffer.append( Constants.SKIN_TOP_BAR_PATH_SEPARATOR );
		}

		buffer.append( "<a class=\"path\" href=\"../" );
		if( prefix.length()>0 )
		{
			buffer.append( prefix );
			buffer.append( engine.getIndexPageName() );
			buffer.append( engine.getPageExtension() );
		}
		else
			buffer.append( firstIndexPage );
		buffer.append( "\"" );

		if( max_length>0 && title.length()>max_length )
		{
			buffer.append( " title=\"" );
			buffer.append( clearHtmlCode(title) );
			buffer.append( "\"" );
			title = title.substring(0, max_length) + Constants.SKIN_TRUNCATED_TEXT_POSTFIX;
		}
		buffer.append( ">" );
		buffer.append( title );
		buffer.append( "</a>" );
		return buffer.toString();
	}

	public String getSlideBackgroundPath()
	{
		Map vars = getVariableContainer(imageDirectory);
		String key = Constants.CACHE_KEY_PREFIX + Constants.META_SLIDE_BACKGROUND_IMAGE;

		if( !vars.containsKey(key) )
			vars.put(key, getSlideBackgroundPathImpl());

		return (String)vars.get(key);
	}

	private String getSlideBackgroundPathImpl()
	{
		File background = getBackgroundImageFile(Constants.META_SLIDE_BACKGROUND_IMAGE, imageDirectory);

		if( background==null )
			background = getBackgroundImageFile(Constants.META_INDEX_BACKGROUND_IMAGE, imageDirectory);

		if( background==null )
			return null;

		String backgroundPath;

		if( !outputDirectory.equals(imageDirectory) || !isInsideAlbum(background) )
		{
			copyFile(background, outputDirectory);
			backgroundPath = "../" + background.getName();
		}
		else
			backgroundPath = se.datadosen.util.IO.relativePath(background, new File(outputDirectory, engine.getSlideDirectory()));

		return backgroundPath;
	}

	public String getCurrentSlideNavigationBar(String upAction, String slideShowActionId)
	{
		String iconSetPath = getIconSetPath(imageDirectory);
		StringBuffer buffer = new StringBuffer();

		if( !isEmptyString(previousPage) )
		{
			if( isVariableValueEqual( Constants.USER_VAR_INCLUDE_FIRST_LAST_PAGE_ACTIONS, imageDirectory, true ) )
			{
				buffer.append( getActionBlock("javascript:firstPage()", texts.getString("first-slide"), iconSetPath + "/first.gif") );
				buffer.append('\n');
			}

			buffer.append( getActionBlock("javascript:prevPage()", texts.getString("prev-slide"), iconSetPath + "/previous.gif") );
			buffer.append('\n');
		}
		else
		{
			if( isVariableValueEqual( Constants.USER_VAR_INCLUDE_FIRST_LAST_PAGE_ACTIONS, imageDirectory, true ) )
			{
				buffer.append( getActionBlock(null, texts.getString("on-first-slide"), iconSetPath + "/first_disabled.gif") );
				buffer.append('\n');
			}

			buffer.append( getActionBlock(null, texts.getString("on-first-slide"), iconSetPath + "/previous_disabled.gif") );
			buffer.append('\n');
		}

		buffer.append( Constants.SKIN_NAVIGATION_BAR_SEPARATOR );
		buffer.append('\n');

		if( engine.isMetaData() )
		{
			if( hasSlideMetaData() )
				buffer.append( getActionBlock("javascript:toggleElementVisibility('photometainfo')", texts.getString("exif-info"), iconSetPath + "/info.gif") );
			else
				buffer.append( getActionBlock(null, texts.getString("no-exif-info"), iconSetPath + "/info_disabled.gif") );
			buffer.append('\n');
		}

		buffer.append( upAction );
		buffer.append('\n');

		if( isVariableValueEqual( Constants.USER_VAR_INCLUDE_SLIDE_SHOW, imageDirectory, true ) )
		{
			String extraParams = "id=\"" + slideShowActionId + "\"";

			buffer.append( getActionBlock("javascript:toggleSlideShow(exp);", null, texts.getString("slide-show"), "", extraParams) );
			buffer.append('\n');
			buffer.append( "<script language=\"javascript\" type=\"text/javascript\">");
			buffer.append('\n');
			if( isSlideShowValidForCurrentSlide() )
				buffer.append("setSlideShowStatus( getCookie('slideShowOn')!=null );");
			else
				buffer.append("setSlideShowStatus( false );");
			buffer.append('\n');
			buffer.append("</script>");
			buffer.append('\n');
		}

		buffer.append( Constants.SKIN_NAVIGATION_BAR_SEPARATOR );
		buffer.append('\n');

		if( !isEmptyString(nextPage) )
		{
			buffer.append( getActionBlock("javascript:nextPage()", texts.getString("next-slide"), iconSetPath + "/next.gif") );
			buffer.append('\n');

			if( isVariableValueEqual( Constants.USER_VAR_INCLUDE_FIRST_LAST_PAGE_ACTIONS, imageDirectory, true ) )
			{
				buffer.append( getActionBlock("javascript:lastPage()", texts.getString("last-slide"), iconSetPath + "/last.gif") );
				buffer.append('\n');
			}
		}
		else
		{
			buffer.append( getActionBlock(null, texts.getString("on-last-slide"), iconSetPath + "/next_disabled.gif") );
			buffer.append('\n');

			if( isVariableValueEqual( Constants.USER_VAR_INCLUDE_FIRST_LAST_PAGE_ACTIONS, imageDirectory, true ) )
			{
				buffer.append( getActionBlock(null, texts.getString("on-last-slide"), iconSetPath + "/last_disabled.gif") );
				buffer.append('\n');
			}
		}

		return buffer.toString();
	}

	protected Dimension getMovieSlideSize()
	{
		String size = readUserVariableAsString(Constants.USER_VAR_MOVIE_SLIDE_DIMENSION, imageDirectory, null);

		try
		{
			Dimension display_size = new Dimension();
			int index = size.toLowerCase().indexOf("x");

			if( index<=0 || index==size.length()-1 )
				throw new IllegalArgumentException();

			display_size.width = Integer.parseInt( size.substring(0, index) );
			display_size.height = Integer.parseInt( size.substring(index+1) );
			return display_size;
		}
		catch( Exception e )
		{
			return new Dimension(maxImageWidth, maxImageHeight);
		}
	}

	public boolean hasComment()
	{
		String int_comment = getSlideInternalComment(null);
		String ext_comment = getSlideExternalComment(null, true);

		return( (int_comment!=null && !int_comment.trim().equals("")) || (ext_comment!=null && !ext_comment.trim().equals("")) );
	}

	public int getSlideShowDelay()
	{
		if( isMovieFile(currentFile) )
			return readUserVariableAsInteger(Constants.USER_VAR_SLIDE_SHOW_MOVIE_DELAY, imageDirectory, Constants.DEFAULT_SLIDE_SHOW_MOVIE_DELAY);
		else
		{
			int delay = readUserVariableAsInteger(Constants.USER_VAR_SLIDE_SHOW_DELAY, imageDirectory, Constants.DEFAULT_SLIDE_SHOW_DELAY);

			if( hasComment() )
				return readUserVariableAsInteger(Constants.USER_VAR_SLIDE_SHOW_COMMENT_DELAY, imageDirectory, delay);
			else
				return delay;
		}
	}

	// Generate drop box options for quick navigation
	public String getJumpingMenuItemsContent()
	{
		String template = getJumpingMenuItems();
		Object[] args = new Object[totalIndexes];

		for( int i=0; i<totalIndexes; i++ )
		{
			if( i==indexNum-1 )
				args[i] = "selected=\"selected\"";
			else
				args[i] = "";
		}
		return formatBigTemplate(template, args);
	}

	private String getJumpingMenuItems()
	{
		Map vars = getVariableContainer(imageDirectory);

		if( !vars.containsKey(Constants.CACHE_KEY_SLIDE_JUMP_TO_PAGE) )
			vars.put(Constants.CACHE_KEY_SLIDE_JUMP_TO_PAGE, getJumpingMenuItemsImpl());

		return (String)vars.get(Constants.CACHE_KEY_SLIDE_JUMP_TO_PAGE);
	}

	private String getJumpingMenuItemsImpl()
	{
		// Make links to sibling directories
		StringBuffer buffer = new StringBuffer();
		File parentDir = imageDirectory.getParentFile();
		String template = "<option value=\"{0}\" {1}>{2}</option>\n";
		int max_length = getMaximumFolderLengthOnTopPathBar();
		Object[] args = new Object[3];
		String title;

		if( isInsideAlbum(parentDir) )
		{
			File[] files = listFiles(parentDir, true, programDirectory);

			for( int i=0; i<files.length; i++ )
			{
				if( !files[i].isDirectory() )
					continue;

				File first_slide = getFirstSlideOfFolder(files[i]);

				if( first_slide==null )
					continue;

				boolean multiIndexes = hasFolderMultiIndexes(files[i]);

				title = getDirectoryTitle(files[i]);
				args[0] = "../../" + encodeUrlIfNecessary(files[i].getName()) + "/" + engine.getSlideDirectory() + "/" + encodeUrlIfNecessary(getLabel(first_slide)) + engine.getPageExtension();
				if( files[i].equals(imageDirectory) )
				{
					args[1] = (!multiIndexes) ? "selected=\"selected\"" : "";
					args[2] = "- ";
				}
				else
				{
					args[1] = "";
					args[2] = "+ ";
				}

				if( max_length>0 && title.length()>max_length )
					title = title.substring(0, max_length) + Constants.SKIN_TRUNCATED_TEXT_POSTFIX;
				args[2] = args[2] + title;

				buffer.append(MessageFormat.format(template, args));
				if( files[i].equals(imageDirectory) && multiIndexes )
					writeSlideCurrentFolderPages(buffer, Constants.SKIN_JUMPING_MENU_HIERARCHY_SEPARATOR + "- ");
			}
		}
		else
			writeSlideCurrentFolderPages(buffer, "- ");

		return buffer.toString();
	}

	private void writeSlideCurrentFolderPages(StringBuffer buffer, String prefix)
	{
		String template = "<option value=\"{0}\" {1}>" + prefix + texts.getString("page") + " {2}</option>\n";
		Object[] args = new Object[3];

		for (int i=1; i<=totalIndexes; i++)
		{
			File first_slide = getFirstSlideOfIndexPage(imageDirectory, i);

			if( first_slide==null )
				continue;

			Map slide_vars = (Map)getVariableContainer(first_slide);

			if( slide_vars==null )
				continue;

			args[0] = slide_vars.get("currentPage");
			args[1] = "{" + (i-1) + "}";
			args[2] = "" + i;

			buffer.append(MessageFormat.format(template, args));
		}
	}

	private int getTotalIndexes(File dir)
	{
		File[] allFiles = listFiles(dir, true, dir);
		double result = ((double)allFiles.length) / (cols * rows);

		return (int) Math.ceil( result );
	}

	private boolean hasFolderMultiIndexes(File dir)
	{
		int dirTotalIndexes = getTotalIndexes(dir);

		if( dirTotalIndexes==1 )
			return false;

		for( int i=1; i<=dirTotalIndexes; i++ )
		{
			File first_slide = getFirstSlideOfIndexPage(dir, i);

			if( first_slide!=null )
				return( i<dirTotalIndexes );
		}

		return false;
	}

	private File getFirstSlideOfFolder(File dir)
	{
		int dirTotalIndexes = getTotalIndexes(dir);

		for( int i=1; i<=dirTotalIndexes; i++ )
		{
			File first_slide = getFirstSlideOfIndexPage(dir, i);

			if( first_slide!=null )
				return first_slide;
		}

		return null;
	}

	private File getFirstSlideOfIndexPage(File dir, int indexPageNo)
	{
		Map vars = getVariableContainer(dir);
		String key = Constants.CACHE_KEY_FIRST_SLIDE_OF_INDEX_PAGE + indexPageNo;

		if( !vars.containsKey(key) )
			vars.put(key, getFirstSlideOfIndexPageImpl(dir, indexPageNo));

		return (File)vars.get(key);
	}

	private File getFirstSlideOfIndexPageImpl(File src_dir, int indexPageNo)
	{
		int slide_no = ((indexPageNo - 1) * (cols * rows)); // pointing to the first slide of each index page
		File[] allFiles = listFiles(src_dir, true, src_dir);

		if( slide_no>=allFiles.length )
			return null;

		File slide = allFiles[slide_no];
		int i = 0;

		while( slide.isDirectory() )
		{
			i++;
			if( ((slide_no+i) == allFiles.length) || (i==(cols * rows)) )
			{
				slide = null;
				break;
			}
			slide = allFiles[slide_no+i];
		}

		return slide;
	}

	public boolean isSlideShowValidForCurrentSlide()
	{
		if( isVariableValueEqual( Constants.USER_VAR_INCLUDE_SLIDE_SHOW, imageDirectory, true ) )
		{
			if( isVariableValueEqual( Constants.USER_VAR_CYCLIC_SLIDE_SHOW, imageDirectory, true ) )
				return true;
			else
				return !isEmptyString(nextPage);
		}

		return false;
	}

	public boolean hasSlideMetaData()
	{
		if( !engine.isMetaData() || meta==null )
			return false;

		Iterator it = meta.entrySet().iterator();

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

			if( key.indexOf("Unknown")==-1 && key.indexOf("Jpeg")==-1 && !isEmptyString(entry.getValue()) )
				return true;
		}

		return false;
	}

	public String getCurrentSlideFullTitle()
	{
		return getAlbumPageTitlePrefix() + getSlideTitle(currentFile);
	}
}
