#pragma once

#include "32blit.h"
#include "usb_device.h"
#include "usb_host.h"
#include "i2c.h"

extern USBD_HandleTypeDef hUsbDeviceHS;

class USBManager
{
public:
	typedef enum {usbtCDC, usbtMSC} Type;
	typedef enum {usbsCDC, usbsMSCInititalising, usbsMSCMounting, usbsMSCMounted, usbsMSCUnmounting, usbsMSCUnmounted} State;

	USBManager() : m_type(usbtCDC), m_state(usbsCDC), m_mountStartTime(0), m_unmountStartTime(0), m_bHasActivity(false), m_bHasHadSomeActivity(false)
	{

	};

	void SetType(Type type)
	{
		if(m_type != type)
		{
			// tear down USB
			MX_USB_DEVICE_Deinit();
			m_type = type;

			// set start state
			if(m_type == usbtCDC)
				m_state = usbsCDC;
			else
			{
				m_bHasActivity = false;
				m_bHasHadSomeActivity = false;
				m_state = usbsMSCInititalising;
			}

			// recreate usb device
			MX_USB_DEVICE_Init();

      // re-init i2c
      MX_I2C4_Init();


		}
	}

	Type GetType(void)
	{
		return m_type;
	}

	void SetState(State state)
	{
		m_state = state;

		switch(m_state)
		{
			case usbsMSCMounting :
				m_mountStartTime = HAL_GetTick();
			break;

			case usbsMSCUnmounting :
				m_unmountStartTime = HAL_GetTick();
			break;

			case usbsMSCUnmounted:
				SetType(usbtCDC);

			default:
			break;
		}
	}

	State GetState(void)
	{
		return m_state;
	}

	const char *GetStateName(void)
	{
		return m_stateNames[GetState()];
	}

	bool IsMSCReady(void)
	{
		 return m_state == usbsMSCInititalising || m_state == usbsMSCMounting || m_state == usbsMSCMounted;
	}

	bool HasHadActivity(void)
	{
		bool bHasActivity = m_bHasActivity;
		m_bHasActivity = false;
		return bHasActivity;
	}

	void LogActivity(void)
	{
		m_bHasActivity = true;
		m_bHasHadSomeActivity = true;
	}

	void Update()
	{
		// On linux we seem to get many start/stops so use a timer to set mount status.
		const uint32_t uMountUnmountTime = 500;

		// can't be mounted if there's no usb connection
		// maybe show some kind of warning if previously mounted?
		if(m_type == usbtMSC && hUsbDeviceHS.dev_state != USBD_STATE_CONFIGURED)
		{
			m_state = usbsMSCInititalising;
			m_bHasHadSomeActivity = false;
		}

		switch(m_state)
		{
			case usbsMSCInititalising:
				if(m_bHasHadSomeActivity)
					m_state = usbsMSCMounting; // for windows as we don;t ever get a scsi start message
			break;

			case usbsMSCMounting :
				if(HAL_GetTick() > m_mountStartTime  + uMountUnmountTime)
					m_state = usbsMSCMounted;
			break;

			case usbsMSCUnmounting :
				if(HAL_GetTick() > m_unmountStartTime  + uMountUnmountTime)
					SetState(usbsMSCUnmounted);
			break;

			default:
			break;
		}

		// update otg mode
		// we're polling this here due to issues getting the interrupt working
		int new_mode = USB_GetMode(USB_OTG_HS);
		if(otg_mode != new_mode) {
			USB_DisableGlobalInt(USB_OTG_HS);

			if(new_mode) { // host
				MX_USB_DEVICE_Deinit();
				MX_USB_HOST_Init();

				// force CDC
				m_type = usbtCDC;
				m_state = usbsCDC;
			} else {
				MX_USB_HOST_Deinit();
				MX_USB_DEVICE_Init();
			}

			USB_EnableGlobalInt(USB_OTG_HS);

			otg_mode = new_mode;
		}
	}

private:
	Type				m_type;
	State				m_state;
	uint32_t		m_mountStartTime;
	uint32_t		m_unmountStartTime;
	bool				m_bHasActivity;
	bool				m_bHasHadSomeActivity;

	int otg_mode = 0;

	const char 	*m_stateNames[usbsMSCUnmounted+1] = {"CDC", "MSC Initialising", "MSC Mounting", "MSC Mounted", "MSC Unmounting", "MSC Unmounted"};
};


