#pragma once
#include "Snapshotable.h"
#include "CPU.h"

class VrcIrq : public Snapshotable
{
private:
	shared_ptr<Console> _console;
	uint8_t _irqReloadValue;
	uint8_t _irqCounter;
	int16_t _irqPrescalerCounter;
	bool _irqEnabled;
	bool _irqEnabledAfterAck;
	bool _irqCycleMode;

protected:
	void StreamState(bool saving) override
	{
		Stream(_irqReloadValue, _irqCounter, _irqPrescalerCounter, _irqEnabled, _irqEnabledAfterAck, _irqCycleMode);
	}

public:
	VrcIrq(shared_ptr<Console> console)
	{
		_console = console;
	}

	void Reset()
	{
		_irqPrescalerCounter = 0;
		_irqReloadValue = 0;
		_irqCounter = 0;
		_irqEnabled = false;
		_irqEnabledAfterAck = false;
		_irqCycleMode = false;
	}

	void ProcessCpuClock()
	{
		if(_irqEnabled) {
			_irqPrescalerCounter -= 3;

			if(_irqCycleMode || (_irqPrescalerCounter <= 0 && !_irqCycleMode)) {
				if(_irqCounter == 0xFF) {
					_irqCounter = _irqReloadValue;
					_console->GetCpu()->SetIrqSource(IRQSource::External);
				} else {
					_irqCounter++;
				}
				_irqPrescalerCounter += 341;
			}
		}
	}

	void SetReloadValue(uint8_t value)
	{
		_irqReloadValue = value;
	}

	void SetReloadValueNibble(uint8_t value, bool highBits)
	{
		if(highBits) {
			_irqReloadValue = (_irqReloadValue & 0x0F) | ((value & 0x0F) << 4);
		} else {
			_irqReloadValue = (_irqReloadValue & 0xF0) | (value & 0x0F);
		}
	}

	void SetControlValue(uint8_t value)
	{
		_irqEnabledAfterAck = (value & 0x01) == 0x01;
		_irqEnabled = (value & 0x02) == 0x02;
		_irqCycleMode = (value & 0x04) == 0x04;

		if(_irqEnabled) {
			_irqCounter = _irqReloadValue;
			_irqPrescalerCounter = 341;
		}

		_console->GetCpu()->ClearIrqSource(IRQSource::External);
	}

	void AcknowledgeIrq()
	{
		_irqEnabled = _irqEnabledAfterAck;
		_console->GetCpu()->ClearIrqSource(IRQSource::External);
	}
};