/*
 * Copyright (C) 2014-2022 CZ.NIC
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * In addition, as a special exception, the copyright holders give
 * permission to link the code of portions of this program with the
 * OpenSSL library under certain conditions as described in each
 * individual source file, and distribute linked combinations including
 * the two.
 */

#include <cstdlib> /* ::std::malloc */
#include <QtGlobal> /* Q_ASSERT() */

#include "src/datovka_shared/isds/type_conversion.h" /* Isds::long2DbState() */
#include "src/isds/internal_type_conversion.h"

enum Isds::Type::DbType IsdsInternal::libisdsDbType2DbType(const isds_DbType ibt,
    bool *ok)
{
	bool iOk = true;
	enum Isds::Type::DbType type = Isds::Type::BT_NULL;

	switch (ibt) {
	case DBTYPE_SYSTEM: type = Isds::Type::BT_SYSTEM; break;
	case DBTYPE_OVM: type = Isds::Type::BT_OVM; break;
	case DBTYPE_OVM_NOTAR: type = Isds::Type::BT_OVM_NOTAR; break;
	case DBTYPE_OVM_EXEKUT: type = Isds::Type::BT_OVM_EXEKUT; break;
	case DBTYPE_OVM_REQ: type = Isds::Type::BT_OVM_REQ; break;
	case DBTYPE_OVM_FO: type = Isds::Type::BT_OVM_FO; break;
	case DBTYPE_OVM_PFO: type = Isds::Type::BT_OVM_PFO; break;
	case DBTYPE_OVM_PO: type = Isds::Type::BT_OVM_PO; break;
	case DBTYPE_PO: type = Isds::Type::BT_PO; break;
	case DBTYPE_PO_ZAK: type = Isds::Type::BT_PO_ZAK; break;
	case DBTYPE_PO_REQ: type = Isds::Type::BT_PO_REQ; break;
	case DBTYPE_PFO: type = Isds::Type::BT_PFO; break;
	case DBTYPE_PFO_ADVOK: type = Isds::Type::BT_PFO_ADVOK; break;
	case DBTYPE_PFO_DANPOR: type = Isds::Type::BT_PFO_DANPOR; break;
	case DBTYPE_PFO_INSSPR: type = Isds::Type::BT_PFO_INSSPR; break;
	case DBTYPE_PFO_AUDITOR: type = Isds::Type::BT_PFO_AUDITOR; break;
	case DBTYPE_PFO_ZNALEC: type = Isds::Type::BT_PFO_ZNALEC; break;
	case DBTYPE_PFO_TLUMOCNIK: type = Isds::Type::BT_PFO_TLUMOCNIK; break;
	case DBTYPE_FO: type = Isds::Type::BT_FO; break;
	default:
		Q_ASSERT(0);
		iOk = false;
		type = Isds::Type::BT_SYSTEM; /* FIXME */
		break;
	}

	if (ok != Q_NULLPTR) {
		*ok = iOk;
	}
	return type;
}

isds_DbType IsdsInternal::dbType2libisdsDbType(enum Isds::Type::DbType bt,
    bool *ok)
{
	bool iOk = true;
	isds_DbType ibt = DBTYPE_SYSTEM;

	switch (bt) {
	/*
	 * Isds::Type::BT_NULL same as default.
	 */
	case Isds::Type::BT_SYSTEM: ibt = DBTYPE_SYSTEM; break;
	case Isds::Type::BT_OVM: ibt = DBTYPE_OVM; break;
	case Isds::Type::BT_OVM_NOTAR: ibt = DBTYPE_OVM_NOTAR; break;
	case Isds::Type::BT_OVM_EXEKUT: ibt = DBTYPE_OVM_EXEKUT; break;
	case Isds::Type::BT_OVM_REQ: ibt = DBTYPE_OVM_REQ; break;
	case Isds::Type::BT_OVM_FO: ibt = DBTYPE_OVM_FO; break;
	case Isds::Type::BT_OVM_PFO: ibt = DBTYPE_OVM_PFO; break;
	case Isds::Type::BT_OVM_PO: ibt = DBTYPE_OVM_PO; break;
	case Isds::Type::BT_PO: ibt = DBTYPE_PO; break;
	case Isds::Type::BT_PO_ZAK: ibt = DBTYPE_PO_ZAK; break;
	case Isds::Type::BT_PO_REQ: ibt = DBTYPE_PO_REQ; break;
	case Isds::Type::BT_PFO: ibt = DBTYPE_PFO; break;
	case Isds::Type::BT_PFO_ADVOK: ibt = DBTYPE_PFO_ADVOK; break;
	case Isds::Type::BT_PFO_DANPOR: ibt = DBTYPE_PFO_DANPOR; break;
	case Isds::Type::BT_PFO_INSSPR: ibt = DBTYPE_PFO_INSSPR; break;
	case Isds::Type::BT_PFO_AUDITOR: ibt = DBTYPE_PFO_AUDITOR; break;
	case Isds::Type::BT_PFO_ZNALEC: ibt = DBTYPE_PFO_ZNALEC; break;
	case Isds::Type::BT_PFO_TLUMOCNIK: ibt = DBTYPE_PFO_TLUMOCNIK; break;
	case Isds::Type::BT_FO: ibt = DBTYPE_FO; break;
	default:
		Q_ASSERT(0);
		iOk = false;
		break;
	}

	if (ok != Q_NULLPTR) {
		*ok = iOk;
	}
	return ibt;
}

enum Isds::Type::DbType IsdsInternal::libisdsDbTypePtr2DbType(
    const isds_DbType *ibtp, bool *ok)
{
	if (ibtp == NULL) {
		if (ok != Q_NULLPTR) {
			*ok = true;
		}
		return Isds::Type::BT_NULL;
	}

	return libisdsDbType2DbType(*ibtp, ok);
}

bool IsdsInternal::dbType2libisdsDbTypePtr(isds_DbType **tgt,
    enum Isds::Type::DbType src)
{
	if (Q_UNLIKELY(tgt == Q_NULLPTR)) {
		Q_ASSERT(0);
		return false;
	}
	if (src == Isds::Type::BT_NULL) {
		if (*tgt != NULL) {
			::std::free(*tgt); *tgt = NULL;
		}
		return true;
	}
	if (*tgt == NULL) {
		*tgt = (isds_DbType *)::std::malloc(sizeof(**tgt));
		if (Q_UNLIKELY(*tgt == NULL)) {
			Q_ASSERT(0);
			return false;
		}
	}

	bool ok = false;
	**tgt = IsdsInternal::dbType2libisdsDbType(src, &ok);
	if (Q_UNLIKELY(!ok)) {
		::std::free(*tgt); *tgt = NULL;
		return false;
	}

	return true;
}

enum Isds::Type::DbState IsdsInternal::longPtr2DbState(const long int *bs,
    bool *ok)
{
	if (bs == NULL) {
		if (ok != Q_NULLPTR) {
			*ok = true;
		}
		return Isds::Type::BS_ERROR;
	}

	return Isds::long2DbState(*bs, ok);
}

bool IsdsInternal::dbState2longPtr(long int **tgt, enum Isds::Type::DbState src)
{
	if (Q_UNLIKELY(tgt == Q_NULLPTR)) {
		Q_ASSERT(0);
		return false;
	}
	if (*tgt == NULL) {
		*tgt = (long int *)::std::malloc(sizeof(**tgt));
		if (Q_UNLIKELY(tgt == NULL)) {
			Q_ASSERT(0);
			return false;
		}
	}
	switch (src) {
	case Isds::Type::BS_ERROR:
		::std::free(*tgt); *tgt = NULL;
		break;
	default:
		**tgt = src;
		break;
	}

	return true;
}

enum Isds::Type::UserType IsdsInternal::libisdsUserType2UserType(
    const isds_UserType *ut, bool *ok)
{
	if (ut == NULL) {
		if (ok != Q_NULLPTR) {
			*ok = true;
		}
		return Isds::Type::UT_NULL;
	}

	bool iOk = true;
	enum Isds::Type::UserType type = Isds::Type::UT_NULL;

	switch (*ut) {
	case USERTYPE_PRIMARY: type = Isds::Type::UT_PRIMARY; break;
	case USERTYPE_ENTRUSTED: type = Isds::Type::UT_ENTRUSTED; break;
	case USERTYPE_ADMINISTRATOR: type = Isds::Type::UT_ADMINISTRATOR; break;
	case USERTYPE_OFFICIAL: type = Isds::Type::UT_OFFICIAL; break;
	case USERTYPE_OFFICIAL_CERT: type = Isds::Type::UT_OFFICIAL_CERT; break;
	case USERTYPE_LIQUIDATOR: type = Isds::Type::UT_LIQUIDATOR; break;
	case USERTYPE_RECEIVER: type = Isds::Type::UT_RECEIVER; break;
	case USERTYPE_GUARDIAN: type = Isds::Type::UT_GUARDIAN; break;
	default:
		Q_ASSERT(0);
		iOk = false;
		type = Isds::Type::UT_NULL;
		break;
	}

	if (ok != Q_NULLPTR) {
		*ok = iOk;
	}
	return type;
}

bool IsdsInternal::userType2libisdsUserType(isds_UserType **tgt,
    enum Isds::Type::UserType src)
{
	if (Q_UNLIKELY(tgt == Q_NULLPTR)) {
		Q_ASSERT(0);
		return false;
	}
	if (src == Isds::Type::UT_NULL) {
		if (*tgt != NULL) {
			::std::free(*tgt); *tgt = NULL;
		}
		return true;
	}
	if (*tgt == NULL) {
		*tgt = (isds_UserType *)::std::malloc(sizeof(**tgt));
		if (Q_UNLIKELY(*tgt == NULL)) {
			Q_ASSERT(0);
			return false;
		}
	}
	switch (src) {
	/*
	 * Isds::Type::UT_NULL cannot be reached here.
	 *
	 * case Isds::Type::UT_NULL:
	 * 	::std::free(*tgt); *tgt = NULL;
	 * 	break;
	 */
	case Isds::Type::UT_PRIMARY: **tgt = USERTYPE_PRIMARY; break;
	case Isds::Type::UT_ENTRUSTED: **tgt = USERTYPE_ENTRUSTED; break;
	case Isds::Type::UT_ADMINISTRATOR: **tgt = USERTYPE_ADMINISTRATOR; break;
	case Isds::Type::UT_OFFICIAL: **tgt = USERTYPE_OFFICIAL; break;
	case Isds::Type::UT_OFFICIAL_CERT: **tgt = USERTYPE_OFFICIAL_CERT; break;
	case Isds::Type::UT_LIQUIDATOR: **tgt = USERTYPE_LIQUIDATOR; break;
	case Isds::Type::UT_RECEIVER: **tgt = USERTYPE_RECEIVER; break;
	case Isds::Type::UT_GUARDIAN: **tgt = USERTYPE_GUARDIAN; break;
	default:
		Q_ASSERT(0);
		::std::free(*tgt); *tgt = NULL;
		return false;
		break;
	}

	return true;
}

Isds::Type::Privileges IsdsInternal::longPtr2Privileges(long int *ip)
{
	if (ip == NULL) {
		return Isds::Type::PRIVIL_NONE;
	}

	return Isds::long2Privileges((qint64)*ip);
}

bool IsdsInternal::privileges2longPtr(long int **tgt,
    Isds::Type::Privileges src)
{
	if (Q_UNLIKELY(tgt == Q_NULLPTR)) {
		Q_ASSERT(0);
		return false;
	}
	if (*tgt == NULL) {
		*tgt = (long int *)::std::malloc(sizeof(**tgt));
		if (Q_UNLIKELY(tgt == NULL)) {
			Q_ASSERT(0);
			return false;
		}
	}

	**tgt = src;
	return true;
}

enum Isds::Type::OtpMethod IsdsInternal::libisdsOtpMethod2OtpMethod(
    isds_otp_method iom, bool *ok)
{
	bool iOk = true;
	enum Isds::Type::OtpMethod method = Isds::Type::OM_UNKNOWN;

	switch (iom) {
	case OTP_HMAC: method = Isds::Type::OM_HMAC; break;
	case OTP_TIME: method = Isds::Type::OM_TIME; break;
	default:
		Q_ASSERT(0);
		iOk = false;
		break;
	}

	if (ok != Q_NULLPTR) {
		*ok = iOk;
	}
	return method;
}

isds_otp_method IsdsInternal::otpMethod2libisdsOtpMethod(
    enum Isds::Type::OtpMethod om, bool *ok)
{
	bool iOk = true;
	isds_otp_method iom = OTP_HMAC;

	switch (om) {
	/*
	 * Isds::Type::OM_UNKNOWN same as default.
	 */
	case Isds::Type::OM_HMAC: iom = OTP_HMAC; break;
	case Isds::Type::OM_TIME: iom = OTP_TIME; break;
	default:
		Q_ASSERT(0);
		iOk = false;
		break;
	}

	if (ok != Q_NULLPTR) {
		*ok = iOk;
	}
	return iom;
}

enum Isds::Type::OtpResolution IsdsInternal::libisdsOtpResolution2OtpResolution(
    isds_otp_resolution ior, bool *ok)
{
	bool iOk = true;
	enum Isds::Type::OtpResolution resolution = Isds::Type::OR_UNKNOWN;

	switch (ior) {
	case OTP_RESOLUTION_SUCCESS: resolution = Isds::Type::OR_SUCCESS; break;
	case OTP_RESOLUTION_UNKNOWN: resolution = Isds::Type::OR_UNKNOWN; break;
	case OTP_RESOLUTION_BAD_AUTHENTICATION: resolution = Isds::Type::OR_BAD_AUTH; break;
	case OTP_RESOLUTION_ACCESS_BLOCKED: resolution = Isds::Type::OR_BLOCKED; break;
	case OTP_RESOLUTION_PASSWORD_EXPIRED: resolution = Isds::Type::OR_PWD_EXPIRED; break;
	case OTP_RESOLUTION_TOO_FAST: resolution = Isds::Type::OR_TOO_FAST; break;
	case OTP_RESOLUTION_UNAUTHORIZED: resolution = Isds::Type::OR_UNAUTHORIZED; break;
	case OTP_RESOLUTION_TOTP_SENT: resolution = Isds::Type::OR_TOTP_SENT; break;
	case OTP_RESOLUTION_TOTP_NOT_SENT: resolution = Isds::Type::OR_TOTP_NOT_SENT; break;
	default:
		Q_ASSERT(0);
		iOk = false;
		break;
	}

	if (ok != Q_NULLPTR) {
		*ok = iOk;
	}
	return resolution;
}

isds_otp_resolution IsdsInternal::otpResolution2libisdsOtpResolution(
    enum Isds::Type::OtpResolution ores, bool *ok)
{
	bool iOk = true;
	isds_otp_resolution ior = OTP_RESOLUTION_UNKNOWN;

	switch (ores) {
	case Isds::Type::OR_SUCCESS: ior = OTP_RESOLUTION_SUCCESS; break;
	case Isds::Type::OR_UNKNOWN: ior = OTP_RESOLUTION_UNKNOWN; break;
	case Isds::Type::OR_BAD_AUTH: ior = OTP_RESOLUTION_BAD_AUTHENTICATION; break;
	case Isds::Type::OR_BLOCKED: ior = OTP_RESOLUTION_ACCESS_BLOCKED; break;
	case Isds::Type::OR_PWD_EXPIRED: ior = OTP_RESOLUTION_PASSWORD_EXPIRED; break;
	case Isds::Type::OR_TOO_FAST: ior = OTP_RESOLUTION_TOO_FAST; break;
	case Isds::Type::OR_UNAUTHORIZED: ior = OTP_RESOLUTION_UNAUTHORIZED; break;
	case Isds::Type::OR_TOTP_SENT: ior = OTP_RESOLUTION_TOTP_SENT; break;
	case Isds::Type::OR_TOTP_NOT_SENT: ior = OTP_RESOLUTION_TOTP_NOT_SENT; break;
	default:
		Q_ASSERT(0);
		iOk = false;
		break;
	}

	if (ok != Q_NULLPTR) {
		*ok = iOk;
	}
	return ior;
}

enum Isds::Type::MepResolution IsdsInternal::libisdsMepResolution2MepResolution(
    isds_mep_resolution imp, bool *ok)
{
	bool iOk = true;
	enum Isds::Type::MepResolution resolution = Isds::Type::MR_UNKNOWN;

	switch (imp) {
	case MEP_RESOLUTION_SUCCESS: resolution = Isds::Type::MR_SUCCESS; break;
	case MEP_RESOLUTION_UNKNOWN: resolution = Isds::Type::MR_UNKNOWN; break;
	case MEP_RESOLUTION_UNRECOGNISED: resolution = Isds::Type::MR_UNRECOGNISED; break;
	case MEP_RESOLUTION_ACK_REQUESTED: resolution = Isds::Type::MR_ACK_REQUESTED; break;
	case MEP_RESOLUTION_ACK: resolution = Isds::Type::MR_ACK; break;
	case MEP_RESOLUTION_ACK_EXPIRED: resolution = Isds::Type::MR_ACK_EXPIRED; break;
	default:
		Q_ASSERT(0);
		iOk = false;
		break;
	}

	if (ok != Q_NULLPTR) {
		*ok = iOk;
	}
	return resolution;
}

isds_mep_resolution IsdsInternal::mepResolution2libisdsMepResolution(
    enum Isds::Type::MepResolution mres, bool *ok)
{
	bool iOk = true;
	isds_mep_resolution imr = MEP_RESOLUTION_UNKNOWN;

	switch (mres) {
	case Isds::Type::MR_SUCCESS: imr = MEP_RESOLUTION_SUCCESS; break;
	case Isds::Type::MR_UNKNOWN: imr = MEP_RESOLUTION_UNKNOWN; break;
	case Isds::Type::MR_UNRECOGNISED: imr = MEP_RESOLUTION_UNRECOGNISED; break;
	case Isds::Type::MR_ACK_REQUESTED: imr = MEP_RESOLUTION_ACK_REQUESTED; break;
	case Isds::Type::MR_ACK: imr = MEP_RESOLUTION_ACK; break;
	case Isds::Type::MR_ACK_EXPIRED: imr = MEP_RESOLUTION_ACK_EXPIRED; break;
	default:
		Q_ASSERT(0);
		iOk = false;
		break;
	}

	if (ok != Q_NULLPTR) {
		*ok = iOk;
	}
	return imr;
}

enum Isds::Type::DTType IsdsInternal::libisdsDTType2DTType(
    const enum isds_vault_type *vt, bool *ok)
{
	if (vt == NULL) {
		if (ok != Q_NULLPTR) {
			*ok = false;
		}
		return Isds::Type::DTT_UNKNOWN;
	}

	bool iOk = true;
	enum Isds::Type::DTType dtType = Isds::Type::DTT_UNKNOWN;

	switch (*vt) {
	case VAULT_NONE: dtType = Isds::Type::DTT_INACTIVE; break;
	case VAULT_PREPAID: dtType = Isds::Type::DTT_PREPAID; break;
	case VAULT_UNUSED_2: dtType = Isds::Type::DTT_UNUSED_2; break;
	case VAULT_CONTRACTUAL: dtType = Isds::Type::DTT_CONTRACTUAL; break;
	case VAULT_TRIAL: dtType = Isds::Type::DTT_TRIAL; break;
	case VAULT_UNUSED_5: dtType = Isds::Type::DTT_UNUSED_5; break;
	case VAULT_SPECIAL_OFFER: dtType = Isds::Type::DTT_SPECIAL_OFFER; break;
	default:
		Q_ASSERT(0);
		iOk = false;
		break;
	}

	if (ok != Q_NULLPTR) {
		*ok = iOk;
	}
	return dtType;
}

enum Isds::Type::FutDTPaidState IsdsInternal::libisdsDTPaid2DTPaid(
    const enum isds_vault_payment_status *vpt, bool *ok)
{
	if (vpt == NULL) {
		if (ok != Q_NULLPTR) {
			*ok = true;
		}
		return Isds::Type::FDTPS_UNKNOWN;
	}

	bool iOk = true;
	enum Isds::Type::FutDTPaidState pStatus = Isds::Type::FDTPS_UNKNOWN;

	switch (*vpt) {
	case VAULT_NOT_PAID_YET: pStatus = Isds::Type::FDTPS_NOT_PAID_YET;
		break;
	case VAULT_PAID_ALREADY: pStatus = Isds::Type::FDTPS_PAID_ALREADY;
		break;
	default:
		Q_ASSERT(0);
		iOk = false;
		break;
	}

	if (ok != Q_NULLPTR) {
		*ok = iOk;
	}
	return pStatus;
}
