#include "key_chain.h"
#include <oak/oak.h>
#include <plist/plist.h>

key_chain_t::key_t::key_t (std::string const& identity, std::string const& name, std::string const& key_data) : _identity(identity), _name(name), _key_data(key_data)
{
	init();
}

key_chain_t::key_t::key_t (key_t const& rhs) : _identity(rhs._identity), _name(rhs._name), _key_data(rhs._key_data)
{
	init();
}

key_chain_t::key_t::~key_t ()
{
	cleanup();
}

void key_chain_t::key_t::init () const
{
	_ssl_key  = NULL;
	_ssl_bio  = NULL;
	_ssl_data = NULL;
}

bool key_chain_t::key_t::setup () const
{
	if(_ssl_key)
		return true;

	bool res = false;
	if(_ssl_key = EVP_PKEY_new())
	{
		if(_ssl_bio = BIO_new_mem_buf((char*)_key_data.data(), _key_data.size()))
		{
			if(_ssl_data = PEM_read_bio_DSA_PUBKEY(_ssl_bio, NULL, NULL, NULL))
			{
				if(res = EVP_PKEY_assign_DSA(_ssl_key, _ssl_data) == 1)
					_ssl_data = NULL;
			}
			else
			{
				fprintf(stderr, "*** error reading key\n");
			}
		}
		else
		{
			fprintf(stderr, "*** error creating BIO\n");
		}
	}
	else
	{
		fprintf(stderr, "*** error creating PKEY\n");
	}

	if(!res)
		cleanup();

	return res;
}

void key_chain_t::key_t::cleanup () const
{
	if(_ssl_data)
		DSA_free(_ssl_data);

	if(_ssl_bio)
		BIO_free(_ssl_bio);

	if(_ssl_key)
		EVP_PKEY_free(_ssl_key);

	init();
}

// =============
// = Key Chain =
// =============

void key_chain_t::load (std::string const& path)
{
	keys.clear();

	plist::dictionary_t identities;
	if(plist::get_key_path(plist::load(path), "identities", identities))
	{
		iterate(identity, identities)
		{
			std::string name, keyData;
			if(plist::get_key_path(identity->second, "name", name) && plist::get_key_path(identity->second, "key", keyData))
			{
				keys.push_back(key_ptr(new key_t(identity->first, name, keyData)));
			}
			else
			{
				fprintf(stderr, "no name/key in entry:\n%s", to_s(identity->second).c_str());
			}
		}
	}
	else
	{
		fprintf(stderr, "no identities in ‘%s’\n", path.c_str());
	}
}

void key_chain_t::save (std::string const& path) const
{
	plist::dictionary_t identities;
	iterate(key, keys)
	{
		plist::dictionary_t entry;
		entry.insert(std::make_pair("name", (*key)->name()));
		entry.insert(std::make_pair("key",  (*key)->_key_data));
		identities.insert(std::make_pair((*key)->identity(), entry));
	}

	plist::dictionary_t plist;
	plist.insert(std::make_pair("identities", identities));

	plist::save(path, plist);
}

void key_chain_t::add (key_t const& key)
{
	keys.push_back(key_ptr(new key_t(key.identity(), key.name(), key._key_data)));
}

key_chain_t::key_ptr key_chain_t::find (std::string const& identity) const
{
	iterate(key, keys)
	{
		if((*key)->identity() == identity && (*key)->setup())
			return *key;
	}
	return key_ptr();
}
