/**
 * Copyright (C) 2013-2015 Regents of the University of California.
 * Authors:
 *		Jeff Thompson <jefft0@remap.ucla.edu>
 *		Rafael Teixeira <monoman@gmail.com>
 * @author: From code in ndn-cxx by Yingdi Yu <yingdi@cs.ucla.edu>
 *
 * This program 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 3 of the License, or
 * (at your option) any later version.
 *
 * 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * A copy of the GNU Lesser General Public License is in the file COPYING.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Net.NamedData.Security.Certificate
{





	using Net.NamedData.Encoding.Der;
	using Net.NamedData.Security;
	using Net.NamedData.Util;

	public class PublicKey
	{
		public PublicKey()
		{
			keyType_ = null;
			keyDer_ = new Blob();
		}

		/**
		 * Create a new PublicKey by decoding the keyDer. Set the key type from the
		 * decoding.
		 * @param keyDer The blob of the SubjectPublicKeyInfo DER.
		 * @ if can't Decode the key DER.
		 */
		public PublicKey(Blob keyDer)
		{
			keyDer_ = keyDer;

			// Get the public key OID.
			String oidString = null;
			try {
				DerNode parsedNode = DerNode.parse(keyDer.buf(), 0);
				var rootChildren = parsedNode.getChildren();
				var algorithmIdChildren = DerNode.getSequence(rootChildren, 0).getChildren();
				oidString = ((DerNode)algorithmIdChildren.get(0)).toVal().toString();
			} catch (DerDecodingException ex) {
				throw new UnrecognizedKeyFormatException
				  ("PublicKey: Error decoding the public key" +
				   ex.getMessage());
			}

			// Verify that the we can Decode.
			if (oidString.equals(RSA_ENCRYPTION_OID)) {
				keyType_ = KeyType.RSA;

				KeyFactory keyFactory = null;
				try {
					keyFactory = KeyFactory.getInstance("RSA");
				} catch (NoSuchAlgorithmException exception) {
					// Don't expect this to happen.
					throw new UnrecognizedKeyFormatException
					  ("RSA is not supported: " + exception.getMessage());
				}

				try {
					keyFactory.generatePublic
					  (new X509EncodedKeySpec(keyDer.getImmutableArray()));
				} catch (InvalidKeySpecException exception) {
					// Don't expect this to happen.
					throw new UnrecognizedKeyFormatException
					  ("X509EncodedKeySpec is not supported for RSA: " + exception.getMessage());
				}
			} else if (oidString.equals(EC_ENCRYPTION_OID)) {
				keyType_ = KeyType.ECDSA;

				KeyFactory keyFactory = null;
				try {
					keyFactory = KeyFactory.getInstance("EC");
				} catch (NoSuchAlgorithmException exception) {
					// Don't expect this to happen.
					throw new UnrecognizedKeyFormatException
					  ("EC is not supported: " + exception.getMessage());
				}

				try {
					keyFactory.generatePublic
					  (new X509EncodedKeySpec(keyDer.getImmutableArray()));
				} catch (InvalidKeySpecException exception) {
					// Don't expect this to happen.
					throw new UnrecognizedKeyFormatException
					  ("X509EncodedKeySpec is not supported for EC: " + exception.getMessage());
				}
			} else
				throw new UnrecognizedKeyFormatException(
				  "PublicKey: Unrecognized OID " + oidString);
		}

		/**
		 * Encode the public key into DER.
		 * @return the encoded DER syntax tree.
		 */
		public DerNode
		toDer()
		{
			return DerNode.parse(keyDer_.buf());
		}

		public KeyType
		getKeyType() { return keyType_; }

		/*
		 * Get the digest of the public key.
		 * @param digestAlgorithm The digest algorithm.
		 */
		public Blob
		getDigest(DigestAlgorithm digestAlgorithm)
		{
			if (digestAlgorithm == DigestAlgorithm.SHA256) {
				return new Blob(Common.digestSha256(keyDer_.buf()));
			} else
				throw new UnrecognizedDigestAlgorithmException("Wrong format!");
		}

		/*
		 * Get the digest of the public key using DigestAlgorithm.SHA256.
		 */
		public Blob
		getDigest()
		{
			try {
				return getDigest(DigestAlgorithm.SHA256);
			} catch (UnrecognizedDigestAlgorithmException ex) {
				// We don't expect this exception.
				throw new Error("UnrecognizedDigestAlgorithmException " + ex.getMessage());
			}
		}

		/*
		 * Get the raw bytes of the public key in DER format.
		 */
		public Blob
		getKeyDer() { return keyDer_; }

		private static String RSA_ENCRYPTION_OID = "1.2.840.113549.1.1.1";
		private static String EC_ENCRYPTION_OID = "1.2.840.10045.2.1";

		private readonly KeyType keyType_;
		private readonly Blob keyDer_;   /**< PublicKeyInfo in DER */
	}
}
