/*
 * Copyright (c) 2011-2012 ICM Uniwersytet Warszawski All rights reserved.
 * See LICENCE file for licensing information.
 *
 * Derived from the code copyrighted and licensed as follows:
 * 
 * Copyright (c) Members of the EGEE Collaboration. 2004.
 * See http://www.eu-egee.org/partners/ for details on the copyright
 * holders.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 *    
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package eu.emi.security.authn.x509.helpers.proxy;

import java.io.IOException;

import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.BERTaggedObject;
import org.bouncycastle.asn1.DLSequence;

import eu.emi.security.authn.x509.proxy.BaseProxyCertificateOptions;
import eu.emi.security.authn.x509.proxy.ProxyPolicy;

/**
 * Proxy cert info extension class.
 * 
 * <pre>
 * ProxyCertInfoExtension ::= SEQUENCE { 
 *          proxyPolicy            ProxyPolicy,
 *          pCPathLenConstraint    [1] EXPLICIT ProxyCertPathLengthConstraint OPTIONAL }
 *  
 *     ProxyCertPathLengthConstraint ::= INTEGER
 * </pre>
 * 
 * @author Joni Hahkala
 * @author K. Benedyczak
 */
public class DraftRFCProxyCertInfoExtension extends ProxyCertInfoExtension
{
	/** The oid of the rfc draft proxy cert extension. */
	public static final String DRAFT_EXTENSION_OID = "1.3.6.1.4.1.3536.1.222";

	/**
	 * Generate new proxy certificate info extension with length limit len
	 * and policy policy. Use negative value if no limit is desired.
	 * 
	 * @param pathLen
	 *                the maximum number of proxy certificates to follow
	 *                this one. If -1 is used then no limit will be set. 
	 * @param policy
	 *                the proxy policy extension.
	 */
	public DraftRFCProxyCertInfoExtension(int pathLen, ProxyPolicy policy)
	{
		this.pathLen = pathLen;
		this.policy = policy;
	}

	/**
	 * Generate a proxy that inherits all rights and that has no cert path
	 * length limitations.
	 */
	public DraftRFCProxyCertInfoExtension()
	{
		policy = new ProxyPolicy(ProxyPolicy.INHERITALL_POLICY_OID);
	}

	/**
	 * Constructor that generates instance out of byte array.
	 * 
	 * @param bytes
	 *                The byte array to consider as the ASN.1 encoded
	 *                proxyCertInfo extension.
	 * @throws IOException
	 *                 thrown in case the parsing of the byte array fails.
	 */
	public DraftRFCProxyCertInfoExtension(byte[] bytes) throws IOException
	{
		this((ASN1Sequence) ASN1Primitive.fromByteArray(bytes));
	}

	/**
	 * Read a proxyCertInfoExtension from the ASN1 sequence.
	 * 
	 * @param seq
	 *                The sequence containing the extension.
	 * @throws IOException IO exception
	 */
	public DraftRFCProxyCertInfoExtension(ASN1Sequence seq) throws IOException
	{
		int index = 0;
		
		if (seq == null || seq.size() == 0)
			throw new IOException("ProxyCertInfoExtension is empty");

		if (seq.getObjectAt(index) instanceof DLSequence)
		{
			policy = new ProxyPolicy((DLSequence)seq.getObjectAt(index));
			index++;
		} else
		{
			throw new IOException("ProxyCertInfoExtension parser error, expected policy sequence, but got: "
					+ seq.getObjectAt(index).getClass());
		}

		if (seq.size() <= index)
			return;

		if (seq.getObjectAt(index) instanceof ASN1TaggedObject)
		{
			ASN1TaggedObject tagged = (ASN1TaggedObject) seq.getObjectAt(index);
			if (tagged.getTagNo() != 1)
				throw new IOException("ProxyCertInfoExtension parser error, "
						+ "expected path constraint tagged with 1 but was tagged with " + 
						tagged.getTagNo());
			ASN1Primitive pathLenObj = tagged.getObject();
			if (pathLenObj instanceof ASN1Integer)
				pathLen = ((ASN1Integer) pathLenObj).getValue().intValue();
			else
				throw new IOException("ProxyCertInfoExtension parser error, "
						+ "expected path constraint of integer type but got " + 
						pathLenObj);
		} else
		{
			throw new IOException("ProxyCertInfoExtension parser error, "
					+ "expected path constraint encoded as tagged integer but but got " + 
					seq.getObjectAt(index));
		}
		
		index++;
		if (seq.size() > index)
			throw new IOException("ProxyCertInfoExtension parser error, sequence contains too many items");
	}

	@Override
	public ASN1Primitive toASN1Primitive()
	{
		ASN1EncodableVector v = new ASN1EncodableVector();
		if (policy != null)
		{
			v.add(policy.toASN1Primitive());
		} else
		{
			throw new IllegalArgumentException("Can't generate " +
					"ProxyCertInfoExtension without mandatory policy");
		}
		if (pathLen != BaseProxyCertificateOptions.UNLIMITED_PROXY_LENGTH)
		{
			v.add(new BERTaggedObject(true, 1, new ASN1Integer(pathLen)));
		}

		return new DLSequence(v);
	}
}
