#ifndef __HLLHANDLER_H
#define __HLLHANDLER_H

#ifndef SOFTPLC7KERNEL_CLASS
	#ifdef SOFTPLC7KERNEL_BUILD_DLL
		#define SOFTPLC7KERNEL_CLASS __declspec(dllexport) 
		#define SOFTPLC7KERNEL_API __declspec(dllexport) 
		#define SOFTPLC7KERNEL_DATA __declspec(dllexport) 
	#else
		#define SOFTPLC7KERNEL_CLASS __declspec(dllimport) 
		#define SOFTPLC7KERNEL_API __declspec(dllimport)
		#define SOFTPLC7KERNEL_DATA __declspec(dllimport) 
	#endif
#endif

// Der enum BLOCK_TYPE bestimmt den Baustein-Typen innerhalb der CPU
// und stimmt in der Regel mit der Codierung bei ANYPTR überein.
// Die Peripherie, Timer und Zähler werden hier nicht unterstützt.
enum BLOCK_TYPE
{
	TYPE_NONE	= 0,// nicht definiert oder noch nicht unterstützt
	TYPE_DBDB	= 1,// Datenbaustein mit Nummer
	TYPE_E		= 0x81,// Eingang
	TYPE_A		= 0x82,// Ausgang
	TYPE_M		= 0x83,// Merker
	TYPE_DB		= 0x84,// aufgeschlagener Datenbaustein
	TYPE_DI		= 0x85,// aufgeschlagener Instanz-Datenbaustein
	TYPE_L		= 0x86,// Lokaldaten
	TYPE_VL		= 0x87,// Vorgänger-Lokaldaten
	TYPE_Z		= 0x1c,// Zähler
	TYPE_T		= 0x1d,// Timer
};
// Der enum SOURCE_WIDTH liefert gleichzeitig die Anzahl der benötigten Bytes bzw. 0 für Bitoperanden.
enum SOURCE_WIDTH
{
	WIDTH_X		= 0,// Bit
	WIDTH_B		= 1,// Byte
	WIDTH_W		= 2,// Wort
	WIDTH_DW	= 4 // Doppelwort
};

// Hll-Funktionen, die einen FC oder FB ersetzen, erhalten einen Zeiger auf einen HllHandler.
// Über diesen können die Aktualparameter ausgelesen werden und auf Operandenbereiche in der
// CPU zugegriffen werden.
class SOFTPLC7KERNEL_CLASS HllHandler
{
protected:
	void*		m_pThread;// Zeiger auf SoftPlc7Kernel-interne Struktur
public:
	// Ein HllHandler wird nur im SoftPlc7Kernel erzeugt und erhält einen Zeiger auf den aktuellen S7Thread.
	// Ist pThread == NULL, so können nur globale Daten (Zähler, Timer, DBs) geschrieben und gelesen werden,
	// nicht dagegen thread-abhängige Daten wie Register, aufgeschlagener Datenbaustein und initielle OB-Nummer.
				HllHandler(void* pThread = NULL);
	// Liefert den Zeiger auf den S7Thread zurück.
	void*		GetThread();
	// Liefert bei (S)FC-Aufruf die Parameter als HllPTR32-Array.
	// Nach Casten auf (HllPTR32*) kann auf die einzelnen Parameter zugegriffen werden.
	void*		GetFCParams();
	// Liefert bei (S)FB-Aufruf die Parameter im Instanz-DB.
	// Die Parameter müssen entsprechend dem Typ aus dem Speicher gelesen werden.
	void*		GetFBParams();

	// Liefert den Wert des gewünschten Akkus (1 bis 4 erlaubt) als DWORD oder 0.
	DWORD		GetAkkuAsDWORD(long lAkku);
	// Liefert den Wert des gewünschten Akkus (1 bis 4 erlaubt) als float oder 0.0.
	float		GetAkkuAsFloat(long lAkku);
	// Liefert den Wert des gewünschten Adressregisters (1 und 2 erlaubt) oder 0.
	DWORD		GetAR(long lAR);
	// Liefert die Nummer des aufgeschlagenen Datenbausteins oder 0.
	WORD		GetActDBNumber();
	// Liefert die Nummer des aufgeschlagenen Instanz-Datenbausteins oder 0.
	WORD		GetActDINumber();
	// Liefert die Nummer des Organisationsbausteins, von dem aus die Hll-Funktion aufgerufen wurde (oberster Callstack-Eintrag).
	WORD		GetInitialOBNumber();
	// Liefert die Nummer des Anlauf-Organisationsbausteins, der zuletzt aufgerufen wurde.
	WORD		GetStartupOBNumber();
	// Setzt den gewünschten Akku (1 bis 4 erlaubt) auf den Wert dwValue
	// und gibt an, ob die Funktion durchgeführt werden konnte.
	bool		SetAkku(long lAkku, DWORD dwValue);
	// Setzt den gewünschten Akku (1 bis 4 erlaubt) auf den Wert fltValue
	// und gibt an, ob die Funktion durchgeführt werden konnte.
	bool		SetAkku(long lAkku, float fltValue);
	// Setzt das Adressregister AR1 auf den Wert dwValue.
	void		SetAR1(DWORD dwValue);
	// Schlägt den angegebenen Datenbausteins auf
	// und gibt an, ob die Funktion durchgeführt werden konnte.
	bool		SetActDBNumber(WORD wNumber);
	// Schlägt den angegebenen Instanz-Datenbausteins auf
	// und gibt an, ob die Funktion durchgeführt werden konnte.
	bool		SetActDINumber(WORD wNumber);

	// Liefert die Anzahl der Timer innerhalb der CPU.
	WORD		GetTimerCount();
	// Liefert die Anzahl der Zähler innerhalb der CPU.
	WORD		GetCounterCount();
	// Liefert die Anzahl der Merker innerhalb der CPU.
	WORD		GetFlagCount();
	// Liefert die Anzahl der Peripherie-Eingangs-Bytes innerhalb der CPU.
	WORD		GetPEByteCount();
	// Liefert die Anzahl der Peripherie-Ausgangs-Bytes innerhalb der CPU.
	WORD		GetPAByteCount();
	// Liefert die Anzahl der remanenten Timer innerhalb der CPU.
	WORD		GetRemanentTimerCount();
	// Liefert die Anzahl der remanenten Zähler innerhalb der CPU.
	WORD		GetRemanentCounterCount();
	// Liefert die Anzahl der remanenten Merker innerhalb der CPU.
	WORD		GetRemanentFlagCount();

	// Liefert den Wert des Timers mit der Nummer wTimer.
	// Entspricht dem AWL-Befehl "L T<number>"
	WORD		GetTimerValue(WORD wTimer);
	// Liefert den Wert des Timers BCD-kodiert und dem Code für die Zeiteinheit.
	// Entspricht dem AWL-Befehl "LC T<number>"
	WORD		GetTimerValueUnitBCD(WORD wTimer);
	// Liefert den Wert des Zählers mit der Nummer wCounter.
	WORD		GetCounterValue(WORD wCounter);

	// Setzt den Wert des Timers mit der Nummer wTimer auf den Wert wValue
	// und gibt an, ob die Funktion durchgeführt werden konnte.
	// Der Wert wValue ist dabei BCD-kodiert und enthält auch die Zeiteinheit.
	bool		SetTimerValue(WORD wTimer, WORD wValue);
	// Setzt den Wert des Zählers mit der Nummer wCounter auf den Wert wValue
	// und gibt an, ob die Funktion durchgeführt werden konnte.
	bool		SetCounterValue(WORD wCounter, WORD wValue);

	// Kopiert maximal wLen Bytes aus dem Quell-Baustein ab Adresse wAddr in den durch pbDest bestimmten Zielpuffer.
	// Gibt an, wieviele Bytes kopiert werden konnten. Es werden weniger als wLen Bytes kopiert,
	// wenn der angegebene Bereich nicht vollständig im Datenbaustein liegt.
	WORD		GetBytes
	(
		BLOCK_TYPE eType,// Typ des Quell-Bausteins
		WORD wAddr,// Anfangsadresse im Quell-Baustein
		BYTE* pbDest,// Zieladresse
		WORD wLen// Anzahl der zu kopierenden Bytes
	);
	// Kopiert maximal wLen Bytes aus dem durch pbSrc bestimmten Quellpuffer in den Ziel-Baustein ab Adresse wAddr.
	// Gibt an, wieviele Bytes kopiert werden konnten. Es werden weniger als wLen Bytes kopiert,
	// wenn der angegebene Bereich nicht vollständig im Datenbaustein liegt.
	WORD		SetBytes
	(
		BLOCK_TYPE eType,// Typ des Ziel-Bausteins
		WORD wAddr,// Anfangsadresse im Ziel-Baustein
		BYTE* pbSrc,// Quelladresse
		WORD wLen// Anzahl der zu kopierenden Bytes
	);

	// Kopiert maximal wLen Bytes aus dem Quell-Datenbaustein mit der Nummer wDBNumber in den durch pbDest bestimmten Zielpuffer.
	// Gibt an, wieviele Bytes kopiert werden konnten. Es werden weniger als wLen Bytes kopiert,
	// wenn der angegebene Bereich nicht vollständig im Datenbaustein liegt.
	WORD		GetDBBytes
	(
		WORD wDBNumber,// Nummer des Datenbausteins
		WORD wAddr,// Anfangsadresse im Datenbaustein
		BYTE* pbDest,// Zieladresse
		WORD wLen// Anzahl der zu kopierenden Bytes
	);
	// Kopiert maximal wLen Bytes aus dem durch pbSrc bestimmten Quellpuffer in den Ziel-Datenbaustein ab Adresse wAddr.
	// Gibt an, wieviele Bytes kopiert werden konnten. Es werden weniger als wLen Bytes kopiert,
	// wenn der angegebene Bereich nicht vollständig im Datenbaustein liegt.
	WORD		SetDBBytes
	(
		WORD	wDBNumber,// Nummer des Datenbausteins
		WORD wAddr,// Anfangsadresse im Datenbaustein
		BYTE* pbSrc,// Quelladresse
		WORD wLen// Anzahl der zu kopierenden Bytes
	);
	// Gibt an, ob die CPU schon initialisiert ist.
	bool		CPUIsInitialized();
	// Gibt an, ob die CPU gerade im FREEZE-Zustand ist.
	bool		CPUIsFrozen();
	// Gibt an, ob die CPU sich beendet.
	bool		CPUIsTerminating();
	// Gibt an, ob die CPU ein Programm ausführt.
	bool		CPUIsRunning();
};

// Die Daten eines HllANYPTR bestehen genau aus den 10 Bytes eines STEP7-ANYPTRs. 
// Die Peripherie, Timer und Zähler werden hier nicht unterstützt.
// Es werden Funktionen zur Verfügung gestellt, die die einzelnen Teilinformationen zurückgeben
// und solche, die bei richtiger Anwendung die Zieldaten liefern.
class SOFTPLC7KERNEL_CLASS HllANYPTR
{
protected:
	BYTE			m_bData[10];// 10 Bytes STEP7-ANYPTR
public:
	// Liefert den Bausteintypen.
	BLOCK_TYPE		GetBlockType();
	// Liefert die Breite des Quell-Datentypen.
	SOURCE_WIDTH	GetSourceWidth();
	// Liefert die Nummer des Datenbausteins, wenn ein solcher angegeben ist.
	WORD			GetBlockNumber();
	// Liefert die Anzahl der Wiederholungen, wenn ein Array angegeben ist
	WORD			GetRepetition();
	// Extrahiert die Adresse und setzt die Variablen lAddr und lBit.
	// Die Adresse ist dann "lAddr.lBit".
	void			GetPointer(long & lAddr, long & lBit);
	// Extrahiert die letzten 2 Bytes.
	// Bei Blöcken wird <Adresse>*8 + <Bit> zurückgegeben.
	// Bei Zählern und Timern wird die Nummer zurückgegeben.
	WORD			GetAddress();
	// Liefert ein Bit aus dem Ziel-Operanden.
	bool			GetSourceBit(HllHandler* pHandler);
	// Liefert ein Byte aus dem Ziel-Operanden.
	BYTE			GetSourceByte(HllHandler* pHandler);
	// Liefert ein Wort aus dem Ziel-Operanden.
	// Steht im Speicher aufsteigend 0x12, 0x34, so wird 0x1234 zurückgegeben.
	WORD			GetSourceWORD(HllHandler* pHandler);
	// Liefert ein Doppelwort aus dem Ziel-Operanden.
	// Steht im Speicher austeigend 0x12, 0x34, 0x56, 0x78, so wird 0x12345678 zurückgegeben.
	DWORD			GetSourceDWORD(HllHandler* pHandler);

	// Setzt ein Bit im Ziel-Baustein.
	void			SetSourceBit(HllHandler* pHandler, bool bBit);
	// Setzt ein Byte im Ziel-Baustein.
	void			SetSourceByte(HllHandler* pHandler, BYTE byte);
	// Setzt ein Wort im Ziel-Baustein.
	// Wenn der Parameter w == 0x1234 ist, so werden aufsteigend die Byte-Werte 0x12, 0x34 geschrieben.
	void			SetSourceWORD(HllHandler* pHandler, WORD w);
	// Setzt ein Doppelwort im Ziel-Baustein.
	// Wenn der Parameter dw == 0x12345678 ist, so werden aufsteigend die Byte-Werte 0x12, 0x34, 0x56, 0x78 geschrieben.
	void			SetSourceDWORD(HllHandler* pHandler, DWORD dw);
};

// Die Daten eines HllPTR32 repräsentieren genau die 4 Bytes, über die ein bereichsübergreifender Zeiger definiert wird.
// Es werden Funktionen zur Verfügung gestellt, die die einzelnen Teilinformationen zurückgeben
// und solche, die bei richtiger Anwendung die Zieldaten liefern.
class SOFTPLC7KERNEL_CLASS HllPTR32
{
protected:
	DWORD		m_dwData;// 4 Bytes bereichsübergreifender Zeiger
public:
	// Konstruiert einen noch ungültigen bereichsübergreifenden Zeiger
				HllPTR32();
	// Konstruiert einen bereichsübergreifenden Zeiger, der durch dw bestimmt ist. Es wird dw nach m_dwData kopiert.
	// D.h. wenn der Operand "M 0.4" kodiert wird, muss dw == 0x04000083 sein.
	// Im Speicher steht dann aufsteigend 0x83, 0x00, 0x00, 0x04, was der Codierung von "M 0.4" entspricht.
				HllPTR32(const DWORD & dw);
	// Konstruiert einen bereichsübergreifenden Zeiger, der durch den Typ eType, und den Pointer "lAddr.lBit" bestimmt ist.
				HllPTR32(BLOCK_TYPE eType, long lAddr, long lBit);

	// Liefert den Typ des Ziel-Bausteins.
	BLOCK_TYPE	GetBlockType();
	// Extrahiert den bereichsinternen Zeiger und setzt die Variablen lAddr und lBit.
	void		GetPointer(long & lAddr, long & lBit);

	// Liefert ein Bit aus dem Ziel-Baustein.
	bool		GetSourceBit(HllHandler* pHandler);
	// Liefert ein Byte aus dem Ziel-Baustein.
	BYTE		GetSourceByte(HllHandler* pHandler);
	// Liefert ein Wort aus dem Ziel-Baustein.
	// Wenn im Speicher aufsteigend die Byte-Werte 0x12, 0x34 stehen, so wird 0x1234 zurückgegeben.
	WORD		GetSourceWORD(HllHandler* pHandler);
	// Liefert ein Doppelwort aus dem Ziel-Baustein.
	// Wenn im Speicher aufsteigend die Byte-Werte 0x12, 0x34, 0x56, 0x78 stehen, so wird 0x12345678 zurückgegeben.
	DWORD		GetSourceDWORD(HllHandler* pHandler);
	// Liefert wCount Bytes aus dem Ziel-Baustein und gibt an, ob die Funktion ausgeführt werden konnte.
	bool		GetSourceBytes(HllHandler* pHandler, BYTE* pbDest, WORD wCount);
	// Versucht, einen zusammengesetzten Datentyp mit Länge wLen nach pbDest zu kopieren und gibt an, ob die Funktion ohne Fehler ausgeführt werden konnte.
	bool		GetSourceSTRUCT(HllHandler* pHandler, BYTE* pbDest, WORD wLen);
	// Kopiert 10 Bytes aus dem Ziel-Baustein als HllANYPTR und gibt an, ob die Funktion ausgeführt werden konnte.
	bool		GetSourceHllANYPTR(HllHandler* pHandler, HllANYPTR* pAny);


	// Setzt ein Bit im Ziel-Baustein.
	void		SetSourceBit(HllHandler* pHandler, bool bBit);
	// Setzt ein Byte im Ziel-Baustein.
	void		SetSourceByte(HllHandler* pHandler, BYTE byte);
	// Setzt ein Wort im Ziel-Baustein.
	// Wenn der Parameter w == 0x1234 ist, so werden aufsteigend die Byte-Werte 0x12, 0x34 geschrieben.
	void		SetSourceWORD(HllHandler* pHandler, WORD w);
	// Kopiert wCount Bytes in den Ziel-Baustein und gibt an, ob die Funktion ausgeführt werden konnte.
	bool		SetSourceBytes(HllHandler* pHandler, BYTE* pbSrc, WORD wCount);
	// Setzt ein Doppelwort im Ziel-Baustein.
	// Wenn der Parameter dw == 0x12345678 ist, so werden aufsteigend die Byte-Werte 0x12, 0x34, 0x56, 0x78 geschrieben.
	void		SetSourceDWORD(HllHandler* pHandler, DWORD dw);
	// Versucht, von pbSrc in einen zusammengesetzten Datentyp der Länge wLen zu kopieren und gibt an, ob die Funktion ohne Fehler ausgeführt werden konnte.
	bool		SetSourceSTRUCT(HllHandler* pHandler, BYTE* pbSrc, WORD wLen);
};

// Diese Klasse kapselt alle Funktionalitäten, die nötig sind, die SoftPlc7 um eine Plugin-Schnittstelle zu erweitern.
// Man kann zwei Funktionen angeben. Die eine wird am Anfang einer jeden Zeitscheibe ausgeführt, also noch bevor OB-Zyklen erneut anlaufen.
// Die zweite Funktion wird ausgeführt, nachdem innerhalb der Zeitscheibe alle OB-Zyklen abgearbeitet wurden.
class SOFTPLC7KERNEL_CLASS PluginHandler
{
protected:
	HANDLE		m_hStart;
	HANDLE		m_hReady;
	HANDLE		m_hContinue;
	HANDLE		m_hThread;
	void		(*m_pFunc1)();
	void		(*m_pFunc2)();
	static void	ThreadFunction(PluginHandler* pHandler);
public:
	// Instanziiert einen PluginHandler und erzeugt einen Thread. Benötigt werden:
	// 1. Zwei Events, die im SoftPlc7Kernel erzeugt und an die Plugin-Funktion übergeben werden.
	// 2. Eine Funktion, die am Anfang einer Zeitscheibe ausgeführt werden soll, also bevor OBs gestartet werden.
	// 3. Eine Funktion, die innerhalb einer Zeitscheibe ausgeführt werden soll, nachdem alle OB-Zyklen abgearbeitet sind.
	// Beide Funktionszeiger dürfen auch NULL sein, dann wird an der entsprechenden Stelle keine Funktion ausgeführt.
				PluginHandler(HANDLE hReady, HANDLE hContinue, void (*pFunc1)(), void (*pFunc2)());
	// Zerstört den PluginHandler und beendet den Thread.
	virtual		~PluginHandler();
	// Setzt intern ein Event, sodass der erzeugte Thread den Anfang einer Zeitscheibe erkennt.
	void		Start();
};

// Diese Klasse beschreibt den Datentyp DATE_AND_TIME.
// Es werden Funktionen zur Verfügung gestellt, die die einzelnen Teilinformationen zurückgeben
// und solche, die bei richtiger Anwendung die Zieldaten liefern.
class SOFTPLC7KERNEL_CLASS HllDATE_AND_TIME
{
public:
	BYTE	bYear;		// Jahr, BCD-kodiert
	BYTE	bMonth;		// Monat, BCD-kodiert
	BYTE	bDay;		// Tag, BCD-kodiert
	BYTE	bHour;		// Stunde, BCD-kodiert
	BYTE	bMinute;	// Minute, BCD-kodiert
	BYTE	bSecond;	// Sekunde, BCD-kodiert
	BYTE	bMSD;		// Millisekunden (höchste 2 Nibble aus BCD-Kodierung)
	BYTE	bLSDandDayOfWeek;	// Millisekunden (unterstes Nibble aus BCD-Kodierung, im oberen Nibble) und Wochentag (Sonntag=1, unteres Nibble)
public:
	// Liefert die Millisekunden, Wertebereich 0..999.
	WORD	GetMillisecond();
	// Setzt die Millisekunden, Wertebereich 0..999.
	void	SetMillisecond(WORD wMillisecond);
	// Liefert die Codierung für den Wochentag (Sonntag=1, Montag=2, ...).
	BYTE	GetDayOfWeek();
	// Setzt den Code für den Wochentag (Sonntag=1, Montag=2, ...).
	void	SetDayOfWeek(BYTE bDayOfWeek);
	// Konvertiert nach SYSTEMTIME.
	SYSTEMTIME	ToSYSTEMTIME();
	// Übernimmt den Wert aus einer SYSTEMTIME.
	void	FromSYSTEMTIME(SYSTEMTIME st);
	// Liefert die Millisekunden seit Mitternacht.
	DWORD	GetMillisecondsOfDay();
};

// Liefert das Wort, das ab der Adresse pbSrc im S7-Format steht.
WORD	SOFTPLC7KERNEL_API	GetSourceWORD(BYTE* pbSrc);
// Liefert das Doppelwort, das ab der Adresse pbSrc im S7-Format steht.
DWORD	SOFTPLC7KERNEL_API	GetSourceDWORD(BYTE* pbSrc);
// Liefert den float-Wert, der ab der Adresse pbSrc im S7-Format steht.
float	SOFTPLC7KERNEL_API	GetSourceFloat(BYTE* pbSrc);

// Schreibt ab der Adresse pbDest das Wort w im S7-Format.
void	SOFTPLC7KERNEL_API	SetSourceWORD(BYTE* pbDest, WORD w);
// Schreibt ab der Adresse pbDest das Doppelwort dw im S7-Format.
void	SOFTPLC7KERNEL_API	SetSourceDWORD(BYTE* pbDest, DWORD dw);
// Schreibt ab der Adresse pbDest den float-Wert flt im S7-Format.
void	SOFTPLC7KERNEL_API	SetSourceFloat(BYTE* pbDest, float flt);

// Liefert die S7-Zeit (entsprechend SFC 1).
void	SOFTPLC7KERNEL_API	ReadClock(HllDATE_AND_TIME & oDT);
// Setzt die S7-Zeit (entsprechend SFC 0)
// und gibt an, ob die Funktion durchgeführt werden konnte.
bool	SOFTPLC7KERNEL_API	SetClock(HllDATE_AND_TIME & oDT);

#endif
