
#include "Stdafx.h"

extern "C" {
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lkf5defs.h"
}

using namespace System::IO;

#include "LKFController.h"
#include "LKFContext.h"
#include "LKFAufenthalt.h"
#include "LKFException.h"
#include "LKFIntContext.h"
#include "LKFStatContext.h"
#include "LKFNeoPaedContext.h"
#include "LKFMetaData.h"

namespace lkfxdok {
	namespace c {

		LKFContext::LKFContext()
		{
			_contextPtr = lkf_alloc_context(0, "", 0);
			if (_contextPtr == nullptr) {
				throw gcnew LKFMemoryException("cannot alloc lkf context");
			}
		}

		LKFContext::~LKFContext() {
			this->!LKFContext();
		}

		LKFContext::!LKFContext() {
			if (_contextPtr != nullptr) {
				lkf_free_context((LKF_CONTEXT *)_contextPtr);
				_contextPtr = nullptr;
			}
			
		}

		void LKFContext::Debug_Free() {
			if (_contextPtr != nullptr) {
				lkf_free_context((LKF_CONTEXT *)_contextPtr);
				_contextPtr = nullptr;
			}
		}

		int LKFContext::Flags::get (void) {
			return _flags;
		}
		
		void LKFContext::Flags::set(int flags) {
			_flags = flags & ~(1<<5);
			lkf_set_contextflags((LKF_CONTEXT *)_contextPtr, _flags);
		}

		void LKFContext::SetMetaData(LKFMetaData^ metaData) {
			lkf_set_modelljahr_stamm((LKF_CONTEXT*)_contextPtr, (LKF_METADATA *) metaData->_ptr);
			_modelljahr = metaData->Modelljahr;
		}

		int LKFContext::Modelljahr::get(void) {
			return _modelljahr;
		}

		void LKFContext::Modelljahr::set(int modelljahr) {
			char errorBuffer[4096]; /* V2025.1: buffer groesser */
			_modelljahr = modelljahr;

			char* path = (char*)Marshal::StringToHGlobalAnsi(LKFController::DataPath).ToPointer();
			int res = lkf_set_modelljahr((LKF_CONTEXT *)_contextPtr, modelljahr, path, errorBuffer);
			if (res) {
				Marshal::FreeHGlobal((IntPtr)path);
				LKFException ^ex = gcnew LKFException(res, gcnew String(errorBuffer, 0, (int) strlen(errorBuffer), System::Text::Encoding::UTF8));
				throw ex;
			}
			Marshal::FreeHGlobal((IntPtr)path);
		}

		int LKFContext::Abrechnungsjahr::get ()
		{
			return _abrechnungsjahr;
		}
		
		void LKFContext::Abrechnungsjahr::set(int abrechnungsjahr) {
			_abrechnungsjahr = abrechnungsjahr;
			lkf_set_abrechnungsjahr((LKF_CONTEXT *)_contextPtr, abrechnungsjahr);
		}

		void LKFContext::ThrowOutOfMemoryException(String^ functionName) {
			const char* mt = lkf_context_get_outOfMemoryText((LKF_CONTEXT*)_contextPtr);
			if (mt != nullptr) {
				throw gcnew LKFMemoryException(gcnew String(mt, 0, (int)strlen(mt), System::Text::Encoding::UTF8));
			}
			throw gcnew LKFMemoryException(functionName + ": LKF out of memory");
		}
		

		void LKFContext::SetAbrechnungsPeriode(int jahr, int period_val, int lfdnr, LKFContextPeriode type, int flags) {
			_abrechnungsjahr = jahr;
			lkf_set_contextparameters((LKF_CONTEXT *)_contextPtr, jahr, period_val, lfdnr, (char)type, flags & ~(1 << 5));
		}

		void LKFContext::EnableTrace(int facility, int level) {
			lkf_set_debug_tracefile((LKF_CONTEXT *)_contextPtr, 0, 0, 0);
			lkf_trace_setLevel((LKF_CONTEXT *)_contextPtr, facility, level);
		}

		void LKFContext::SetTraceAufenthalt(String ^patid) {
			if (patid == nullptr)
				lkf_trace_setAufenthalt((LKF_CONTEXT *)_contextPtr, 0);
			else {
				char* traceAufenthalt = (char*)Marshal::StringToHGlobalAnsi(patid).ToPointer();
				lkf_trace_setAufenthalt((LKF_CONTEXT *)_contextPtr, traceAufenthalt);
				Marshal::FreeHGlobal((IntPtr)traceAufenthalt);
			}
		}

		void LKFContext::EnableDebugTrace() {
			EnableTrace(0, 2);
		}

		unsigned int LKFContext::TraceLineCount::get()
		{
			return lkf_trace_getLineCount((LKF_CONTEXT *)_contextPtr);
		}

		String ^LKFContext::GetTraceLine(unsigned int index) {
			const char *line = lkf_trace_getLine((LKF_CONTEXT *)_contextPtr, index);
			if (line)
				return gcnew String(line, 0, (int) strlen(line), System::Text::Encoding::UTF8);
			return nullptr;
		}

		String^ LKFContext::GetMeldeDiagTypeText(char typ) {
			const char* line = lkf_plaus_meldediag_typ((LKF_CONTEXT*)_contextPtr, typ);
			if (line)
				return gcnew String(line, 0, (int)strlen(line), System::Text::Encoding::UTF8);
			return nullptr;
		}

		String^ LKFContext::GetMeldeDiagText(char typ) {
			const char* line = lkf_plaus_meldediag_text((LKF_CONTEXT*)_contextPtr, typ);
			if (line)
				return gcnew String(line, 0, (int)strlen(line), System::Text::Encoding::UTF8);
			return nullptr;
		}

		String^ LKFContext::GetErrorText() {
			const char* line = lkf_context_get_errortext((LKF_CONTEXT*)_contextPtr);
			if (line)
				return gcnew String(line, 0, (int)strlen(line), System::Text::Encoding::UTF8);
			return nullptr;
		}

		String^ LKFContext::GetOutOfMemoryText() {
			const char* line = lkf_context_get_outOfMemoryText ((LKF_CONTEXT*)_contextPtr);
			if (line)
				return gcnew String(line, 0, (int)strlen(line), System::Text::Encoding::UTF8);
			return nullptr;
		}

		LKFContextWithData::LKFContextWithData() : LKFContext()
		{
			_lastImportCharCount = 0;
			_lastImportErrorCount = 0;
			_lastImportLineCount = 0;
		}

		unsigned int LKFContextWithData::AufenthaltCount::get(void) {
			return lkf_get_aufenthalt_anz((LKF_CONTEXT *)_contextPtr);
		}

		unsigned int LKFContextWithData::AdditionalLines::get(void) {
			return lkf_getaddlineanz((LKF_CONTEXT *)_contextPtr);
		}

		String ^LKFContextWithData::GetAdditionalLine(unsigned int index) {
			char buf[2048];
			if (!lkf_getaddline((LKF_CONTEXT *)_contextPtr, index, buf))
				return gcnew String(buf, 0, (int) strlen(buf), System::Text::Encoding::UTF8);
			return nullptr;
		}

		void LKFContextWithData::PlausCheck() {
			lkf_check((LKF_CONTEXT *)_contextPtr, 0);
		}

		void LKFContextWithData::Scoring() {
			lkf_context_scoring((LKF_CONTEXT *)_contextPtr, 0);
		}

		int LKFContextWithData::PlausCheck ([Out] String^% error) {
			int ret = lkf_check((LKF_CONTEXT*)_contextPtr, 0);
			if (ret != 0) {
				error = GetLastError();
			}
			return ret;
		}

		int LKFContextWithData::Scoring ([Out] String^% error) {
			int ret = lkf_context_scoring((LKF_CONTEXT*)_contextPtr, 0);
			if (ret < 0) {
				error = GetLastError();
			}
			return ret;
		}

		String^ LKFContextWithData::GetLastError() {
			const char* error = lkf_get_context_error((LKF_CONTEXT*)_contextPtr);
			if (error != nullptr) {
				return gcnew String(error, 0, (int) strlen(error), System::Text::Encoding::UTF8);
			}
			return nullptr;
		}

		LKFIntContext ^LKFContextWithData::IntScoring() {
			int ret = lkf_int_scoring((LKF_CONTEXT *)_contextPtr);
			if (ret == LKF_OUTOFMEMORY_ERROR) {
				throw gcnew LKFMemoryException(this->GetOutOfMemoryText());
			}
			return gcnew LKFIntContext(this);
		}

		LKFStatContext ^LKFContextWithData::Stat() {
			int ret = lkf_stat((LKF_CONTEXT *)_contextPtr);
			if (ret == LKF_OUTOFMEMORY_UINTERROR) {
				ThrowOutOfMemoryException("LKFContextWithData::Stat");
			}
			return gcnew LKFStatContext(this);
		}

		LKFNeoPaedContext ^LKFContextWithData::NeoPaed() {
			lkf_stat((LKF_CONTEXT *)_contextPtr);
			return gcnew LKFNeoPaedContext(this);
		}

		LKFAufenthalt ^LKFContextWithData::GetAufenthalt(unsigned int index)
		{
			LKF_AUFENTHALT *aufPtr = lkf_get_aufenthalt((LKF_CONTEXT *)_contextPtr, index);
			if (aufPtr != nullptr)
				return gcnew LKFAufenthalt(this, (void*)aufPtr);
			return nullptr;
		}

		void LKFContextWithData::AddLine(String ^line) 
		{
			char *cline = (char*)(Marshal::StringToHGlobalAnsi(line)).ToPointer();
			lkf_add_line((LKF_CONTEXT *)_contextPtr, cline);
			Marshal::FreeHGlobal((IntPtr)cline);
		};

		void LKFContextWithData::AddBuffer(array<unsigned char> ^buffer)
		{
			int charCount, errorCount;
			char *cBuffer = new char[buffer->Length + 1];
			Marshal::Copy(buffer, 0, IntPtr(cBuffer), buffer->Length);
			cBuffer[buffer->Length] = 0;
			_lastImportLineCount = lkf_add_lines2((LKF_CONTEXT *)_contextPtr, cBuffer, &charCount, &errorCount);
			delete cBuffer;

			if (_lastImportLineCount == LKF_OUTOFMEMORY_ERROR) {
				_lastImportCharCount = charCount;
				_lastImportErrorCount = errorCount;
				ThrowOutOfMemoryException("LKFContextWithData::AddBuffer");
			}

			_lastImportCharCount = charCount;
			_lastImportErrorCount = errorCount;
		}

		void LKFContextWithData::LoadFile(String ^fileName) 
		{
			StreamReader ^r = gcnew StreamReader(fileName);
			while (!r->EndOfStream) {
				AddLine(r->ReadLine());
			}
			r->Close();
		}

		void LKFContextWithData::WriteFile(LKFContextFileType fileType, String^ outputFileName) {
			StreamWriter^ f = gcnew StreamWriter(outputFileName);
			WriteFile(fileType, f);
			f->Close();
		}

		void LKFContextWithData::WriteCSVFile(array<int>^ fields, String^ outputFileName) {
			StreamWriter^ f = gcnew StreamWriter(outputFileName);
			WriteCSVFile(fields, f);
			f->Close();
		}

		
		void LKFContextWithData::WriteResultFile(StreamWriter^ f) {
			unsigned int addlineCount, aufenthaltCount, aufenthaltLineCount, i, j, k;
			char line[2048];
			LKF_AUFENTHALT* pAufenthalt;
			String ^lastAddLineAnz;

			addlineCount = lkf_getaddlineanz((LKF_CONTEXT*)_contextPtr);
			for (i = 0; i < addlineCount; i++) {
				lkf_getaddline((LKF_CONTEXT*)_contextPtr, i, line);
				lastAddLineAnz = gcnew String(line, 0, (int) strlen(line), System::Text::Encoding::UTF8);
				if (lastAddLineAnz->StartsWith("K01")) {
					f->WriteLine(lastAddLineAnz);
				}
				else {
					break;
				}
			}

			aufenthaltCount = lkf_get_aufenthalt_anz((LKF_CONTEXT*)_contextPtr);

			for (j = 0; j < aufenthaltCount; j++) {
				pAufenthalt = lkf_get_aufenthalt((LKF_CONTEXT*)_contextPtr, j);
				aufenthaltLineCount = lkf_auf_getlineanz(pAufenthalt);
				for (k = 0; k < aufenthaltLineCount; k++) {
					lkf_auf_getline(pAufenthalt, k, line);
					f->WriteLine(gcnew String(line, 0, (int) strlen(line), System::Text::Encoding::UTF8));
				}
			}

			if (i < addlineCount) {
				f->WriteLine(lastAddLineAnz);
				for (; i < addlineCount; i++) {
					lkf_getaddline((LKF_CONTEXT*)_contextPtr, i, line);
					f->WriteLine(gcnew String(line, 0, (int) strlen(line), System::Text::Encoding::UTF8));
				}
			}
		}

		void LKFContextWithData::WriteCSVFile(array<int>^ fields, StreamWriter^ streamWriter)
		{	
			unsigned int aufenthaltCount, j;

			char line[2048];
			LKF_AUFENTHALT* pAufenthalt;
			
			unsigned int* fieldIndices = new unsigned int[fields->Length];
			Marshal::Copy(fields, 0, IntPtr(fieldIndices), fields->Length);
			lkf_define_custom_line((LKF_CONTEXT*)_contextPtr, fields->Length, fieldIndices, 1, 0);
			delete fieldIndices;

			aufenthaltCount = lkf_get_aufenthalt_anz((LKF_CONTEXT*)_contextPtr);

			for (j = 0; j < aufenthaltCount; j++) {
				pAufenthalt = lkf_get_aufenthalt((LKF_CONTEXT*)_contextPtr, j);
				lkf_auf_line_custom(pAufenthalt, line);
				streamWriter->WriteLine(gcnew String(line, 0, (int)strlen(line), System::Text::Encoding::UTF8));
			}
		}

		void LKFContextWithData::WriteFile(LKFContextFileType fileType, StreamWriter^ f) {
			unsigned int count, i;
			char line[2048];

			if (fileType == LKFContextFileType::Stat) {
				Stat()->WriteFile(f);
				return;
			}
			else if (fileType == LKFContextFileType::Result) {
				WriteResultFile(f);
				return;
			}

			switch (fileType) {

				case LKFContextFileType::Error:
					count = lkf_setErrorFilter((LKF_CONTEXT *)_contextPtr, LKF_ERRORFILTER_ALLERRORS);
					if (count == LKF_OUTOFMEMORY_UINTERROR) {
						ThrowOutOfMemoryException("lkf_setErrorFilter");
					}
					lkf_getSatzartErrorHeadline((LKF_CONTEXT *)_contextPtr, LKF_FAC_ALL, 2048, line);
					f->WriteLine(gcnew String(line, 0, (int)strlen(line), System::Text::Encoding::UTF8));
					for (i = 0; i < count; i++) {
						lkf_getSatzartError((LKF_CONTEXT *)_contextPtr, i, 2048, line);
						f->WriteLine(gcnew String(line, 0, (int)strlen(line), System::Text::Encoding::UTF8));
					}
					break;
				case LKFContextFileType::Warning:
					count = lkf_setErrorFilter((LKF_CONTEXT *)_contextPtr, LKF_ERRORFILTER_ALLWARNINGS);
					if (count == LKF_OUTOFMEMORY_UINTERROR) {
						ThrowOutOfMemoryException("lkf_setErrorFilter");
					}
					lkf_getSatzartErrorHeadline((LKF_CONTEXT *)_contextPtr, LKF_FAC_ALL, 2048, line);
					f->WriteLine(gcnew String(line, 0, (int)strlen(line), System::Text::Encoding::UTF8));
					for (i = 0; i < count; i++) {
						lkf_getSatzartError((LKF_CONTEXT *)_contextPtr, i, 2048, line);
						f->WriteLine(gcnew String(line, 0, (int)strlen(line), System::Text::Encoding::UTF8));
					}
					break;

				case LKFContextFileType::Info:
					count = lkf_setErrorFilter((LKF_CONTEXT *)_contextPtr, LKF_ERRORFILTER_ALLINFOS);
					if (count == LKF_OUTOFMEMORY_UINTERROR) {
						ThrowOutOfMemoryException("lkf_setErrorFilter");
					}
					lkf_getSatzartErrorHeadline((LKF_CONTEXT *)_contextPtr, LKF_FAC_ALL, 2048, line);
					f->WriteLine(gcnew String(line, 0, (int)strlen(line), System::Text::Encoding::UTF8));
					for (i = 0; i < count; i++) {
						lkf_getSatzartError((LKF_CONTEXT *)_contextPtr, i, 2048, line);
						f->WriteLine(gcnew String(line, 0, (int)strlen(line), System::Text::Encoding::UTF8));
					}
					break;

				default:
					break;
			}
		}

		void LKFContextWithData::SetErrorFormat(unsigned int facility, array<int> ^fields) {
			if (fields == nullptr) {
				lkf_setErrorFormat((LKF_CONTEXT *)_contextPtr, facility, 0, 0);
			}
			else {
				unsigned int *fieldIndices = new unsigned int[fields->Length];
				Marshal::Copy(fields, 0, IntPtr(fieldIndices), fields->Length);
				lkf_setErrorFormat((LKF_CONTEXT *)_contextPtr, facility, fields->Length, fieldIndices);
				delete fieldIndices;
			}
		}

		unsigned int LKFContextWithData::SetErrorFilter(unsigned int filter) {
			return lkf_setErrorFilter((LKF_CONTEXT *)_contextPtr, filter);
		}

		String  ^LKFContextWithData::GetErrorHeadLine(unsigned int errorIndex) {
			char line[2048];
			lkf_getSatzartErrorHeadline((LKF_CONTEXT *)_contextPtr, LKF_FAC_ALL, 2048, line);
			return gcnew String(line, 0, (int)strlen(line), System::Text::Encoding::UTF8);
		}


		String  ^LKFContextWithData::GetError(unsigned int index) {
			char line[2048];
			lkf_getSatzartError((LKF_CONTEXT *)_contextPtr, index, 2048, line);
			return gcnew String(line, 0, (int)strlen(line), System::Text::Encoding::UTF8);
		}

		String  ^LKFContextWithData::GetErrorField(unsigned int index, unsigned int errorField) {
			char line[512];
			lkf_getSatzartErrorField ((LKF_CONTEXT *) _contextPtr, index, errorField, line, 512);
			return gcnew String(line, 0, (int)strlen(line), System::Text::Encoding::UTF8);
		}

		unsigned int LKFContextWithData::ErrorCount::get(void) {
			return lkf_context_getSatzartErrorAnz((LKF_CONTEXT *)_contextPtr, 12);
		}

		unsigned int LKFContextWithData::WarningCount::get(void) {
			return lkf_context_getSatzartErrorAnz((LKF_CONTEXT *)_contextPtr, 2);
		}

		unsigned int LKFContextWithData::AufenthaltErrorCount::get(void) {
			return lkf_context_GetErrorAnz((LKF_CONTEXT *)_contextPtr, 0, 0);
		}

		unsigned int LKFContextWithData::AufenthaltWarningCount::get(void) {
			return lkf_context_GetWarningAnz((LKF_CONTEXT *)_contextPtr, 0, 0);
		}


		unsigned int LKFContextWithData::InfoCount::get(void) {
			return lkf_context_GetHinweisAnz((LKF_CONTEXT *)_contextPtr, 0, 0);
		}

		unsigned int LKFContextWithData::AcceptedErrorCount::get(void) {
			int acceptedCount;
			lkf_context_GetErrorAnz((LKF_CONTEXT *)_contextPtr, 0, &acceptedCount);
			return (unsigned int) acceptedCount;
		}

		unsigned int LKFContextWithData::AcceptedWarningCount::get(void) {
			int acceptedCount;
			lkf_context_GetWarningAnz((LKF_CONTEXT *)_contextPtr, 0, &acceptedCount);
			return (unsigned int)acceptedCount;
		}

		unsigned int LKFContextWithData::AcceptedInfoCount::get(void) {
			int acceptedCount;
			lkf_context_GetHinweisAnz((LKF_CONTEXT *)_contextPtr, 0, &acceptedCount);
			return (unsigned int)acceptedCount;
		}

		int LKFContextWithData::GetAmbCounter(unsigned int ambIndex) {
			return lkf_get_amb_counter((LKF_CONTEXT *)_contextPtr, ambIndex);
		}

		int LKFContextWithData::LastImportCharCount::get(void) {
			return _lastImportCharCount;
		}

		int LKFContextWithData::LastImportLineCount::get(void) {
			return _lastImportLineCount;
		}

		int LKFContextWithData::LastImportErrorCount::get(void) {
			return _lastImportErrorCount;
		}

		int LKFContextWithData::GetContextValue(int contextvalue) {
			return lkf_get_contextvalue((LKF_CONTEXT *)_contextPtr, contextvalue);
		}

		double LKFContextWithData::GetContextScoringValue(unsigned int contextvalue) {
			return lkf_get_context_scorvalue((LKF_CONTEXT*)_contextPtr, 0, contextvalue);
		}

		// double GetContextScoringValue(int contextvalue);


		LKFContextForStat::LKFContextForStat() : LKFContextWithData()
		{
		}

		void LKFContextForStat::Modelljahr::set(int modelljahr) {
		}

		void LKFContextForStat::SetAbrechnungsPeriode(int jahr, int period_val, int lfdnr, LKFContextPeriode type, int flags) {
			Abrechnungsjahr = jahr;
			lkf_set_contextparameters((LKF_CONTEXT *)_contextPtr, jahr, period_val, lfdnr, (char)type, flags & ~(1 << 5));
		}

		void LKFContextForStat::Abrechnungsjahr::set(int abrechnungsjahr) {
			_abrechnungsjahr = abrechnungsjahr;
			char errorBuffer[4096];

			char* path = (char*)Marshal::StringToHGlobalAnsi(LKFController::DataPath).ToPointer();
			int res = lkf_set_statjahr((LKF_CONTEXT *)_contextPtr, abrechnungsjahr, path, errorBuffer);
			if (res) {
				Marshal::FreeHGlobal((IntPtr)path);
				LKFException ^ex = gcnew LKFException(res, gcnew String(errorBuffer));
				throw ex;
			}
			Marshal::FreeHGlobal((IntPtr)path);
			_modelljahr = lkf_context_getModellJahr((LKF_CONTEXT *)_contextPtr);
			lkf_set_abrechnungsjahr((LKF_CONTEXT *)_contextPtr, abrechnungsjahr);
		}
	}
}

