<%
	import java.io.File;

	void writeSlidePath(File dir, String prefix)
	{
		if( !isTopLevelFolder(dir) )
		{
			writeSlidePath(dir.getParentFile(), prefix + "../");
			out.print(TOP_BAR_PATH_SEPARATOR);
		}

		if( prefix.length()>0 )
			out.print("<a class=\"path\" href=\"../" + prefix + engine.getIndexPageName() + engine.getPageExtension() + "\">");
		else
			out.print("<a class=\"path\" href=\"../" + firstIndexPage + "\">");

		out.print( getDirectoryTitle(dir) );
		out.print("</a>");
	}

	String getSlideVoiceAnnotationPath()
	{
		File sound = getOriginalVoiceAnnotationFile(new File(imageDirectory, fileName));

		if( sound!=null )
		{
			// JAlbum itself doesn't do a good job on copying the sound files because of the following
			// reason so doing it manually:
			//   - Copying the sound files into the root of the destination folder (instead of "slides")
			//     which causes problems when playing them with WMP (it doesn't support playing from
			//     any other folder than the current because of the security reasons).
			File destDir = new File(outputDirectory.getPath(), "slides/");

			destDir.mkdirs();
			se.datadosen.util.IO.copyFile(sound.getAbsolutePath(), destDir, true);

			if( !outputDirectory.equals(imageDirectory) )
			{
                // Deleting the wrongly copied sound file by JAlbum (the sound files should be located
                // inside "slides"/current folder instead of the root for security reasons)
                File invalidDestVoice = new File(outputDirectory.getPath(), sound.getName());

                if( invalidDestVoice.exists() )
                    invalidDestVoice.delete();
            }
			return sound.getName();
		}

		return null;
	}

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

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

		return vars.get(key);
	}

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

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

		if( background!=null )
		{
			String backgroundPath;

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

			return backgroundPath;
		}

		return null;
	}

	String getSlideInformation(int lineNo)
	{
		String contentInfo = getSlideInformationContent(lineNo);
		
		if( contentInfo==null )
			return null;
		
		StringTokenizer st = new StringTokenizer(contentInfo, ",;|~");
		String result = "";

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

			if( token.equalsIgnoreCase("title") )
				result = appendString(result, getSlideTitle(null), " - ");
			else if( token.equalsIgnoreCase("filename") )
				result = appendString(result, getFileName(null), " - ");
			else if( token.equalsIgnoreCase("filetype") )
				result = appendString(result, "(" + se.datadosen.util.FileFilters.getExtensionOf(files[imageNum-1]) + ")", " - ");
			else if( token.equalsIgnoreCase("dimension") )
				result = appendString(result, getFileDimension(null, true), " - ");
			else if( token.equalsIgnoreCase("slidedimension") )
				result = appendString(result, getFileDimension(null, false), " - ");
			else if( token.equalsIgnoreCase("filesize") )
				result = appendString(result, "(" + getFileSize(null) + ")", " - ");
			else if( token.equalsIgnoreCase("date") )
				result = appendString(result, getSlideDate(null), " - ");
            else if( meta!=void && meta.containsKey(token) )
				result = appendString(result, transformMetadataValue(token, meta.get(token)), " - ");
		}
		return result;
	}

	String getSlideInformationContent(int lineNo)
	{
		String infoContent = readUserVariableAsString(USER_VAR_SLIDE_CAPTION_CONTENT, null, DEFAULT_SLIDE_CAPTION_CONTENT);
		StringBuffer normalizedInfoContent = new StringBuffer();
		int index;
		
		for( int i=0; i<infoContent.length(); i++ )
		{
			char ch = infoContent.charAt(i);
			
			if( ch==';' || ch==',' || ch=='~' )
				normalizedInfoContent.append('|');
			else
				normalizedInfoContent.append(ch);
		}
		
		while( lineNo>0 )
		{
			index = normalizedInfoContent.toString().indexOf("||");

			if( index==-1 || index==normalizedInfoContent.length()-2 )
				return null;
			
			normalizedInfoContent.replace(0, index+2, "");
			lineNo--;
		}

		index = normalizedInfoContent.toString().indexOf("||");

		if( index!=-1 )
			normalizedInfoContent.replace(index, normalizedInfoContent.length(), "");

		return normalizedInfoContent.toString();
	}

	String getSlideCommentHorizontalAlign()
	{
		String align_value = readUserVariableAsString(USER_VAR_SLIDE_COMMENT_HORIZONTAL_ALIGN, imageDirectory, "LEFT"); //The default align is "left"

		if( align_value.trim().equalsIgnoreCase("Center") )
			align = "center";
		else if( align_value.trim().equalsIgnoreCase("Left") )
			align = "left";
		else if( align_value.trim().equalsIgnoreCase("Right") )
			align = "right";
		return align;
	}

	int getSlideThumbnailCount()
	{
		return readUserVariableAsInteger(USER_VAR_SLIDE_PAGE_THUMBNAIL_COUNT, imageDirectory, DEFAULT_SLIDE_PAGE_THUMBNAIL_COUNT);
	}

	void writeThumbnails(boolean next)
	{
		if( isVariableValueEqual( USER_VAR_INCLUDE_SLIDE_PAGE_THUMBNAILS, imageDirectory, false ) )
			return;

		int max_thumb_count = getSlideThumbnailCount();
		int start = next ? imageNum : (imageNum - max_thumb_count - 1);
		int end;

		if( start > totalImages )
			start = totalImages;

		if( start < 0 )
			start = 0;

		end = start + max_thumb_count;

		if( next )
		{
			if( end > totalImages )
				end = totalImages;
		}
		else
		{
			if( end >= imageNum )
				end = imageNum - 1;
		}

		if( end < 0 )
			end = 0;

		if( start==end )
			out.println("<img src=\"$resPath/empty.gif\" width=" + getThumbLinksColumnWidth() + " height=\"1\" border=\"0\" alt=\"\"/>");
		else
		{
			for (int i=start; i<end; i++)
			{
				writeThumbnail(files[i]);
				out.println("<br/>");
			}
		}
	}

	void writeThumbnail(File myfile)
	{
		Map vars = getVariableContainer(myfile);

		if( !vars.containsKey(CACHE_KEY_THUMBNAIL_FRAME_BLOCK) )
			vars.put(CACHE_KEY_THUMBNAIL_FRAME_BLOCK, getThumbnailFrameBlock(myfile));

		out.println( vars.get(CACHE_KEY_THUMBNAIL_FRAME_BLOCK) );
	}

	String getThumbnailFrameBlock(File myfile)
	{
		Map vars = (Map)fileVariables.get(myfile);
		boolean isMovie = isMovieFile(myfile);
		Dimension display_size = new Dimension();
		String tooltip = getThumbnailTooltip(myfile);
		int border_size = getSlideThumbnailBorderSize(myfile);
		String border_color = shouldAddColorToSlideThumbnailBorder(myfile) ? getSlideThumbnailBorderColor(myfile) : null;
		String border_rsc_path = getSlideThumbnailBorderResourcePath(myfile);
		String link_path = vars.get("currentPage");
		String display_path;
		boolean apply_size_on_display = true;
		double thumbnail_ratio = getThumbLinksRatio();
		String mini_sign = null;

		if( isMovie )
		{
			File thumbnail = getMovieThumbnailFile(myfile);
			String icon_set_path = getIconSetPath(myfile);

			if( thumbnail==null )
			{
				display_path = icon_set_path + "/movie.gif";
				apply_size_on_display = false;

				display_size.width = (int) (maxThumbWidth * thumbnail_ratio);
				display_size.height = (int) (maxThumbHeight * thumbnail_ratio);
			}
			else
			{
				display_path = "../thumbs/" + thumbnail.getName();
				display_size = getIconDimension(thumbnail, (int) (maxThumbWidth * thumbnail_ratio), (int) (maxThumbHeight * thumbnail_ratio));
				mini_sign = icon_set_path + "/movie_small.gif";
			}
		}
		else
		{
			display_path = vars.get("thumbPath");

			if( isFixedShapeFilterAppliedOnThumbnails() )
			{
				// Fixed shape filter applied! So we should go for the maximum thumbnail dimensions
                display_size.width = (int) (vars.get("maxThumbWidth") * thumbnail_ratio);
                display_size.height = (int) (vars.get("maxThumbHeight") * thumbnail_ratio);
            }
            else
            {
                display_size.width = (int) (vars.get("thumbWidth") * thumbnail_ratio);
                display_size.height = (int) (vars.get("thumbHeight") * thumbnail_ratio);
            }
		}

		return getFrameBlock(display_path, link_path, false, tooltip, display_size, border_rsc_path, border_size, border_color, false, apply_size_on_display, mini_sign);
	}

	String getDownloadLink()
	{
		boolean isMovie = isMovieFile(null);
	    String download_path = isMovie ? fileName : originalPath;
	    String download_text = "$text.download-file (" + getFileSize(null) + ")";

        return getActionBlock(download_path, "target=\"_blank\"", download_text, getIconSetPath(imageDirectory) + "/download.gif", "align=\"middle\"");
	}

	void writeCurrentSlide()
	{
		File myfile = files[imageNum-1];
		Map vars = (Map)fileVariables.get(myfile);
		boolean isMovie = isMovieFile(myfile);
		Dimension display_size = new Dimension();
		int border_size = getSlideBorderSize(myfile);
		String border_color = shouldAddColorToSlideBorder(myfile) ? getSlideBorderColor(myfile) : null;
		String border_rsc_path = getSlideBorderResourcePath(myfile);
		String link_path;
		String display_path;
		String tooltip = "";

		if( isMovie )
		{
			display_path = myfile.getName();
			display_size = getMovieSlideSize();

			if( !outputDirectory.equals(imageDirectory) )
			{
                // Deleting the wrongly copied movie file by JAlbum (the movies should be located
                // inside "slides"/current folder instead of the root for security reasons)
                File invalidDestMovie = new File(outputDirectory.getPath(), myfile.getName());

                if( invalidDestMovie.exists() )
                    invalidDestMovie.delete();
            }
		}
		else
		{
			if( isVariableValueEqual( USER_VAR_INCLUDE_MOVE_FORWARD_FACILITY, imageDirectory, true ) && engine.getImageLinking()!=se.datadosen.jalbum.AlbumBean.LINK_ORIGINALS_VIA_SCALED )
			{
				if( nextPage!=void )
				{
					link_path = vars.get("nextPage");
					tooltip = getFromResourceBundle("next-slide");
				}
				else
					tooltip = getFromResourceBundle("on-last-slide");
			}
			else
			{
				link_path = vars.get("originalPage");
				if( link_path!=null )
					tooltip = getFromResourceBundle("orig-image");
			}

			display_path = vars.get("imagePath");

			if( isFixedShapeFilterAppliedOnSlides() )
			{
				// Fixed shape filter applied! So we should go for the maximum image dimensions
				display_size.width = vars.get("maxImageWidth");
				display_size.height = vars.get("maxImageHeight");
			}
			else
			{
				display_size.width = vars.get("imageWidth");
				display_size.height = vars.get("imageHeight");
			}
		}

		String frame_block = getFrameBlock(display_path, link_path, false, tooltip, display_size, border_rsc_path, border_size, border_color, isMovie, true, null);

		out.println( frame_block );
	}

	void writeCurrentSlideNavigationBar(String upAction, String slideShowActionId)
	{
		String iconSetPath = getIconSetPath(imageDirectory);

		if( previousPage!=void )
		{
			if( isVariableValueEqual( USER_VAR_INCLUDE_FIRST_LAST_PAGE_ACTIONS, imageDirectory, true ) )
				out.println( getActionBlock("javascript:firstPage()", "$text.first-slide", iconSetPath + "/first.gif") );

			out.println( getActionBlock("javascript:prevPage()", "$text.prev-slide", iconSetPath + "/previous.gif") );
		}
		else
		{
			if( isVariableValueEqual( USER_VAR_INCLUDE_FIRST_LAST_PAGE_ACTIONS, imageDirectory, true ) )
				out.println( getActionBlock(null, "$text.on-first-slide", iconSetPath + "/first_disabled.gif") );

			out.println( getActionBlock(null, "$text.on-first-slide", iconSetPath + "/previous_disabled.gif") );
		}

		out.println( NAVIGATION_BAR_SEPARATOR );

		if( engine.isMetaData() )
		{
			if( hasSlideMetaData() )
				out.println( getActionBlock("javascript:togglePhotographicMetaInformation()", "$text.exif-info", iconSetPath + "/info.gif") );
			else
				out.println( getActionBlock(null, "$text.no-exif-info", iconSetPath + "/info_disabled.gif") );
		}

		out.println( upAction );

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

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

		out.println( NAVIGATION_BAR_SEPARATOR );

		if( nextPage!=void )
		{
			out.println( getActionBlock("javascript:nextPage()", "$text.next-slide", iconSetPath + "/next.gif") );

			if( isVariableValueEqual( USER_VAR_INCLUDE_FIRST_LAST_PAGE_ACTIONS, imageDirectory, true ) )
				out.println( getActionBlock("javascript:lastPage()", "$text.last-slide", iconSetPath + "/last.gif") );
		}
		else
		{
			out.println( getActionBlock(null, "$text.on-last-slide", iconSetPath + "/next_disabled.gif") );

			if( isVariableValueEqual( USER_VAR_INCLUDE_FIRST_LAST_PAGE_ACTIONS, imageDirectory, true ) )
				out.println( getActionBlock(null, "$text.on-last-slide", iconSetPath + "/last_disabled.gif") );
		}
	}

	int getThumbLinksColumnWidth()
	{
		if( isVariableValueEqual( USER_VAR_INCLUDE_SLIDE_PAGE_THUMBNAILS, imageDirectory, false ) )
			return 5;
		else
			return (int) ((maxThumbWidth*getThumbLinksRatio()) + (getSlideThumbnailBorderSize(imageDirectory)*2));
	}

	double getThumbLinksRatio()
	{
		return readUserVariableAsDouble(USER_VAR_SLIDE_PAGE_THUMBNAIL_RATIO, imageDirectory, DEFAULT_SLIDE_PAGE_THUMBNAIL_RATIO);
	}

	Dimension getMovieSlideSize()
	{
		String size = readUserVariableAsString(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);
		}
	}

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

	int getSlideShowDelay()
	{
		int delay = readUserVariableAsInteger(USER_VAR_SLIDE_SHOW_DELAY, imageDirectory, DEFAULT_SLIDE_SHOW_DELAY);

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

	// Generate drop box options for quick navigation
	void writeSlideJumpingMenuItems()
	{
		String template = getSlideJumpingMenuItems();
        int dirTotalIndexes = (totalIndexes!=void) ? totalIndexes : 1;
		Object[] args = new Object[dirTotalIndexes];

		if (indexNum == void)
			indexNum = 1;
		for( int i=0; i<dirTotalIndexes; i++ )
		{
			if( i==indexNum-1 )
				args[i] = "selected=\"selected\"";
			else
				args[i] = "";
		}
		out.println( formatBigTemplate(template, args) );
	}

	String getSlideJumpingMenuItems()
	{
		Map vars = getVariableContainer(imageDirectory);

		if( !vars.containsKey(CACHE_KEY_SLIDE_JUMP_TO_PAGE) )
			vars.put(CACHE_KEY_SLIDE_JUMP_TO_PAGE, getSlideJumpingMenuItemsImpl());

		return vars.get(CACHE_KEY_SLIDE_JUMP_TO_PAGE);
	}

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

		if( isInsideAlbum(parentDir) )
		{
			File[] files = engine.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]);

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

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

		return buffer.toString();
	}

	void writeSlideCurrentFolderPages(StringBuffer buffer, String prefix)
	{
		String template = "<option value=\"{0}\" {1}>" + prefix + getFromResourceBundle("page") + " {2}</option>\n";
        int dirTotalIndexes = (totalIndexes!=void) ? totalIndexes : 1;
		Object[] args = new Object[3];

		if (indexNum == void)
			indexNum = 1;

		for (int i=1; i<=dirTotalIndexes; 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));
		}
	}

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

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

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

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

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

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

		return vars.get(key);
	}

	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 = engine.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;
	}

	boolean isSlideShowValidForCurrentSlide()
	{
		if( isVariableValueEqual( USER_VAR_INCLUDE_SLIDE_SHOW, imageDirectory, true ) )
		{
			if( isVariableValueEqual( USER_VAR_CYCLIC_SLIDE_SHOW, imageDirectory, true ) )
				return true;
			else
				return nextPage!=void;
		}

		return false;
	}

	boolean hasCurrentSlideOriginalSource()
	{
		return( originalPath!=void && !engine.getImageLinking().equals(se.datadosen.jalbum.AlbumBean.LINK_SCALED) );
	}

	void setupOriginalSlide()
	{
		if( !hasCurrentSlideOriginalSource() )
			return;

		String originalPage = originalPath;	// Default if no extra template page
		File template = new File(skinDirectory, "slide-original.htt");

		if (template.exists())
		{
			originalPage = label + "-orig" + engine.getPageExtension();
			engine.processTemplateFile(template, new File(outputDirectory, "slides/" + originalPage), current);
		}

		current.put("originalPage", originalPage);
	}

	boolean hasSlideMetaData()
	{
		// Unfortunately the 'meta' is not null even when the image doesn't contain any EXIF info
		// so I have to check the 'flush' too which is not so safe (I know!) but no better way!
		//return engine.isMetaData() && meta!=void && meta!=null && flash!=void;

		if( !engine.isMetaData() || meta==void || 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;
	}

	String getCurrentSlideFullTitle()
	{
	    File currentFile = files[imageNum-1];

	    return getAlbumPageTitlePrefix() + getSlideTitle(currentFile);
	}
%>