/*
 * Author: Pawel Jablonski
 * E-mail: pj@xirx.net
 * WWW: xirx.net
 * GIT: git.xirx.net
 *
 * License: You can use this code however you like
 * but leave information about the original author.
 * Code is free for non-commercial and commercial use.
 */

#ifndef IO_H
#define IO_H

#include <QObject>
#include <QList>
#include <QVector>
#include <QDateTime>
#include <QThread>
#include <QCoreApplication>

#include "keyboard.h"
#include "led.h"
#include "lcd.h"
#include "rs232.h"
#include "rtc.h"
#include "speaker.h"
#include "fs.h"

//! This class contains IO functions. It is a motherboard simulation part for the CPU.
class IO : public QObject
{
	Q_OBJECT

	public:
		static const unsigned char IN_DATA_MASK = 0x0f; //!< Input data mask for the data part of the BUS
		static const unsigned char IN_CONTROL_MASK = 0xf0; //!< Input data mask for the control bits part of the BUS

		static const unsigned char IN_CLOCK_BIT = (1 << 4); //!< Input clock control bit
		static const unsigned char IN_KEYBOARD_READY_BIT = (1 << 5); //!< Input "keyboard has data to read" control bit
		static const unsigned char IN_RS232_READY_BIT = (1 << 6); //!< Input "RS232 has data to read" control bit
		static const unsigned char IN_OPERATION_COMPLETE_BIT = (1 << 7); //!< Input operation completed control bit

		static const unsigned char OUT_DATA_MASK = 0x0f; //!< Output data mask for the data part of the BUS
		static const unsigned char OUT_CONTROL_MASK = 0xf0; //!< Output data mask for the control bits part of the BUS

		static const unsigned char OUT_HALF_BIT = (1 << 4); //!< Output "half selector of the byte" control bit
		static const unsigned char OUT_MODE_BIT = (1 << 5); //!< Output "register selection or data transision mode" control bit
		static const unsigned char OUT_RW_BIT = (1 << 6); //!< Output read/write control bit
		static const unsigned char OUT_DATA_READY_BIT = (1 << 7); //!< Input "data is ready to read from output" control bit

		static const unsigned char REG_SELECT_MASK = 0x0f; //!< Register selection mask

		static const unsigned char HALF_UP_DATA_MASK = 0xf0; //!< Upper half of the byte mask
		static const int HALF_DATA_OFFSET = 4; //!< Upper half of the byte offset

		IO(QObject *parent = nullptr);
		~IO() override;

		IO(const IO &) = delete;
		IO &operator=(const IO &) = delete;
		IO(IO &&) = delete;
		IO &operator=(IO &&) = delete;

		void reset();

		void keyboardKeyPress(int key, Qt::KeyboardModifiers modifiers);
		void ledSetRun(bool enable);
		void ledSetError(bool enable);
		void rs232Receive(const QString &text);
		void rtcSetDateTime(const QDateTime &dateTime);
		void speakerSetVolume(unsigned int volume);
		void fsSetPath(const QString &path);

	private:
		//!< List of motherboard's virtual registers
		enum RegAddress
		{
			REG_KEYBOARD = 0, //!< Keyboard register
			REG_LED_COMMAND = 1, //!< LED register
			REG_ADDRESS_LOW = 2, //!< Lower address register
			REG_ADDRESS_HIGH = 3, //!< Upper address register
			REG_LCD_COLOR = 4, //!< LCD color register
			REG_LCD_CHAR = 5, //!< LCD char register
			REG_LCD_COMMAND = 6, //!< LCD command register
			REG_RS232_RX = 7, //!< RS232 receive register
			REG_RS232_TX = 8, //!< RS232 transmit register
			REG_RTC_FIELD = 9, //!< Real Time Clock data register
			REG_RTC_COMMAND = 10, //!< Real Time Clock command register
			REG_SPEAKER_FIELD = 11, //!< Speaker data register
			REG_SPEAKER_COMMAND = 12, //!< Speaker command register
			REG_FS_NAME = 13, //!< File System name register
			REG_FS_DATA = 14, //!< File System data register
			REG_FS_COMMAND = 15 //!< File System command register
		};

		//!< List of LED commands
		enum LedCommand
		{
			LED_RUN_OFF = 0, //!< Turn off run LED
			LED_RUN_ON, //!< Turn on run LED
			LED_ERROR_OFF, //!< Turn off error LED
			LED_ERROR_ON //!< Turn on error LED
		};

		//!< List of LCD commands
		enum LcdCommand
		{
			LCD_CLEAR = 0, //!< Clear the screen
			LCD_REFRESH, //!< Refresh the screen using data from the framebuffer
			LCD_SCROLL //!< Scroll up one line up the screen
		};

		//!< List of Real Time Clock fields
		enum RtcField
		{
			RTC_YEAR = 0, //!< Year
			RTC_MONTH, //!< Month
			RTC_DAY, //!< Day
			RTC_HOUR, //!< Hour
			RTC_MINUTE, //!< Minute
			RTC_SECOND //!< Second
		};

		//!< List of Real Time Clock commands
		enum RtcCommand
		{
			RTC_READ = 0, //!< Read current date and time
			RTC_WRITE //!< Set current date and time
		};

		//!< List of Speaker fields
		enum SpeakerField
		{
			SPEAKER_NOTE = 0, //!< Note number
			SPEAKER_TIME, //!< Time to play
			SPEAKER_FILL, //!< Percentage fill of the square wave
			SPEAKER_VOLUME //!< Volume
		};

		//!< List of Speaker commands
		enum SpeakerCommand
		{
			SPEAKER_CLEAR = 0, //!< Stop playing and clear the buffer
			SPEAKER_ADD_NOTE, //!< Add note to the buffer and play it
		};

		//!< List of File System commands
		enum FSCommand
		{
			FS_OPEN_APP = 0, //!< Open an application
			FS_OPEN_FILE, //!< Open a file
			FS_LIST_APP, //!< List all applications
			FS_LIST_FILE, //!< List all files
			FS_SIZE, //!< Get size of the file
			FS_READ_BLOCK //!< Read data from the block
		};

		//!< This struct cointains a memory adddress
		struct Address
		{
			unsigned char low; //!< Lower byte of the address
			unsigned char high; //!< Upper byte of the address
		};

		unsigned char regSelected; //!< Selected virtual register number

		//!< This struct contains motherboard's virtual registers buffer
		struct
		{
			unsigned char keyboard; //!< Keyboard buffer
			Address address; //!< Address buffer
			unsigned char lcdForegroundColor; //!< LCD foreground color buffer
			unsigned char lcdBackgroundColor; //!< LCD background color buffer
			unsigned char lcdChar; //!< LCD char buffer
			unsigned char rs232Rx; //!< RS232 receive buffer
			unsigned char rs232Tx; //!< RS232 transmit bufffer
			unsigned char rs232TxBufferFree; //!< RS232 transmit free space in buffer counter
			RTC::DateTime rtcField; //!< Real Time Clock buffer
			Speaker::Note speakerField; //!< Speaker buffer
			unsigned char speakerBufferFree; //!< Speaker free space in buffer counter
			FS::Name fsName; //!< File System name buffer
			FS::Size fsSize; //!< File System size buffer
			FS::Block fsBlock; //!< File System data block buffer
		} reg;

		volatile unsigned char in; //!< Input register buffer

		Keyboard keyboard; //!< Keyboard class instance used for emulation motherboard's IO part
		LED led; //!< LED class instance used for emulation motherboard's IO part
		LCD lcd; //!< LCD class instance used for emulation motherboard's IO part
		RS232 rs232; //!< RS232 class instance used for emulation motherboard's IO part
		RTC rtc; //!< Real Time Clock class instance used for emulation motherboard's IO part
		Speaker speaker; //!< Speaker class instance used for emulation motherboard's IO part
		FS fs; //!< File System class instance used for emulation motherboard's IO part

	signals:
		void updateLEDRunSignal(bool enable);
		void updateLEDErrorSignal(bool enable);

		void updateLCDBufferSignal(LCD::Buffer buffer);

		void updateRS232TxSignal(unsigned char c);
		void updateRS232RxSignal(unsigned char c);

		void updateRTCDateTimeSignal(QDateTime dateTime);

		void updateSpeakerStatusSignal(int bufferUsed);

		void inSignal(unsigned char in);

	public slots:
		void keyboardDataReadySlot(bool ready);
		void rs232DataReadySlot(bool ready);

		void outSlot(unsigned char out);
};

#endif