/**
  ******************************************************************************
  * @file           : usbd_midi_if.c
  * @brief          :
  ******************************************************************************

    (CC at)2016 by D.F.Mac. @TripArts Music

  ******************************************************************************

    Modified by keshikan (www.keshikan.net) 2018
    The license is (CC BY 4.0), and takes over from original usbd_midi_if.h/c.

    See also original source code page.
    https://github.com/mimuz/mimuz-tuch/blob/master/STM32/

  ******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include "usbd_midi_if.h"
#include "stm32f0xx_hal.h"


// basic midi rx/tx functions
static uint16_t MIDI_DataRx(uint8_t *msg, uint16_t length);
static uint16_t MIDI_DataTx(uint8_t *msg, uint16_t length);

// for Cure Series
#define MIDI_BUFFER_SIZ (512)//FIFO buffer byte size for midi message buffer

RingBufferU8 rbuf_usb_rx[MIDI_OUT_JACK_NUM]; //for input from USB
RingBufferU8 rbuf_jack_rx[MIDI_IN_JACK_NUM];  //for input from MIDI-IN jack

//for receiving midi data from jack
MidiAnalysisStatus analyzed_status[MIDI_IN_JACK_NUM];
MIDIEvent midi_event[MIDI_IN_JACK_NUM];	//received midi data

uint8_t rx_midi_msg[MIDI_IN_JACK_NUM];


FUNC_STATUS midiInit()
{
	uint32_t i,j;

	for(i=0; i<MIDI_OUT_JACK_NUM; i++){
		if(BUFFER_SUCCESS != cureRingBufferU8Init(&rbuf_usb_rx[i], MIDI_BUFFER_SIZ))
		{
			return FUNC_ERROR;
		}
	}

	for(i=0; i<MIDI_IN_JACK_NUM; i++){
		if(BUFFER_SUCCESS != cureRingBufferU8Init(&rbuf_jack_rx[i], MIDI_BUFFER_SIZ))
		{
			return FUNC_ERROR;
		}
	}

	//Init RX
	for(i=0; i<MIDI_IN_JACK_NUM; i++){

		rx_midi_msg[i] = 0x00;
		analyzed_status[i].data_idx = 0;
		midi_event[i].length = 0;

		for(j=0; j<MIDI_SENDDATA_MAX; j++){
			midi_event[i].midi_byte[j] = 0x00;
		}
	}

	return FUNC_SUCCESS;
}

FUNC_STATUS midiGetFromUsbRx(uint8_t cable_num, uint8_t* dat)
{
	if(BUFFER_SUCCESS != cureRingBufferU8Dequeue(&rbuf_usb_rx[cable_num], dat))
	{
		return FUNC_ERROR;
	}

	return FUNC_SUCCESS;
}

FUNC_STATUS midiGetFromJackRx(uint8_t cable_num)
{
	if(BUFFER_SUCCESS != cureRingBufferU8Dequeue(&rbuf_jack_rx[cable_num], &rx_midi_msg[cable_num]))
	{
		return FUNC_ERROR;
	}

	return FUNC_SUCCESS;
}

FUNC_STATUS midiSetFromJackRx(uint8_t cable_num, uint8_t* dat)
{
	if(BUFFER_SUCCESS != cureRingBufferU8Enqueue(&rbuf_jack_rx[cable_num], dat))
	{
		return FUNC_ERROR;
	}

	return FUNC_SUCCESS;
}


bool isUsbRxBufEmpty(uint8_t cable_num)
{
	if( 0 != _cureRingBufferU8GetUsedSize(&rbuf_usb_rx[cable_num]) ){
		return false;
	}

	return true;
}

bool isJackRxBufEmpty(uint8_t cable_num)
{
	if( 0 != _cureRingBufferU8GetUsedSize(&rbuf_jack_rx[cable_num]) ){
		return false;
	}

	return true;
}

bool isRxBufEmpty()
{
	uint32_t i;

	for(i=0; i<MIDI_OUT_JACK_NUM; i++){
		if(0 != _cureRingBufferU8GetUsedSize(&rbuf_usb_rx[i]))
		{
			return false;
		}
	}

	for(i=0; i<MIDI_IN_JACK_NUM; i++){
		if(0 != _cureRingBufferU8GetUsedSize(&rbuf_jack_rx[i]))
		{
			return false;
		}
	}
	return true;
}


USBD_MIDI_ItfTypeDef USBD_Interface_fops_FS =
{
  MIDI_DataRx,
  MIDI_DataTx
};


static uint16_t MIDI_DataRx(uint8_t *msg, uint16_t length){
  uint16_t cnt;
  uint16_t msgs = length / 4;
  uint16_t chk = length % 4;
  uint8_t u8b;
  uint8_t midi_size;

  if(0 != chk)
  {
	  return 0;
  }

  for(uint32_t cnt_msgs = 0; cnt_msgs < msgs; cnt_msgs++){

	  uint8_t cable_num = (msg[0 + 4*cnt_msgs] & 0xF0) >> 4;
	  uint8_t code_idx_num = msg[0 + 4*cnt_msgs] & 0x0F;

	  switch (code_idx_num) {

	  	  //not defined
		  case 0x0:
		  case 0x1:
			  midi_size = 0;
			  break;

		  //1byte message
		  case 0x5:
		  case 0xF:
			  midi_size = 1;
			  break;

		  //2byte message
		  case 0x2:
		  case 0x6:
		  case 0xC:
		  case 0xD:
			  midi_size = 2;
			  break;

		  //3byte message
		  case 0x3:
		  case 0x4:
		  case 0x7:
		  case 0x8:
		  case 0x9:
		  case 0xA:
		  case 0xB:
		  case 0xE:
			  midi_size = 3;
			  break;

		  default:
			  midi_size = 0;
			  break;
	  }

	  for(cnt = 0;cnt < midi_size;cnt ++){
		  u8b = *(msg + 4*cnt_msgs + cnt + 1);
		  cureRingBufferU8Enqueue(&rbuf_usb_rx[cable_num], &u8b);
	  }
  }

  return 0;
}

void sendMidiMessage(uint8_t *msg, uint16_t size){
  if(size == 4){
//	APP_Rx_Buffer[0] = msg[0];
//	APP_Rx_Buffer[1] = msg[1];
//	APP_Rx_Buffer[2] = msg[2];
//	APP_Rx_Buffer[3] = msg[3];
//    USBD_MIDI_SendData(&hUsbDeviceFS, APP_Rx_Buffer, size);
    MIDI_DataTx(msg, size);
  }
}

static uint16_t MIDI_DataTx(uint8_t *msg, uint16_t length){
  uint32_t i = 0;
  while (i < length) {
    APP_Rx_Buffer[APP_Rx_ptr_in] = *(msg + i);
    APP_Rx_ptr_in++;
    i++;
    if (APP_Rx_ptr_in == APP_RX_DATA_SIZE) {
      APP_Rx_ptr_in = 0;
    }
  }
  return USBD_OK;
}

bool midiEventIsGenerated(uint8_t cable_num)
{
	uint8_t upper_half_byte= (rx_midi_msg[cable_num]) & 0xF0;

	if( upper_half_byte & 0x80 ){//0x80-0xFF:status byte

		switch(upper_half_byte){

			case 0xF0://0xF0-0xFF:system message
				switch(rx_midi_msg[cable_num]){

					case 0xF0://SysEx Start
						analyzed_status[cable_num].data_idx = 0;
						midi_event[cable_num].midi_byte[ analyzed_status[cable_num].data_idx++ ] = rx_midi_msg[cable_num];
						analyzed_status[cable_num].type = MSG_SYSEX;
						analyzed_status[cable_num].stat = WAIT_SYSTEM_DATA;
						break;

					case 0xF7://SysEx End
						midi_event[cable_num].midi_byte[ analyzed_status[cable_num].data_idx++ ] = rx_midi_msg[cable_num];
						midi_event[cable_num].length = analyzed_status[cable_num].data_idx;
						analyzed_status[cable_num].stat = END_ANALYSIS;
						break;

					case 0xF2://Song Position
						midi_event[cable_num].midi_byte[0] = rx_midi_msg[cable_num];
						analyzed_status[cable_num].type = MSG_THREE_BYTE;
						analyzed_status[cable_num].stat = WAIT_DATA1;
						break;

					case 0xF1://Time Code
					case 0xF3://Song Select
						midi_event[cable_num].midi_byte[0] = rx_midi_msg[cable_num];
						analyzed_status[cable_num].type = MSG_TWO_BYTE;
						analyzed_status[cable_num].stat = WAIT_DATA1;
						break;

					case 0xF4://Undefined
					case 0xF5://Undefined
					case 0xF6://Tune request
					case 0xF8://Timing clock
					case 0xF9://Undefined
					case 0xFA://Start
					case 0xFB://Continue
					case 0xFC://Stop
					case 0xFD://Undefined
					case 0xFE://Active Sensing
					case 0xFF://Reset
						midi_event[cable_num].midi_byte[0] = rx_midi_msg[cable_num];
						midi_event[cable_num].length = 1;
						analyzed_status[cable_num].type = MSG_ONE_BYTE;
						analyzed_status[cable_num].stat = END_ANALYSIS;
						break;
				}
				analyzed_status[cable_num].is_system_common = true;
				break;

			case 0x80://Note Off
			case 0x90://Note On
			case 0xA0://Polyphonic key-pressure
			case 0xB0://ControlChange
			case 0xE0://PitchBend
				midi_event[cable_num].midi_byte[0] = rx_midi_msg[cable_num];
				analyzed_status[cable_num].type = MSG_THREE_BYTE;
				analyzed_status[cable_num].stat = WAIT_DATA1;
				analyzed_status[cable_num].is_system_common = false;
				break;

			case 0xC0://Program Change
			case 0xD0://Channel pressure
				midi_event[cable_num].midi_byte[0] = rx_midi_msg[cable_num];
				analyzed_status[cable_num].type = MSG_TWO_BYTE;
				analyzed_status[cable_num].stat = WAIT_DATA1;
				analyzed_status[cable_num].is_system_common = false;
				break;

			default:
				analyzed_status[cable_num].type = MSG_NOTHING;
				analyzed_status[cable_num].stat = START_ANALYSIS;
				analyzed_status[cable_num].is_system_common = false;
				break;
		}

	}else{//0x00-0x7F:data byte

		switch(analyzed_status[cable_num].stat){

			case WAIT_DATA1:
				midi_event[cable_num].midi_byte[1] = rx_midi_msg[cable_num];

				if(MSG_THREE_BYTE == analyzed_status[cable_num].type ){
					analyzed_status[cable_num].stat = WAIT_DATA2;
				}else if( MSG_TWO_BYTE == analyzed_status[cable_num].type ){
					midi_event[cable_num].length = 2;
					analyzed_status[cable_num].stat = END_ANALYSIS;
				}else{
					analyzed_status[cable_num].stat = START_ANALYSIS;
				}
				break;

			case WAIT_DATA2:
				if(MSG_THREE_BYTE == analyzed_status[cable_num].type ){
					midi_event[cable_num].midi_byte[2] = rx_midi_msg[cable_num];
					midi_event[cable_num].length = 3;
					analyzed_status[cable_num].stat = END_ANALYSIS;
				}else{
					analyzed_status[cable_num].stat = START_ANALYSIS;
				}
				break;

			case WAIT_SYSTEM_DATA:
				midi_event[cable_num].midi_byte[ analyzed_status[cable_num].data_idx++ ] = rx_midi_msg[cable_num];

				if(analyzed_status[cable_num].data_idx > (MIDI_SENDDATA_MAX - 1) ){
					analyzed_status[cable_num].stat = END_ANALYSIS;
				}
				break;

			case END_ANALYSIS://running status:When status byte is omitted.
				midi_event[cable_num].midi_byte[1] = rx_midi_msg[cable_num];
				if(MSG_THREE_BYTE == analyzed_status[cable_num].type){
					analyzed_status[cable_num].stat = WAIT_DATA2;
				}else if(MSG_TWO_BYTE == analyzed_status[cable_num].type){
					midi_event[cable_num].length = 2;
					analyzed_status[cable_num].stat = END_ANALYSIS;
				}
				break;

			case START_ANALYSIS:
				break;

			default:
				break;
		}
	}

	if(END_ANALYSIS == analyzed_status[cable_num].stat){
		return true;
	}else{
		return false;
	}

}


void midiGenerateUsbPacket(uint8_t cable_num)
{
	uint8_t msg_buf[4] = {0x00,0x00,0x00,0x00};
	uint32_t cnt_remain=0, cnt_length;

	switch(analyzed_status[cable_num].type){

		case MSG_ONE_BYTE:
			//byte 0: cable number + code index number
			msg_buf[0] = (cable_num << 4) + ((midi_event[cable_num].midi_byte[0] & 0xF0) >> 4);

			sendMidiMessage(msg_buf,4);
			break;

		case MSG_TWO_BYTE:
		case MSG_THREE_BYTE:
			//byte 0: cable number + code index number
			if(analyzed_status[cable_num].is_system_common){
				msg_buf[0] = (cable_num << 4) + midi_event[cable_num].length;
			}else{
				msg_buf[0] = (cable_num << 4) + ((midi_event[cable_num].midi_byte[0] & 0xF0) >> 4);
			}

			//byte 1-3
			for(uint32_t i=0; i<midi_event[cable_num].length; i++){
				msg_buf[i+1] = midi_event[cable_num].midi_byte[i];
			}

			sendMidiMessage(msg_buf,4);
			break;

		case MSG_SYSEX:
			for(cnt_length = 0; cnt_length < midi_event[cable_num].length; cnt_length++){
				if( 0xF7 !=midi_event[cable_num].midi_byte[cnt_length] ){
					if(2 == cnt_remain){
						msg_buf[3] = midi_event[cable_num].midi_byte[cnt_length];
						msg_buf[0] = (cable_num << 4) + 0x4;	//SysEx starts or continues
						sendMidiMessage(msg_buf,4);
					}else{
						msg_buf[1 + cnt_remain] = midi_event[cable_num].midi_byte[cnt_length];
					}


				}else{

					switch (cnt_remain) {
						case 0:
							msg_buf[0] = (cable_num << 4) + 0x5;	//SysEx ends with following single byte
							msg_buf[1] = 0xF7;
							msg_buf[2] = msg_buf[3] = 0x00;
							sendMidiMessage(msg_buf,4);
							break;

						case 1:
							msg_buf[0] = (cable_num << 4) + 0x6;	//SysEx ends with following two bytes.
							msg_buf[2] = 0xF7;
							msg_buf[3] = 0x00;
							sendMidiMessage(msg_buf,4);
							break;

						case 2:
							msg_buf[0] = (cable_num << 4) + 0x7;	//SysEx ends with following three bytes.
							msg_buf[3] = 0xF7;
							sendMidiMessage(msg_buf,4);
							break;
						default:
							break;
					}
				}

				cnt_remain++;
				if(cnt_remain >=3){
					cnt_remain = 0;
				}
			}
			break;

		default:
			break;
	}
	USBD_MIDI_SendPacket();
}

void midiProcess(){

	for(uint32_t cable_num=0; cable_num<MIDI_IN_JACK_NUM; cable_num++){
		while( FUNC_SUCCESS == midiGetFromJackRx(cable_num) ){
			if( midiEventIsGenerated(cable_num) ){// Generate MIDI event from UART buffer.
				//Analyze MIDI Message.
				midiGenerateUsbPacket(cable_num);
			}
		}
	}
}

