'''++

Copyright (C) 2019 Calculator developers

All rights reserved.

This file has been generated by the Automatic Component Toolkit (ACT) version 1.6.0.

Abstract: This is an autogenerated Python file in order to allow an easy
 use of Calculator library

Interface version: 1.0.0

'''


import ctypes
import platform
import enum
import os

name = "calculator"

'''Definition of domain specific exception
'''
class ECalculatorException(Exception):
	def __init__(self, code, message = ''):
		self._code = code
		self._message = message
	
	def __str__(self):
		if self._message:
			return 'CalculatorException ' + str(self._code) + ': '+ str(self._message)
		return 'CalculatorException ' + str(self._code)

'''Definition of binding API version
'''
class BindingVersion(enum.IntEnum):
	MAJOR = 1
	MINOR = 0
	MICRO = 0

'''Definition Error Codes
'''
class ErrorCodes(enum.IntEnum):
	SUCCESS = 0
	NOTIMPLEMENTED = 1
	INVALIDPARAM = 2
	INVALIDCAST = 3
	BUFFERTOOSMALL = 4
	GENERICEXCEPTION = 5
	COULDNOTLOADLIBRARY = 6
	COULDNOTFINDLIBRARYEXPORT = 7
	INCOMPATIBLEBINARYVERSION = 8

'''Definition of Function Table
'''
class FunctionTable:
	calculator_getversion = None
	calculator_getlasterror = None
	calculator_releaseinstance = None
	calculator_acquireinstance = None
	calculator_createvariable = None
	calculator_createcalculator = None
	calculator_variable_getvalue = None
	calculator_variable_setvalue = None
	calculator_calculator_enlistvariable = None
	calculator_calculator_getenlistedvariable = None
	calculator_calculator_clearvariables = None
	calculator_calculator_multiply = None
	calculator_calculator_add = None


'''Wrapper Class Implementation
'''
class Wrapper:

	def __init__(self, libraryName = None, symbolLookupMethodAddress = None):
		ending = ''
		if platform.system() == 'Windows':
			ending = 'dll'
		elif platform.system() == 'Linux':
			ending = 'so'
		elif platform.system() == 'Darwin':
			ending = 'dylib'
		else:
			raise ECalculatorException(ErrorCodes.COULDNOTLOADLIBRARY)
		
		if (not libraryName) and (not symbolLookupMethodAddress):
			libraryName = os.path.join(os.path.dirname(os.path.realpath(__file__)),'calculator')
		
		if libraryName is not None:
			path = libraryName + '.' + ending
			try:
				self.lib = ctypes.CDLL(path)
			except Exception as e:
				raise ECalculatorException(ErrorCodes.COULDNOTLOADLIBRARY, str(e) + '| "'+path + '"' )
			
			self._loadFunctionTable()
		elif symbolLookupMethodAddress is not None:
				self.lib = FunctionTable()
				self._loadFunctionTableFromMethod(symbolLookupMethodAddress)
		else:
			raise ECalculatorException(ErrorCodes.COULDNOTLOADLIBRARY, str(e))
		
		self._checkBinaryVersion()
	
	def _loadFunctionTableFromMethod(self, symbolLookupMethodAddress):
		try:
			symbolLookupMethodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_char_p, ctypes.POINTER(ctypes.c_void_p))
			symbolLookupMethod = symbolLookupMethodType(int(symbolLookupMethodAddress))
			
			methodAddress = ctypes.c_void_p()
			
			err = symbolLookupMethod(ctypes.c_char_p(str.encode("calculator_getversion")), methodAddress)
			if err != 0:
				raise ECalculatorException(ErrorCodes.COULDNOTLOADLIBRARY, str(err))
			methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32))
			self.lib.calculator_getversion = methodType(int(methodAddress.value))
			
			err = symbolLookupMethod(ctypes.c_char_p(str.encode("calculator_getlasterror")), methodAddress)
			if err != 0:
				raise ECalculatorException(ErrorCodes.COULDNOTLOADLIBRARY, str(err))
			methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.c_uint64, ctypes.POINTER(ctypes.c_uint64), ctypes.c_char_p, ctypes.POINTER(ctypes.c_bool))
			self.lib.calculator_getlasterror = methodType(int(methodAddress.value))
			
			err = symbolLookupMethod(ctypes.c_char_p(str.encode("calculator_releaseinstance")), methodAddress)
			if err != 0:
				raise ECalculatorException(ErrorCodes.COULDNOTLOADLIBRARY, str(err))
			methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p)
			self.lib.calculator_releaseinstance = methodType(int(methodAddress.value))
			
			err = symbolLookupMethod(ctypes.c_char_p(str.encode("calculator_acquireinstance")), methodAddress)
			if err != 0:
				raise ECalculatorException(ErrorCodes.COULDNOTLOADLIBRARY, str(err))
			methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p)
			self.lib.calculator_acquireinstance = methodType(int(methodAddress.value))
			
			err = symbolLookupMethod(ctypes.c_char_p(str.encode("calculator_createvariable")), methodAddress)
			if err != 0:
				raise ECalculatorException(ErrorCodes.COULDNOTLOADLIBRARY, str(err))
			methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_double, ctypes.POINTER(ctypes.c_void_p))
			self.lib.calculator_createvariable = methodType(int(methodAddress.value))
			
			err = symbolLookupMethod(ctypes.c_char_p(str.encode("calculator_createcalculator")), methodAddress)
			if err != 0:
				raise ECalculatorException(ErrorCodes.COULDNOTLOADLIBRARY, str(err))
			methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.POINTER(ctypes.c_void_p))
			self.lib.calculator_createcalculator = methodType(int(methodAddress.value))
			
			err = symbolLookupMethod(ctypes.c_char_p(str.encode("calculator_variable_getvalue")), methodAddress)
			if err != 0:
				raise ECalculatorException(ErrorCodes.COULDNOTLOADLIBRARY, str(err))
			methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.POINTER(ctypes.c_double))
			self.lib.calculator_variable_getvalue = methodType(int(methodAddress.value))
			
			err = symbolLookupMethod(ctypes.c_char_p(str.encode("calculator_variable_setvalue")), methodAddress)
			if err != 0:
				raise ECalculatorException(ErrorCodes.COULDNOTLOADLIBRARY, str(err))
			methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.c_double)
			self.lib.calculator_variable_setvalue = methodType(int(methodAddress.value))
			
			err = symbolLookupMethod(ctypes.c_char_p(str.encode("calculator_calculator_enlistvariable")), methodAddress)
			if err != 0:
				raise ECalculatorException(ErrorCodes.COULDNOTLOADLIBRARY, str(err))
			methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.c_void_p)
			self.lib.calculator_calculator_enlistvariable = methodType(int(methodAddress.value))
			
			err = symbolLookupMethod(ctypes.c_char_p(str.encode("calculator_calculator_getenlistedvariable")), methodAddress)
			if err != 0:
				raise ECalculatorException(ErrorCodes.COULDNOTLOADLIBRARY, str(err))
			methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.c_uint32, ctypes.POINTER(ctypes.c_void_p))
			self.lib.calculator_calculator_getenlistedvariable = methodType(int(methodAddress.value))
			
			err = symbolLookupMethod(ctypes.c_char_p(str.encode("calculator_calculator_clearvariables")), methodAddress)
			if err != 0:
				raise ECalculatorException(ErrorCodes.COULDNOTLOADLIBRARY, str(err))
			methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p)
			self.lib.calculator_calculator_clearvariables = methodType(int(methodAddress.value))
			
			err = symbolLookupMethod(ctypes.c_char_p(str.encode("calculator_calculator_multiply")), methodAddress)
			if err != 0:
				raise ECalculatorException(ErrorCodes.COULDNOTLOADLIBRARY, str(err))
			methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p))
			self.lib.calculator_calculator_multiply = methodType(int(methodAddress.value))
			
			err = symbolLookupMethod(ctypes.c_char_p(str.encode("calculator_calculator_add")), methodAddress)
			if err != 0:
				raise ECalculatorException(ErrorCodes.COULDNOTLOADLIBRARY, str(err))
			methodType = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p))
			self.lib.calculator_calculator_add = methodType(int(methodAddress.value))
			
		except AttributeError as ae:
			raise ECalculatorException(ErrorCodes.COULDNOTFINDLIBRARYEXPORT, ae.args[0])
		
	def _loadFunctionTable(self):
		try:
			self.lib.calculator_getversion.restype = ctypes.c_int32
			self.lib.calculator_getversion.argtypes = [ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32)]
			
			self.lib.calculator_getlasterror.restype = ctypes.c_int32
			self.lib.calculator_getlasterror.argtypes = [ctypes.c_void_p, ctypes.c_uint64, ctypes.POINTER(ctypes.c_uint64), ctypes.c_char_p, ctypes.POINTER(ctypes.c_bool)]
			
			self.lib.calculator_releaseinstance.restype = ctypes.c_int32
			self.lib.calculator_releaseinstance.argtypes = [ctypes.c_void_p]
			
			self.lib.calculator_acquireinstance.restype = ctypes.c_int32
			self.lib.calculator_acquireinstance.argtypes = [ctypes.c_void_p]
			
			self.lib.calculator_createvariable.restype = ctypes.c_int32
			self.lib.calculator_createvariable.argtypes = [ctypes.c_double, ctypes.POINTER(ctypes.c_void_p)]
			
			self.lib.calculator_createcalculator.restype = ctypes.c_int32
			self.lib.calculator_createcalculator.argtypes = [ctypes.POINTER(ctypes.c_void_p)]
			
			self.lib.calculator_variable_getvalue.restype = ctypes.c_int32
			self.lib.calculator_variable_getvalue.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_double)]
			
			self.lib.calculator_variable_setvalue.restype = ctypes.c_int32
			self.lib.calculator_variable_setvalue.argtypes = [ctypes.c_void_p, ctypes.c_double]
			
			self.lib.calculator_calculator_enlistvariable.restype = ctypes.c_int32
			self.lib.calculator_calculator_enlistvariable.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
			
			self.lib.calculator_calculator_getenlistedvariable.restype = ctypes.c_int32
			self.lib.calculator_calculator_getenlistedvariable.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.POINTER(ctypes.c_void_p)]
			
			self.lib.calculator_calculator_clearvariables.restype = ctypes.c_int32
			self.lib.calculator_calculator_clearvariables.argtypes = [ctypes.c_void_p]
			
			self.lib.calculator_calculator_multiply.restype = ctypes.c_int32
			self.lib.calculator_calculator_multiply.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p)]
			
			self.lib.calculator_calculator_add.restype = ctypes.c_int32
			self.lib.calculator_calculator_add.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p)]
			
		except AttributeError as ae:
			raise ECalculatorException(ErrorCodes.COULDNOTFINDLIBRARYEXPORT, ae.args[0])
	
	def _checkBinaryVersion(self):
		nMajor, nMinor, _ = self.GetVersion()
		if (nMajor != BindingVersion.MAJOR) or (nMinor < BindingVersion.MINOR):
			raise ECalculatorException(ErrorCodes.INCOMPATIBLEBINARYVERSION)
	
	def checkError(self, instance, errorCode):
		if errorCode != ErrorCodes.SUCCESS.value:
			if instance:
				if instance._wrapper != self:
					raise ECalculatorException(ErrorCodes.INVALIDCAST, 'invalid wrapper call')
			message,_ = self.GetLastError(instance)
			raise ECalculatorException(errorCode, message)
	
	def GetVersion(self):
		pMajor = ctypes.c_uint32()
		pMinor = ctypes.c_uint32()
		pMicro = ctypes.c_uint32()
		self.checkError(None, self.lib.calculator_getversion(pMajor, pMinor, pMicro))
		
		return pMajor.value, pMinor.value, pMicro.value
	
	def GetLastError(self, InstanceObject):
		InstanceHandle = None
		if InstanceObject:
			InstanceHandle = InstanceObject._handle
		else:
			raise ECalculatorException(ErrorCodes.INVALIDPARAM, 'Invalid return/output value')
		nErrorMessageBufferSize = ctypes.c_uint64(0)
		nErrorMessageNeededChars = ctypes.c_uint64(0)
		pErrorMessageBuffer = ctypes.c_char_p(None)
		pHasError = ctypes.c_bool()
		self.checkError(None, self.lib.calculator_getlasterror(InstanceHandle, nErrorMessageBufferSize, nErrorMessageNeededChars, pErrorMessageBuffer, pHasError))
		nErrorMessageBufferSize = ctypes.c_uint64(nErrorMessageNeededChars.value)
		pErrorMessageBuffer = (ctypes.c_char * (nErrorMessageNeededChars.value))()
		self.checkError(None, self.lib.calculator_getlasterror(InstanceHandle, nErrorMessageBufferSize, nErrorMessageNeededChars, pErrorMessageBuffer, pHasError))
		
		return pErrorMessageBuffer.value.decode(), pHasError.value
	
	def ReleaseInstance(self, InstanceObject):
		InstanceHandle = None
		if InstanceObject:
			InstanceHandle = InstanceObject._handle
		else:
			raise ECalculatorException(ErrorCodes.INVALIDPARAM, 'Invalid return/output value')
		self.checkError(None, self.lib.calculator_releaseinstance(InstanceHandle))
		
	
	def AcquireInstance(self, InstanceObject):
		InstanceHandle = None
		if InstanceObject:
			InstanceHandle = InstanceObject._handle
		else:
			raise ECalculatorException(ErrorCodes.INVALIDPARAM, 'Invalid return/output value')
		self.checkError(None, self.lib.calculator_acquireinstance(InstanceHandle))
		
	
	def CreateVariable(self, InitialValue):
		dInitialValue = ctypes.c_double(InitialValue)
		InstanceHandle = ctypes.c_void_p()
		self.checkError(None, self.lib.calculator_createvariable(dInitialValue, InstanceHandle))
		if InstanceHandle:
			InstanceObject = Variable(InstanceHandle, self)
		else:
			raise ECalculatorException(ErrorCodes.INVALIDCAST, 'Invalid return/output value')
		
		return InstanceObject
	
	def CreateCalculator(self):
		InstanceHandle = ctypes.c_void_p()
		self.checkError(None, self.lib.calculator_createcalculator(InstanceHandle))
		if InstanceHandle:
			InstanceObject = Calculator(InstanceHandle, self)
		else:
			raise ECalculatorException(ErrorCodes.INVALIDCAST, 'Invalid return/output value')
		
		return InstanceObject
	


''' Class Implementation for Base
'''
class Base:
	def __init__(self, handle, wrapper):
		if not handle or not wrapper:
			raise ECalculatorException(ErrorCodes.INVALIDPARAM)
		self._handle = handle
		self._wrapper = wrapper
	
	def __del__(self):
		self._wrapper.ReleaseInstance(self)


''' Class Implementation for Variable
'''
class Variable(Base):
	def __init__(self, handle, wrapper):
		Base.__init__(self, handle, wrapper)
	def GetValue(self):
		pValue = ctypes.c_double()
		self._wrapper.checkError(self, self._wrapper.lib.calculator_variable_getvalue(self._handle, pValue))
		
		return pValue.value
	
	def SetValue(self, Value):
		dValue = ctypes.c_double(Value)
		self._wrapper.checkError(self, self._wrapper.lib.calculator_variable_setvalue(self._handle, dValue))
		
	


''' Class Implementation for Calculator
'''
class Calculator(Base):
	def __init__(self, handle, wrapper):
		Base.__init__(self, handle, wrapper)
	def EnlistVariable(self, VariableObject):
		VariableHandle = None
		if VariableObject:
			VariableHandle = VariableObject._handle
		else:
			raise ECalculatorException(ErrorCodes.INVALIDPARAM, 'Invalid return/output value')
		self._wrapper.checkError(self, self._wrapper.lib.calculator_calculator_enlistvariable(self._handle, VariableHandle))
		
	
	def GetEnlistedVariable(self, Index):
		nIndex = ctypes.c_uint32(Index)
		VariableHandle = ctypes.c_void_p()
		self._wrapper.checkError(self, self._wrapper.lib.calculator_calculator_getenlistedvariable(self._handle, nIndex, VariableHandle))
		if VariableHandle:
			VariableObject = Variable(VariableHandle, self._wrapper)
		else:
			raise ECalculatorException(ErrorCodes.INVALIDCAST, 'Invalid return/output value')
		
		return VariableObject
	
	def ClearVariables(self):
		self._wrapper.checkError(self, self._wrapper.lib.calculator_calculator_clearvariables(self._handle))
		
	
	def Multiply(self):
		InstanceHandle = ctypes.c_void_p()
		self._wrapper.checkError(self, self._wrapper.lib.calculator_calculator_multiply(self._handle, InstanceHandle))
		if InstanceHandle:
			InstanceObject = Variable(InstanceHandle, self._wrapper)
		else:
			raise ECalculatorException(ErrorCodes.INVALIDCAST, 'Invalid return/output value')
		
		return InstanceObject
	
	def Add(self):
		InstanceHandle = ctypes.c_void_p()
		self._wrapper.checkError(self, self._wrapper.lib.calculator_calculator_add(self._handle, InstanceHandle))
		if InstanceHandle:
			InstanceObject = Variable(InstanceHandle, self._wrapper)
		else:
			raise ECalculatorException(ErrorCodes.INVALIDCAST, 'Invalid return/output value')
		
		return InstanceObject
	
		
