/*
 *  mms_server_common.c
 *
 *  Copyright 2013 Michael Zillgith
 *
 *  This file is part of libIEC61850.
 *
 *  libIEC61850 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.
 *
 *  libIEC61850 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 libIEC61850.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  See COPYING file for the complete license text.
 */

#include "libiec61850_platform_includes.h"
#include "mms_server_internal.h"

/* write_out function required for ASN.1 encoding */
int
mmsServer_write_out(const void *buffer, size_t size, void *app_key)
{
    ByteBuffer* writeBuffer = (ByteBuffer*) app_key;
    return ByteBuffer_append(writeBuffer, (uint8_t*) buffer, size);
}

MmsPdu_t*
mmsServer_createConfirmedResponse(uint32_t invokeId)
{
	MmsPdu_t* mmsPdu = (MmsPdu_t*) GLOBAL_CALLOC(1, sizeof(MmsPdu_t));

	mmsPdu->present = MmsPdu_PR_confirmedResponsePdu;

	asn_long2INTEGER(&(mmsPdu->choice.confirmedResponsePdu.invokeID),
		invokeId);

	return mmsPdu;
}


void
mmsServer_createConfirmedErrorPdu(uint32_t invokeId, ByteBuffer* response, MmsError errorType)
{
	MmsPdu_t* mmsPdu = (MmsPdu_t*) GLOBAL_CALLOC(1, sizeof(MmsPdu_t));
	mmsPdu->present = MmsPdu_PR_confirmedErrorPDU;

	asn_long2INTEGER(&(mmsPdu->choice.confirmedErrorPDU.invokeID),
			invokeId);

	if (errorType == MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT) {
		mmsPdu->choice.confirmedErrorPDU.serviceError.errorClass.present =
				ServiceError__errorClass_PR_access;

		asn_long2INTEGER(&mmsPdu->choice.confirmedErrorPDU.serviceError.errorClass.choice.access,
						ServiceError__errorClass__access_objectnonexistent);
	}
	else if (errorType == MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED) {
		mmsPdu->choice.confirmedErrorPDU.serviceError.errorClass.present =
				ServiceError__errorClass_PR_access;

		asn_long2INTEGER(&mmsPdu->choice.confirmedErrorPDU.serviceError.errorClass.choice.access,
				ServiceError__errorClass__access_objectaccessdenied);
	}
	else if (errorType == MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED) {
		mmsPdu->choice.confirmedErrorPDU.serviceError.errorClass.present =
				ServiceError__errorClass_PR_access;

		asn_long2INTEGER(&mmsPdu->choice.confirmedErrorPDU.serviceError.errorClass.choice.access,
				ServiceError__errorClass__access_objectaccessunsupported);
	}
	else if (errorType == MMS_ERROR_SERVICE_OTHER) {
	    mmsPdu->choice.confirmedErrorPDU.serviceError.errorClass.present =
	                            ServiceError__errorClass_PR_service;
        asn_long2INTEGER(&mmsPdu->choice.confirmedErrorPDU.serviceError.errorClass.choice.access,
                        ServiceError__errorClass__service_other);
	}
	else if (errorType == MMS_ERROR_DEFINITION_OTHER) {
	    mmsPdu->choice.confirmedErrorPDU.serviceError.errorClass.present =
                        ServiceError__errorClass_PR_definition;
        asn_long2INTEGER(&mmsPdu->choice.confirmedErrorPDU.serviceError.errorClass.choice.access,
                        ServiceError__errorClass__definition_other);
	}
	else if (errorType == MMS_ERROR_DEFINITION_OBJECT_EXISTS) {
	    mmsPdu->choice.confirmedErrorPDU.serviceError.errorClass.present =
                        ServiceError__errorClass_PR_definition;
        asn_long2INTEGER(&mmsPdu->choice.confirmedErrorPDU.serviceError.errorClass.choice.access,
                        ServiceError__errorClass__definition_objectexists);
	}
	else if (errorType == MMS_ERROR_DEFINITION_OBJECT_UNDEFINED) {
        mmsPdu->choice.confirmedErrorPDU.serviceError.errorClass.present =
                        ServiceError__errorClass_PR_definition;
        asn_long2INTEGER(&mmsPdu->choice.confirmedErrorPDU.serviceError.errorClass.choice.access,
                        ServiceError__errorClass__definition_objectundefined);
	}
	else if (errorType == MMS_ERROR_DEFINITION_TYPE_UNSUPPORTED) {
        mmsPdu->choice.confirmedErrorPDU.serviceError.errorClass.present =
                        ServiceError__errorClass_PR_definition;
        asn_long2INTEGER(&mmsPdu->choice.confirmedErrorPDU.serviceError.errorClass.choice.access,
                        ServiceError__errorClass__definition_typeunsupported);
    }
	else if (errorType == MMS_ERROR_FILE_FILE_NON_EXISTENT) {
        mmsPdu->choice.confirmedErrorPDU.serviceError.errorClass.present =
                        ServiceError__errorClass_PR_file;
        asn_long2INTEGER(&mmsPdu->choice.confirmedErrorPDU.serviceError.errorClass.choice.access,
                        ServiceError__errorClass__file_filenonexistent);
	}
	else if (errorType == MMS_ERROR_FILE_OTHER) {
        mmsPdu->choice.confirmedErrorPDU.serviceError.errorClass.present =
                        ServiceError__errorClass_PR_file;
        asn_long2INTEGER(&mmsPdu->choice.confirmedErrorPDU.serviceError.errorClass.choice.access,
                        ServiceError__errorClass__file_other);
	}
    else if (errorType == MMS_ERROR_RESOURCE_OTHER) {
        mmsPdu->choice.confirmedErrorPDU.serviceError.errorClass.present =
                        ServiceError__errorClass_PR_resource;
        asn_long2INTEGER(&mmsPdu->choice.confirmedErrorPDU.serviceError.errorClass.choice.access,
                        ServiceError__errorClass__resource_other);
    }
    else if (errorType == MMS_ERROR_RESOURCE_CAPABILITY_UNAVAILABLE) {
        mmsPdu->choice.confirmedErrorPDU.serviceError.errorClass.present =
                        ServiceError__errorClass_PR_resource;
        asn_long2INTEGER(&mmsPdu->choice.confirmedErrorPDU.serviceError.errorClass.choice.access,
                        ServiceError__errorClass__resource_capabilityunavailable);
    }

	der_encode(&asn_DEF_MmsPdu, mmsPdu,
			mmsServer_write_out, (void*) response);

	asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0);
}

int
mmsServer_isIndexAccess(AlternateAccess_t* alternateAccess)
{
	if (alternateAccess->list.array[0]->present == AlternateAccess__Member_PR_unnamed) {
		if ((alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present
				== AlternateAccessSelection__selectAccess_PR_index) ||
			(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present
				== AlternateAccessSelection__selectAccess_PR_indexRange))
		{
			return 1;
		}
		else
			return 0;
	}
	else
		return 0;
}

int
mmsServer_getLowIndex(AlternateAccess_t* alternateAccess)
{
	if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present
				== AlternateAccessSelection__selectAccess_PR_index)
	{
		long index;
		asn_INTEGER2long(
				&alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.index,
				&index);

		return (int) index;
	}

	if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present
				== AlternateAccessSelection__selectAccess_PR_indexRange)
	{
		long index;
		asn_INTEGER2long(
				&alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.indexRange.lowIndex,
				&index);

		return (int) index;
	}

	return -1;
}

int
mmsServer_getNumberOfElements(AlternateAccess_t* alternateAccess)
{
	if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present
				== AlternateAccessSelection__selectAccess_PR_indexRange)
	{
		long number;

		asn_INTEGER2long(
				&alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.indexRange.numberOfElements,
				&number);

		return (int) number;
	}

	return 0;
}

MmsNamedVariableList
mmsServer_getNamedVariableListWithName(LinkedList namedVariableLists, const char* variableListName)
{
    MmsNamedVariableList variableList = NULL;

    LinkedList element = LinkedList_getNext(namedVariableLists);

    while (element != NULL) {
        MmsNamedVariableList varList = (MmsNamedVariableList) element->data;

        if (strcmp(MmsNamedVariableList_getName(varList), variableListName) == 0) {
            variableList = varList;
            break;
        }

        element = LinkedList_getNext(element);
    }

    return variableList;
}


void
mmsServer_deleteVariableList(LinkedList namedVariableLists, char* variableListName)
{
	LinkedList previousElement = namedVariableLists;
	LinkedList element = LinkedList_getNext(namedVariableLists);

	while (element != NULL ) {
		MmsNamedVariableList varList = (MmsNamedVariableList) element->data;

		if (strcmp(MmsNamedVariableList_getName(varList), variableListName)
				== 0) {
			previousElement->next = element->next;
			GLOBAL_FREEMEM(element);
			MmsNamedVariableList_destroy(varList);

			break;
		}

		previousElement = element;
		element = LinkedList_getNext(element);
	}
}


