/*
 * Copyright 2008 Pirion Systems Pty Ltd, 139 Warry St,
 * Fortitude Valley, Queensland, Australia
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

package com.sun.pdfview.decrypt;

import com.sun.pdfview.PDFObject;
import com.sun.pdfview.PDFParseException;

import java.nio.ByteBuffer;
import java.util.Map;

/**
 * Implements Version 4 standard decryption, whereby the Encrypt dictionary
 * contains a list of named 'crypt filters', each of which is the equivalent
 * of a {@link PDFDecrypter}. In addition to this list of crypt filters,
 * the name of the filter to use for streams and the default filter to use
 * for strings is specified. Requests to decode a stream with a named
 * decrypter (typically Identity) instead of the default decrypter
 * are honoured. 
 *
 * @author Luke Kirby
 */
public class CryptFilterDecrypter implements PDFDecrypter {

    /** Maps from crypt filter names to their corresponding decrypters */
    private Map<String, PDFDecrypter> decrypters;
    /** The default decrypter for stream content */
    private PDFDecrypter defaultStreamDecrypter;
    /** The default decrypter for string content */
    private PDFDecrypter defaultStringDecrypter;

    /**
     * Class constructor
     * @param decrypters a map of crypt filter names to their corresponding
     *  decrypters. Must already contain the Identity filter.
     * @param defaultStreamCryptName the crypt filter name of the default
     *  stream decrypter
     * @param defaultStringCryptName the crypt filter name of the default
     * string decrypter
     * @throws PDFParseException if one of the named defaults is not
     *  present in decrypters
     */
    public CryptFilterDecrypter(
            Map<String, PDFDecrypter> decrypters,
            String defaultStreamCryptName,
            String defaultStringCryptName)
            throws PDFParseException {

        this.decrypters = decrypters;
        assert this.decrypters.containsKey("Identity") :
                "Crypt Filter map does not contain required Identity filter";
        defaultStreamDecrypter = this.decrypters.get(defaultStreamCryptName);
        if (defaultStreamDecrypter == null) {
            throw new PDFParseException(
                    "Unknown crypt filter specified as default for streams: " +
                            defaultStreamCryptName);
        }
        defaultStringDecrypter = this.decrypters.get(defaultStringCryptName);
        if (defaultStringDecrypter == null) {
            throw new PDFParseException(
                    "Unknown crypt filter specified as default for strings: " +
                            defaultStringCryptName);
        }
    }

    public ByteBuffer decryptBuffer(
            String cryptFilterName, PDFObject streamObj, ByteBuffer streamBuf)
            throws PDFParseException {
        final PDFDecrypter decrypter;
        if (cryptFilterName == null) {
            decrypter = defaultStreamDecrypter;
        } else {
            decrypter = decrypters.get(cryptFilterName);
            if (decrypter == null) {
                throw new PDFParseException("Unknown CryptFilter: " +
                        cryptFilterName);
            }
        }
        return decrypter.decryptBuffer(
                // elide the filter name to prevent V2 decrypters from
                // complaining about a crypt filter name
                null,
                // if there's a specific crypt filter being used then objNum
                // and objGen shouldn't contribute to the key, so we
                // should make sure that no streamObj makes its way through
                cryptFilterName != null ? null : streamObj,
                streamBuf);
    }

    public String decryptString(int objNum, int objGen, String inputBasicString)
            throws PDFParseException {
        return defaultStringDecrypter.decryptString(objNum, objGen, inputBasicString);
    }

    public boolean isEncryptionPresent() {
        for (final PDFDecrypter decrypter : decrypters.values()) {
            if (decrypter.isEncryptionPresent()) {
                return true;
            }
        }
        return false;
    }

    public boolean isEncryptionPresent(String cryptFilterName) {
        PDFDecrypter decrypter = decrypters.get(cryptFilterName);
        return decrypter != null && decrypter.isEncryptionPresent(cryptFilterName);
    }

    public boolean isOwnerAuthorised() {
        for (final PDFDecrypter decrypter : decrypters.values()) {
            if (decrypter.isOwnerAuthorised()) {
                return true;
            }
        }
        return false;
    }
}
