/*
 *
 * This file is part of the iText (R) project.
    Copyright (c) 1998-2022 iText Group NV
 * Authors: Bruno Lowagie, Paulo Soares, et al.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3
 * as published by the Free Software Foundation with the addition of the
 * following permission added to Section 15 as permitted in Section 7(a):
 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
 * ITEXT GROUP. ITEXT GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
 * OF THIRD PARTY RIGHTS
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA, 02110-1301 USA, or download the license from the following URL:
 * http://itextpdf.com/terms-of-use/
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License,
 * a covered work must retain the producer line in every PDF that is created
 * or manipulated using iText.
 *
 * You can be released from the requirements of the license by purchasing
 * a commercial license. Buying such a license is mandatory as soon as you
 * develop commercial activities involving the iText software without
 * disclosing the source code of your own applications.
 * These activities include: offering paid services to customers as an ASP,
 * serving PDFs on the fly in a web application, shipping iText with a closed
 * source product.
 *
 * For more information, please contact iText Software Corp. at this
 * address: sales@itextpdf.com
 */
package com.itextpdf.text.pdf.richmedia;

import java.io.IOException;
import java.util.HashMap;

import com.itextpdf.text.Rectangle;
import com.itextpdf.text.exceptions.IllegalPdfSyntaxException;
import com.itextpdf.text.pdf.PdfAnnotation;
import com.itextpdf.text.pdf.PdfArray;
import com.itextpdf.text.pdf.PdfDeveloperExtension;
import com.itextpdf.text.pdf.PdfDictionary;
import com.itextpdf.text.pdf.PdfFileSpecification;
import com.itextpdf.text.pdf.PdfIndirectReference;
import com.itextpdf.text.pdf.PdfName;
import com.itextpdf.text.pdf.PdfNameTree;
import com.itextpdf.text.pdf.PdfWriter;

/**
 * Object that is able to create Rich Media Annotations as described
 * in the document "Acrobat Supplement to the ISO 32000", referenced
 * in the code as "ExtensionLevel 3". This annotation is described in
 * section 9.6 entitled "Rich Media" of this document.
 * Extension level 3 introduces rich media PDF constructs that support
 * playing a SWF file and provide enhanced rich media. With rich media
 * annotation, Flash applications, video, audio, and other multimedia
 * can be attached to a PDF with expanded functionality. It improves upon
 * the existing 3D annotation structure to support multiple multimedia
 * file assets, including Flash video and compatible variations on the
 * H.264 format. The new constructs allow a two-way scripting bridge between
 * Flash and a conforming application. There is support for generalized
 * linking of a Flash application state to a comment or view, which enables
 * video commenting. Finally, actions can be linked to video chapter points.
 * @since	5.0.0
 */
public class RichMediaAnnotation {
	/** The PdfWriter to which the annotation will be added. */
	protected PdfWriter writer;
	/** The annotation object */
	protected PdfAnnotation annot;
	/** the rich media content (can be reused for different annotations) */
	protected PdfDictionary richMediaContent = null;
	/** a reference to the RichMediaContent that can be reused. */
	protected PdfIndirectReference richMediaContentReference = null;
	/** the rich media settings (specific for this annotation) */
	protected PdfDictionary richMediaSettings = new PdfDictionary(PdfName.RICHMEDIASETTINGS);
	/** a map with the assets (will be used to construct a name tree.) */
	protected HashMap<String, PdfIndirectReference> assetsmap = null;
	/** an array with configurations (will be added to the RichMediaContent). */
	protected PdfArray configurations = null;
	/** an array of views (will be added to the RichMediaContent) */
	protected PdfArray views = null;

	/**
	 * Creates a RichMediaAnnotation.
	 * @param	writer	the PdfWriter to which the annotation will be added.
	 * @param	rect	the rectangle where the annotation will be added.
	 */
	public RichMediaAnnotation(PdfWriter writer, Rectangle rect) {
		this.writer = writer;
		annot = writer.createAnnotation(rect, PdfName.RICHMEDIA);
        richMediaContent = new PdfDictionary(PdfName.RICHMEDIACONTENT);
		assetsmap = new HashMap<String, PdfIndirectReference>();
		configurations = new PdfArray();
		views = new PdfArray();
	}

	/**
	 * Creates a RichMediaAnnotation using rich media content that has already
	 * been added to the writer. Note that assets, configurations, views added
	 * to a RichMediaAnnotation created like this will be ignored.
	 * @param	writer	the PdfWriter to which the annotation will be added.
	 * @param	rect	the rectangle where the annotation will be added.
	 * @param	richMediaContentReference	reused rich media content.
	 */
	public RichMediaAnnotation(PdfWriter writer, Rectangle rect, PdfIndirectReference richMediaContentReference) {
		this.richMediaContentReference = richMediaContentReference;
		richMediaContent = null;
		this.writer = writer;
		annot = writer.createAnnotation(rect, PdfName.RICHMEDIA);
	}

	/**
	 * Gets a reference to the RichMediaContent for reuse of the
	 * rich media content. Returns null if the content hasn't been
	 * added to the OutputStream yet.
	 * @return	a PdfDictionary with RichMediaContent
	 */
	public PdfIndirectReference getRichMediaContentReference() {
		return richMediaContentReference;
	}

	/**
	 * Adds an embedded file.
	 * (Part of the RichMediaContent.)
	 * @param	name	a name for the name tree
	 * @param	fs		a file specification for an embedded file.
	 */
	public PdfIndirectReference addAsset(String name, PdfFileSpecification fs)
		throws IOException {
		if (assetsmap == null)
			throw new IllegalPdfSyntaxException(
				"You can't add assets to reused RichMediaContent.");
		PdfIndirectReference ref = writer.addToBody(fs).getIndirectReference();
		assetsmap.put(name, ref);
		return ref;
	}

	/**
	 * Adds a reference to an embedded file.
	 * (Part of the RichMediaContent.)
	 * @param	ref	a reference to a PdfFileSpecification
	 */
	public PdfIndirectReference addAsset(String name, PdfIndirectReference ref) throws IOException {
		if (views == null)
			throw new IllegalPdfSyntaxException(
				"You can't add assets to reused RichMediaContent.");
		assetsmap.put(name, ref);
		return ref;
	}

	/**
	 * Adds a RichMediaConfiguration.
	 * (Part of the RichMediaContent.)
	 * @param	configuration	a configuration dictionary
	 */
	public PdfIndirectReference addConfiguration(RichMediaConfiguration configuration) throws IOException {
		if (configurations == null)
			throw new IllegalPdfSyntaxException(
				"You can't add configurations to reused RichMediaContent.");
		PdfIndirectReference ref = writer.addToBody(configuration).getIndirectReference();
		configurations.add(ref);
		return ref;
	}

	/**
	 * Adds a reference to a RichMediaConfiguration.
	 * (Part of the RichMediaContent.)
	 * @param	ref		a reference to a RichMediaConfiguration
	 */
	public PdfIndirectReference addConfiguration(PdfIndirectReference ref) throws IOException {
		if (configurations == null)
			throw new IllegalPdfSyntaxException(
				"You can't add configurations to reused RichMediaContent.");
		configurations.add(ref);
		return ref;
	}

	/**
	 * Adds a view dictionary.
	 * (Part of the RichMediaContent.)
	 * @param	view	a view dictionary
	 */
	public PdfIndirectReference addView(PdfDictionary view) throws IOException {
		if (views == null)
			throw new IllegalPdfSyntaxException(
				"You can't add views to reused RichMediaContent.");
		PdfIndirectReference ref = writer.addToBody(view).getIndirectReference();
		views.add(ref);
		return ref;
	}

	/**
	 * Adds a reference to a view dictionary.
	 * (Part of the RichMediaContent.)
	 * @param	ref	a reference to a view dictionary
	 */
	public PdfIndirectReference addView(PdfIndirectReference ref) throws IOException {
		if (views == null)
			throw new IllegalPdfSyntaxException(
				"You can't add views to reused RichMediaContent.");
		views.add(ref);
		return ref;
	}

	/**
	 * Sets the RichMediaActivation dictionary specifying the style of
	 * presentation, default script behavior, default view information,
	 * and animation style when the annotation is activated.
	 * (Part of the RichMediaSettings.)
	 * @param	richMediaActivation
	 */
	public void setActivation(RichMediaActivation richMediaActivation) {
		richMediaSettings.put(PdfName.ACTIVATION, richMediaActivation);
	}

	/**
	 * Sets the RichMediaDeactivation dictionary specifying the condition
	 * that causes deactivation of the annotation.
	 * (Part of the RichMediaSettings.)
	 * @param	richMediaDeactivation
	 */
	public void setDeactivation(RichMediaDeactivation richMediaDeactivation) {
		richMediaSettings.put(PdfName.DEACTIVATION, richMediaDeactivation);
	}

	/**
	 * Creates the actual annotation and adds different elements to the
	 * PdfWriter while doing so.
	 * @return	a PdfAnnotation
	 */
	public PdfAnnotation createAnnotation() throws IOException {
		if (richMediaContent != null) {
			if (!assetsmap.isEmpty()) {
				PdfDictionary assets = PdfNameTree.writeTree(assetsmap, writer);
				richMediaContent.put(PdfName.ASSETS, writer.addToBody(assets).getIndirectReference());
			}
			if (configurations.size() > 0) {
				richMediaContent.put(PdfName.CONFIGURATION, writer.addToBody(configurations).getIndirectReference());
			}
			if (views.size() > 0) {
				richMediaContent.put(PdfName.VIEWS, writer.addToBody(views).getIndirectReference());
			}
			richMediaContentReference = writer.addToBody(richMediaContent).getIndirectReference();
		}
		writer.addDeveloperExtension(PdfDeveloperExtension.ADOBE_1_7_EXTENSIONLEVEL3);
        annot.put(PdfName.RICHMEDIACONTENT, richMediaContentReference);
        annot.put(PdfName.RICHMEDIASETTINGS, writer.addToBody(richMediaSettings).getIndirectReference());
		return annot;
	}
}
