/**
 * Copyright 2020 German Tischler-Höhle
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 **/
#if ! defined(LIBSECRECY_GCMFACTORYBASE_HPP)
#define LIBSECRECY_GCMFACTORYBASE_HPP

#include <libsecrecy/RawKey.hpp>
#include <libsecrecy/AESEncryptKey.hpp>

namespace libsecrecy
{
	struct GCMFactoryBase
	{
		static RawKeyObject loadKey(
			std::string const & hexhash,
			bool resolve,
			libsecrecy::GPGMEContext & context
		)
		{
			std::string const keyfiledata = RawKeyBase::readKeyData(hexhash,resolve);
			std::istringstream keydataistr(keyfiledata);
			RawKeyBase::expectMagic(keydataistr);
			std::string const scipher = GCMStreamBase::readString(keydataistr);
			std::string const keyname = GCMStreamBase::readString(keydataistr);
			std::string keyhex;
			std::shared_ptr<RawKeyBase> RKB;

			if ( scipher == "AES128" )
			{
				typedef AES128EncryptKey key_type;
				typedef key_type::raw_key_type raw_key_type;
				std::shared_ptr<RawKeyBase> TRKB(new raw_key_type);
				dynamic_cast<raw_key_type *>(TRKB.get())->readKey(context,keydataistr);
				keyhex = dynamic_cast<raw_key_type *>(TRKB.get())->hashToHex();
				RKB = TRKB;
			}
			if ( scipher == "AES192" )
			{
				typedef AES192EncryptKey key_type;
				typedef key_type::raw_key_type raw_key_type;
				std::shared_ptr<RawKeyBase> TRKB(new raw_key_type);
				dynamic_cast<raw_key_type *>(TRKB.get())->readKey(context,keydataistr);
				keyhex = dynamic_cast<raw_key_type *>(TRKB.get())->hashToHex();
				RKB = TRKB;
			}
			if ( scipher == "AES256" )
			{
				typedef AES256EncryptKey key_type;
				typedef key_type::raw_key_type raw_key_type;
				std::shared_ptr<RawKeyBase> TRKB(new raw_key_type);
				dynamic_cast<raw_key_type *>(TRKB.get())->readKey(context,keydataistr);
				keyhex = dynamic_cast<raw_key_type *>(TRKB.get())->hashToHex();
				RKB = TRKB;
			}

			return RawKeyObject(RKB,scipher,keyname,keyhex);
		}

		static RawKeyObject obtainKey(
			std::string const & hexhash,
			bool resolve,
			libsecrecy::GPGMEContext & context,
			RawKeyBaseCacheInterface * cache = nullptr
		)
		{
			RawKeyObject RK;

			if ( cache )
			{
				RK = cache->getKey(hexhash);

				if ( RK.isNull() )
				{
					RK = loadKey(hexhash,resolve,context);
					assert ( ! RK.isNull() );
					cache->addKey(RK);
				}
			}
			else
			{
				RK = loadKey(hexhash,resolve,context);
			}

			assert ( ! RK.isNull() );

			return RK;
		}
	};
}
#endif
