
import se.datadosen.jalbum.AlbumBean;
import se.datadosen.jalbum.AlbumImage;
import se.datadosen.tags.ElementException;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.*;

public class Slide extends AbstractSlide
{
	private static final String MOVIE_FRAME_BLOCK_TEMPLATE = "<table align=\"center\" width=\"{0}\" height=\"{1}\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" background=\"{2}\">\n" +
			"\t<tr>\n\t\t<td align=\"center\" width=\"{0}\" height=\"{1}\">\n\t\t\t{3}\t\t</td>\n\t</tr>\n</table>";

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

	public String getSlideVoiceAnnotationConsole()
	{
		String voicePath = getSlideVoiceAnnotationPath();

		if( voicePath==null )
			return null;

		if( isVariableValueEqual( Constants.USER_VAR_SHOW_AUDIO_CONSOLE, imageDirectory, true ) )
		{
			Dimension audioConsoleSize = getAudioConsoleSize();
			Object[] args = new Object[] {voicePath, new Integer(audioConsoleSize.width), new Integer(audioConsoleSize.height)};

			return MessageFormat.format(MediaUtils.getAudioBlockTemplate(), args);
		}
		else
			return MessageFormat.format(MediaUtils.getAudioHiddenBlockTemplate(), new Object[] {voicePath});
	}

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

		if( sound==null )
			return 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(), engine.getCloseupDirectory() + "/");

		destDir.mkdirs();
		GeneralUtils.copyFile(sound, destDir);

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

	public 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 = GeneralUtils.appendString(result, getSlideTitle(null), " - ");
			else if( token.equalsIgnoreCase("filename") )
				result = GeneralUtils.appendString(result, getFileName(null), " - ");
			else if( token.equalsIgnoreCase("filetype") )
				result = GeneralUtils.appendString(result, "(" + se.datadosen.util.FileFilters.getExtensionOf(currentFile) + ")", " - ");
			else if( token.equalsIgnoreCase("dimension") )
				result = GeneralUtils.appendString(result, getFileDimension(null, true), " - ");
			else if( token.equalsIgnoreCase("slidedimension") )
				result = GeneralUtils.appendString(result, getFileDimension(null, false), " - ");
			else if( token.equalsIgnoreCase("filesize") )
				result = GeneralUtils.appendString(result, "(" + getFileSize(null) + ")", " - ");
			else if( token.equalsIgnoreCase("date") )
				result = GeneralUtils.appendString(result, getSlideDate(null), " - ");
			else if( meta!=null && meta.containsKey(token) )
				result = GeneralUtils.appendString(result, transformMetadataValue(token, meta.get(token)), " - ");
		}
		return result;
	}

	private String getSlideInformationContent(int lineNo)
	{
		String infoContent = readUserVariableAsString(Constants.USER_VAR_SLIDE_CAPTION_CONTENT, null, Constants.DEFAULT_SLIDE_CAPTION_CONTENT);

		return getInformationContentPieces( infoContent, lineNo );
	}

	private String getInformationContentPieces( String infoContent, int lineNo )
	{
		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();
	}

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

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

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

	private double getThumbLinksRatio()
	{
		return readUserVariableAsDouble(Constants.USER_VAR_SLIDE_PAGE_THUMBNAIL_RATIO, imageDirectory, Constants.DEFAULT_SLIDE_PAGE_THUMBNAIL_RATIO) / 100;
	}

	public int getThumbLinksColumnWidth()
	{
		if( isVariableValueEqual( Constants.USER_VAR_INCLUDE_SLIDE_PAGE_THUMBNAILS, imageDirectory, false ) )
			return 5;
		else
		{
			//return (int) (maxThumbWidth*getThumbLinksRatio());
			//TODO!
			//return (int) ((maxThumbWidth*getThumbLinksRatio()) + (getSlideThumbnailFrameSize(imageDirectory)*2));

			BPPFrameHandler thumbFrame = ( BPPFrameHandler ) album.get( Constants.CACHE_KEY_THUMBNAIL_FRAME_FILTER );
			Dimension finalDimension = thumbFrame.getModifiedSize( new Dimension(maxThumbWidth, maxThumbHeight), current );

			return (int) (finalDimension.width*getThumbLinksRatio());
		}
	}

	public String getThumbnails(boolean next)
	{
		if( isVariableValueEqual( Constants.USER_VAR_INCLUDE_SLIDE_PAGE_THUMBNAILS, imageDirectory, false ) )
			return "";

		int maxThumbCount = getSlideThumbnailCount();
		int start = next ? imageNum : (imageNum - maxThumbCount - 1);
		int end;
		StringBuffer buffer = new StringBuffer();

		if( start > totalImages )
			start = totalImages;

		if( start < 0 )
			start = 0;

		end = start + maxThumbCount;

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

		if( end < 0 )
			end = 0;

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

		return buffer.toString();
	}

	private String getThumbnailFrameBlock(File myfile)
	{
		Map vars = getVariableContainer(myfile);

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

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

	private String getThumbnailFrameBlockImpl(File myfile)
	{
		Map vars = (Map)fileVariables.get(myfile);
		Dimension display_size = new Dimension();
		boolean apply_size_on_display = true;
		double thumbnail_ratio = getThumbLinksRatio();
		String display_path;

		if( isMovieFile(myfile) )
		{
			File thumbnail = getMovieThumbnailFile(myfile);

			if( thumbnail==null )
			{
				display_path = "../" + getBPPGeneratedImageDirectory() + Constants.SKIN_MOVIE_STANDARD_GENERATED_THUMBNAIL;
				apply_size_on_display = false;
			}
			else
				display_path = "../" + getBPPGeneratedImageDirectory() + GeneralUtils.replaceFileExtension(thumbnail.getName(), Constants.SKIN_MOVIE_THUMBNAIL_POSTFIX);

			display_size = GeneralUtils.getImageDimension(new File(outputDirectory, engine.getSlideDirectory() + "/" + display_path), maxThumbWidth, maxThumbHeight);
		}
		else
		{
			display_path = (String)vars.get("thumbPath");
			display_size.width = ((Integer)vars.get("thumbWidth")).intValue();
			display_size.height = ((Integer)vars.get("thumbHeight")).intValue();
		}

		String tooltip = getThumbnailTooltip(myfile);
		String link_path = (String)vars.get("currentPage");

		display_size.width *= thumbnail_ratio;
		display_size.height *= thumbnail_ratio;
		return getFrameBlock(display_path, link_path, false, tooltip, display_size, apply_size_on_display);
	}

	public String getDownloadLink()
	{
		String download_text = texts.getString("download-file") + " (" + getFileSize(null) + ")";
		String download_path = isMovieFile(null) || isAudioFile(null) ? fileName : originalPath;

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

	public boolean hasCurrentSlideDownloadLink()
	{
		return( engine.getImageLinking().equals(AlbumBean.LINK_ORIGINALS_VIA_SCALED) || copyOriginals );
	}

	private String getMovieFrameBlock(File movie)
	{
		BPPFrameHandler movieSlideFrame = (BPPFrameHandler)album.get(Constants.CACHE_KEY_MOVIE_SLIDE_FRAME_FILTER);
		Dimension movieSlideSize = getMovieSlideSize();
		BufferedImage bufferedImage = new BufferedImage(movieSlideSize.width, movieSlideSize.height, BufferedImage.TYPE_INT_RGB);
		AlbumImage albumImage = new AlbumImage(bufferedImage, engine);
		Map vars = (Map)fileVariables.get(movie);
		String movieFrameFileName = getBPPGeneratedImageDirectory() + getLabel(movie) + Constants.SKIN_MOVIE_FRAME_POSTFIX;
		Dimension movieFrameSize = movieSlideFrame.getModifiedSize(movieSlideSize, vars);
		Object[] args = new Object[] {new Integer(movieFrameSize.width), new Integer(movieFrameSize.height), "../" + movieFrameFileName, getMovieBlock(movie.getName(), movieSlideSize)};

		try
		{
			albumImage = albumImage.applyFilter(movieSlideFrame, vars);
			albumImage.saveJPEG( GeneralUtils.ensureParentFoldersExist(new File(outputDirectory, movieFrameFileName)), engine.getQualityPercent() );
		}
		catch( IOException e )
		{
			log(e);
		}
		return MessageFormat.format(MOVIE_FRAME_BLOCK_TEMPLATE, args);
	}

	public String getCurrentSlideContent()
	{
		if( isMovieFile(currentFile) )
		{
			fixMediaLocation( currentFile );
			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(), currentFile.getName());

				if( invalidDestMovie.exists() )
					invalidDestMovie.delete();
			}

			return getMovieFrameBlock(currentFile);
		}
		else if( isAudioFile(currentFile) )
		{
			fixMediaLocation( currentFile );
			if( !outputDirectory.equals(imageDirectory) )
			{
				// Deleting the wrongly copied standalone-audio file by JAlbum (the standalone-audios should be located
				// inside "slides"/current folder instead of the root for security reasons)
				File invalidDestAudio = new File(outputDirectory.getPath(), currentFile.getName());

				if( invalidDestAudio.exists() )
					invalidDestAudio.delete();
			}

			Dimension audioConsoleSize = getAudioConsoleSize();
			Object[] args = new Object[] {currentFile.getName(), new Integer(audioConsoleSize.width), new Integer(audioConsoleSize.height)};

			return MessageFormat.format(MediaUtils.getAudioBlockTemplate(), args);
		}
		else
		{
			Map vars = (Map)fileVariables.get(currentFile);
			Dimension displaySize = new Dimension();
			String linkPath = "";
			String tooltip = "";

			if( isVariableValueEqual( Constants.USER_VAR_INCLUDE_MOVE_FORWARD_FACILITY, imageDirectory, true ) && !engine.getImageLinking().equals(AlbumBean.LINK_ORIGINALS_VIA_SCALED) )
			{
				if( !isEmptyString(nextPage) )
				{
					linkPath = (String)vars.get("nextPage");
					tooltip = texts.getString("next-slide");
				}
				else
					tooltip = texts.getString("on-last-slide");
			}
			else
			{
				linkPath = (String)vars.get("originalPage");
				if( linkPath!=null )
					tooltip = texts.getString("orig-image");
			}

			displaySize.width = ((Integer)vars.get("imageWidth")).intValue();
			displaySize.height = ((Integer)vars.get("imageHeight")).intValue();
			return getFrameBlock((String)vars.get("imagePath"), linkPath, false, tooltip, displaySize, true);
		}
	}

	private boolean hasCurrentSlideOriginalSource()
	{
		return( !isEmptyString(originalPath) && engine.getImageLinking().equals(AlbumBean.LINK_ORIGINALS_VIA_SCALED) );
	}

	public void setupOriginalSlide() throws IOException, ElementException
	{
		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, engine.getSlideDirectory() + "/" + originalPage), current);
			originalPage = encodeUrlIfNecessary(originalPage);
		}

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

	public void purgeUnnecessaryDirectoryCachedData()
	{
		// ONLY purging unnecessary cahced data AFTER the whole directory is processed!
		if( !isEmptyString(nextPage) )
			return;

		File[] children = listFiles(imageDirectory, false, outputDirectory);

		purgeUnnecessaryDirectoryCachedDataImpl( getVariableContainer(imageDirectory) );
		for( int i = 0; i<children.length; i++ )
			purgeUnnecessaryDirectoryCachedDataImpl( getVariableContainer(children[i]) );
	}

	public void purgeUnnecessaryDirectoryCachedDataImpl(Map vars)
	{
		Iterator it = vars.keySet().iterator();

		while( it.hasNext() )
		{
			Object key = it.next();

			if( isEmptyString(key) )
				continue;
			if( ((String)key).equalsIgnoreCase(Constants.CACHE_KEY_FIRST_SLIDE_OF_INDEX_PAGE) ||
				((String)key).equalsIgnoreCase(Constants.CACHE_KEY_INDEX_JUMP_TO_PAGE) ||
				((String)key).equalsIgnoreCase(Constants.CACHE_KEY_INDEX_TOP_PATH_BAR) ||
				((String)key).equalsIgnoreCase(Constants.CACHE_KEY_SLIDE_COMMENT) ||
				((String)key).equalsIgnoreCase(Constants.CACHE_KEY_SLIDE_JUMP_TO_PAGE) ||
				((String)key).equalsIgnoreCase(Constants.CACHE_KEY_SLIDE_TOP_PATH_BAR) ||
				((String)key).equalsIgnoreCase(Constants.CACHE_KEY_THUMBNAIL_FRAME_BLOCK) ||
				((String)key).equalsIgnoreCase(Constants.CACHE_KEY_THUMBNAIL_TOOLTIP))
				it.remove();
		}
	}

	public boolean isSlideCommentAtTopBelowNavigation()
	{
		return isSlideCommentAtLocation("Top (Below Navigation)");
	}

	public boolean isSlideCommentAtTopAboveNavigation()
	{
		return isSlideCommentAtLocation("Top (Above Navigation)");
	}

	public boolean isSlideCommentAtBottom()
	{
		return isSlideCommentAtLocation("Bottom");
	}

	private boolean isSlideCommentAtLocation(String location)
	{
		boolean enableDedicatedSlideCommentSection = isVariableValueEqual(Constants.USER_VAR_ENABLE_DEDICATED_SLIDE_COMMENT_SECTION, imageDirectory, true);

		if( !enableDedicatedSlideCommentSection )
			return false;

		String commentVerticalPosition = readUserVariableAsString(Constants.USER_VAR_SLIDE_COMMENT_VERTICAL_POSITION, imageDirectory, Constants.DEFAULT_SLIDE_COMMENT_VERTICAL_POSITION);

		return commentVerticalPosition.trim().equalsIgnoreCase(location);
	}

	public String getCurrentSlideViewerRemarksId()
	{
		return getViewerRemarksId( currentFile );
	}
}
