#include "precomp.h"

#include "anslicensing.h"
#include "license.h"
#include "uniconv.h"
#include "except.h"
#include "tinyxml2.h"
#include "picojson.h"
#include "base64.h"

void LicenseImpl::LoadXml(const char * xmlTemplate)
{
	tinyxml2::XMLDocument xml;
	tinyxml2::XMLElement * rootElement, *element;
	const char * attr;
	int version = 1;
	
	if (xml.Parse(xmlTemplate) != tinyxml2::XML_NO_ERROR)
		throw new LicensingException(STATUS_INVALID_XML);

	if ((element = rootElement = xml.FirstChildElement()) == NULL) 
		throw new LicensingException(STATUS_INVALID_XML);

	if ((attr = element->Attribute("version")) != NULL)
		version = atoi(attr);

	if (version > 1)
		throw new LicensingException(STATUS_GENERIC_ERROR, "unsupported license version");

	bool isLease = false;

	if ((attr = element->Attribute("isLease")) != NULL)
		isLease = (_stricmp(attr, "1") == 0 || _stricmp(attr, "true") == 0);

	m_isLease = isLease;

	if ((element = rootElement->FirstChildElement("LicenseKey")) == NULL)
		throw new LicensingException(STATUS_GENERIC_ERROR, "license key not found");

	m_licenseKey = element->GetText();

	if ((element = rootElement->FirstChildElement("LicenseKeyValidationData")) != NULL)
	{
		BASE64 base64;

		m_licenseKeyValidationData = base64.decode(element->GetText(), strlen(element->GetText()), nullptr);

		if (!m_licenseKeyValidationData.empty())
			throw new LicensingException(STATUS_GENERIC_ERROR, "invalid BASE64 string");
	}

	if ((element = rootElement->FirstChildElement("ActivationKey")) == NULL)
		throw new LicensingException(STATUS_GENERIC_ERROR, "license key not found");

	m_activationKey = element->GetText();

	if ((element = rootElement->FirstChildElement("HardwareId")) == NULL)
		throw new LicensingException(STATUS_GENERIC_ERROR, "license key not found");

	m_hardwareId = element->GetText();
}

#ifndef MAX_XML_BUFFER_SIZE
#define MAX_XML_BUFFER_SIZE 4096
#endif

const char * LicenseImpl::SaveXml()
{
	BASE64 base64;
	char buffer[MAX_XML_BUFFER_SIZE];

	m_xmlLicense.resize(0);

	snprintf(buffer, MAX_XML_BUFFER_SIZE, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"); m_xmlLicense.append(buffer);
	snprintf(buffer, MAX_XML_BUFFER_SIZE, "<License version=\"%d\" isLease=\"%d\">\n", m_version, (m_isLease) ? 1 : 0); m_xmlLicense.append(buffer);
	snprintf(buffer, MAX_XML_BUFFER_SIZE, "\t<LicenseKey>%s</LicenseKey>\n", m_licenseKey.c_str()); m_xmlLicense.append(buffer);

	if (!m_licenseKeyValidationData.empty())
	{
		BASE64 base64;

		auto enc = base64.encode(m_licenseKeyValidationData.data(), m_licenseKeyValidationData.size(), true);

		if (!enc.empty())
		{
			snprintf(buffer, MAX_XML_BUFFER_SIZE, "\t<LicenseKeyValidationData>"); m_xmlLicense.append(buffer);
			m_xmlLicense.append(enc);
			snprintf(buffer, MAX_XML_BUFFER_SIZE, "</LicenseKeyValidationData>\n"); m_xmlLicense.append(buffer);
		}
		else
			throw new LicensingException(STATUS_OUT_OF_MEMORY);
	}

	snprintf(buffer, MAX_XML_BUFFER_SIZE, "\t<ActivationKey>%s</ActivationKey>\n", m_activationKey.c_str()); m_xmlLicense.append(buffer);
	snprintf(buffer, MAX_XML_BUFFER_SIZE, "\t<HardwareId>%s</HardwareId>\n", m_hardwareId.c_str()); m_xmlLicense.append(buffer);
	snprintf(buffer, MAX_XML_BUFFER_SIZE, "</License>"); m_xmlLicense.append(buffer);

	return m_xmlLicense.c_str();
}

void LicenseImpl::LoadJson(const char * jsonLicense)
{
	picojson::value json;

	auto err = picojson::parse(json, jsonLicense);

	if (!err.empty())
		throw new LicensingException(STATUS_INVALID_PARAM);

	picojson::object license = json.get<picojson::object>();

	auto version = license["version"].get<double>();

	if (version > 1)
		throw new LicensingException(STATUS_GENERIC_ERROR, "unsupported license version");

	m_isLease = license.count("is_lease") ? license["is_lease"].get<bool>() : false;

	m_licenseKey = license["license_key"].get<string>();

	if (license.count("license_key_validation_data"))
	{
		BASE64 base64;
		m_licenseKeyValidationData = base64.decode(license["license_key_validation_data"].get<string>().c_str());
	}

	m_activationKey = license["activation_key"].get<string>();

	m_hardwareId = license["hardware_id"].get<string>();
}

#ifndef MAX_JSON_BUFFER_SIZE
#define MAX_JSON_BUFFER_SIZE 4096
#endif

const char * LicenseImpl::SaveJson()
{
	picojson::object license;
	BASE64 base64;

	license["version"] = picojson::value((double)1);
	license["is_lease"] = picojson::value(m_isLease);
	license["license_key"] = picojson::value(m_licenseKey);
	license["hardware_id"] = picojson::value(m_hardwareId);
	license["activation_key"] = picojson::value(m_activationKey);

	if (!m_licenseKeyValidationData.empty())
		license["license_key_validation_data"] = picojson::value(base64.encode(m_licenseKeyValidationData.data(), m_licenseKeyValidationData.size(), true));

	m_jsonLicense = picojson::value(license).serialize(true);

	return m_jsonLicense.c_str();
}

namespace ANSCENTER {
	namespace Licensing {
		template<>
		LicenseT<char>::LicenseT() :
			m_Impl(*new LicenseImpl())
		{

		}

		template<>
		LicenseT<wchar_t>::LicenseT() :
			m_Impl(*new LicenseImpl())
		{

		}

		template<>
		LicenseT<char>::~LicenseT()
		{
			delete& m_Impl;
		}

		template<>
		LicenseT<wchar_t>::~LicenseT()
		{
			delete& m_Impl;
		}

		template<>
		const char* LicenseT<char>::SaveXml()
		{
			return m_Impl.SaveXml();
		}

		template<>
		const char* LicenseT<wchar_t>::SaveXml()
		{
			return m_Impl.SaveXml();
		}

		template<>
		void LicenseT<char>::LoadXml(const char* xml)
		{
			m_Impl.LoadXml(xml);
		}

		template<>
		void LicenseT<wchar_t>::LoadXml(const char* xml)
		{
			m_Impl.LoadXml(xml);
		}

		template<>
		const char* LicenseT<char>::SaveJson()
		{
			return m_Impl.SaveJson();
		}

		template<>
		const char* LicenseT<wchar_t>::SaveJson()
		{
			return m_Impl.SaveJson();
		}

		template<>
		void LicenseT<char>::LoadJson(const char* xml)
		{
			m_Impl.LoadJson(xml);
		}

		template<>
		void LicenseT<wchar_t>::LoadJson(const char* xml)
		{
			m_Impl.LoadJson(xml);
		}

		template<>
		void LicenseT<char>::SetLicenseKey(const char* licenseKey)
		{
			m_Impl.SetLicenseKey(licenseKey);
		}

		template<>
		void LicenseT<wchar_t>::SetLicenseKey(const wchar_t* licenseKey)
		{
			m_Impl.SetLicenseKey(w2s(licenseKey).c_str());
		}

		template<>
		void LicenseT<char>::SetLicenseKeyValidationData(void* validationData, int validationDataLen)
		{
			m_Impl.SetLicenseKeyValidationData((const unsigned char*)validationData, validationDataLen);
		}

		template<>
		void LicenseT<wchar_t>::SetLicenseKeyValidationData(void* validationData, int validationDataLen)
		{
			m_Impl.SetLicenseKeyValidationData((const unsigned char*)validationData, validationDataLen);
		}

		template<>
		void LicenseT<char>::GetLicenseKeyValidationData(void** buf, int* len)
		{
			m_Impl.GetLicenseKeyValidationData((const void**)buf, len);
		}

		template<>
		void LicenseT<wchar_t>::GetLicenseKeyValidationData(void** buf, int* len)
		{
			m_Impl.GetLicenseKeyValidationData((const void**)buf, len);
		}

		template<>
		const char* LicenseT<char>::GetLicenseKey()
		{
			return m_Impl.GetLicenseKey();
		}

		template<>
		const wchar_t* LicenseT<wchar_t>::GetLicenseKey()
		{
			static wstring wstr1;

			wstr1 = s2w(m_Impl.GetLicenseKey());
			return wstr1.c_str();
		}

		template<>
		void LicenseT<char>::SetActivationKey(const char* activationKey)
		{
			m_Impl.SetActivationKey(activationKey);
		}

		template<>
		void LicenseT<wchar_t>::SetActivationKey(const wchar_t* activationKey)
		{
			m_Impl.SetActivationKey(w2s(activationKey).c_str());
		}

		template<>
		const char* LicenseT<char>::GetActivationKey()
		{
			return m_Impl.GetActivationKey();
		}

		template<>
		const wchar_t* LicenseT<wchar_t>::GetActivationKey()
		{
			static wstring wstr1;

			wstr1 = s2w(m_Impl.GetActivationKey());
			return wstr1.c_str();
		}

		template<>
		void LicenseT<char>::SetHardwareId(const char* hardwareId)
		{
			m_Impl.SetHardwareId(hardwareId);
		}

		template<>
		void LicenseT<wchar_t>::SetHardwareId(const wchar_t* hardwareId)
		{
			m_Impl.SetHardwareId(w2s(hardwareId).c_str());
		}

		template<>
		const char* LicenseT<char>::GetHardwareId()
		{
			return m_Impl.GetHardwareId();
		}

		template<>
		const wchar_t* LicenseT<wchar_t>::GetHardwareId()
		{
			static wstring wstr1;

			wstr1 = s2w(m_Impl.GetHardwareId());
			return wstr1.c_str();
		}

		template<>
		void LicenseT<char>::SetLease(bool isLease)
		{
			m_Impl.SetLease(isLease);
		}

		template<>
		void LicenseT<wchar_t>::SetLease(bool isLease)
		{
			m_Impl.SetLease(isLease);
		}

		template<>
		bool LicenseT<char>::IsLease()
		{
			return m_Impl.IsLease();
		}

		template<>
		bool LicenseT<wchar_t>::IsLease()
		{
			return m_Impl.IsLease();
		}
	};
}
