#include "cmPrefix.h"
#include "cmGlobal.h"
#include "cmTime.h"
#include "cmMidi.h"



typedef struct
{
  cmMidiByte_t   status;
  cmMidiByte_t  byteCnt;
  const char*     label;
} cmMidiStatusDesc;

cmMidiStatusDesc _cmMidiStatusDescArray[] =
{
  // channel messages
	{ kNoteOffMdId,	2, "nof" },
	{	kNoteOnMdId,	2, "non" },
	{	kPolyPresMdId,2, "ppr" },
	{	kCtlMdId,		  2, "ctl" },
	{	kPgmMdId,		  1, "pgm" },
	{	kChPresMdId,	1, "cpr" },
	{	kPbendMdId,		2, "pb"  },


	{	kSysExMdId,		kInvalidMidiByte,"sex" },

  // system common
  { kSysComMtcMdId,    1, "mtc" },
  { kSysComSppMdId,    2, "spp" },
  { kSysComSelMdId,    1, "sel" },
  { kSysComUndef0MdId, 0, "cu0" },
  { kSysComUndef1MdId, 0, "cu1" },
  { kSysComTuneMdId,   0, "tun" },
  { kSysComEoxMdId,    0, "eox" },

  // system real-time
  { kSysRtClockMdId, 0, "clk" },
  { kSysRtUndef0MdId,0, "ud0" },
  { kSysRtStartMdId, 0, "beg" },
  { kSysRtContMdId,  0, "cnt" },
  { kSysRtStopMdId,  0, "end" },
  { kSysRtUndef1MdId,0, "ud1" },
  { kSysRtSenseMdId, 0, "sns" },
  { kSysRtResetMdId, 0, "rst" },

 	{ kInvalidStatusMdId,  kInvalidMidiByte, "ERR" }
};

cmMidiStatusDesc _cmMidiMetaStatusDescArray[] =
{
  { kSeqNumbMdId,    2, "seqn"  },
  { kTextMdId,      -1, "text"  },
  { kCopyMdId,      -1, "copy"  },
  { kTrkNameMdId,   -1, "name"  },
  { kInstrNameMdId, -1, "instr" },
  { kLyricsMdId,    -1, "lyric" },
  { kMarkerMdId,    -1, "mark"  },
  { kCuePointMdId,  -1, "cue"   },
  { kMidiChMdId,     1, "chan"  },
  { kEndOfTrkMdId,   0, "eot"   },
  { kTempoMdId,      3, "tempo" },
  { kSmpteMdId,      5, "smpte" },
  { kTimeSigMdId,    4, "tsig"  },
  { kKeySigMdId,     2, "ksig"  },
  { kSeqSpecMdId,   -1, "seqs"  },
  { kInvalidMetaMdId, kInvalidMidiByte, "ERROR"}
};

cmMidiStatusDesc _cmMidiPedalLabel[] = 
{
  { kSustainCtlMdId,    0, "sustn" },
  { kPortamentoCtlMdId, 0, "porta" },
  { kSostenutoCtlMdId,  0, "sostn" },
  { kSoftPedalCtlMdId,  0, "soft"  },
  { kLegatoCtlMdId,     0, "legat" },
  { kInvalidMidiByte, kInvalidMidiByte, "ERROR"}
};

//====================================================================================================

const char* cmMidiStatusToLabel( cmMidiByte_t status )
{
  unsigned i;

  if( !cmMidiIsStatus(status) )
    return NULL;

  // remove the channel value from ch msg status bytes
  if( cmMidiIsChStatus(status) )
    status &= 0xf0;

  for(i=0; _cmMidiStatusDescArray[i].status != kInvalidStatusMdId; ++i)
    if( _cmMidiStatusDescArray[i].status == status )
      return _cmMidiStatusDescArray[i].label;

  return _cmMidiStatusDescArray[i].label; 
}

const char*   cmMidiMetaStatusToLabel( cmMidiByte_t metaStatus )
{
  int i;
  for(i=0; _cmMidiMetaStatusDescArray[i].status != kInvalidMetaMdId; ++i)
    if( _cmMidiMetaStatusDescArray[i].status == metaStatus )
      break;

  return _cmMidiMetaStatusDescArray[i].label; 
}

const char* cmMidiPedalLabel( cmMidiByte_t d0 )
{
  int i;
  for(i=0; _cmMidiPedalLabel[i].status != kInvalidMidiByte; ++i)
    if( _cmMidiPedalLabel[i].status == d0 )
      break;

  return _cmMidiPedalLabel[i].label;
}

cmMidiByte_t cmMidiStatusToByteCount( cmMidiByte_t status )
{
  unsigned i;

  if( !cmMidiIsStatus(status) )
    return kInvalidMidiByte;

  // remove the channel value from ch msg status bytes
  if( cmMidiIsChStatus(status) )
    status &= 0xf0;

  for(i=0; _cmMidiStatusDescArray[i].status != kInvalidStatusMdId; ++i)
    if( _cmMidiStatusDescArray[i].status == status )
      return _cmMidiStatusDescArray[i].byteCnt;

  assert(0);

  return 0; 
}

unsigned      cmMidiTo14Bits( cmMidiByte_t d0, cmMidiByte_t d1 )
{
  unsigned val = d0;
  val <<= 7;
  val += d1;
  return val;
}

void          cmMidiSplit14Bits( unsigned v, cmMidiByte_t* d0Ref, cmMidiByte_t* d1Ref )
{
  *d0Ref = (v & 0x3f80) >> 7;
  *d1Ref = v & 0x7f;
}

int           cmMidiToPbend(  cmMidiByte_t d0, cmMidiByte_t d1 )
{
  int v = cmMidiTo14Bits(d0,d1);
  return v - 8192;
}

void          cmMidiSplitPbend( int v, cmMidiByte_t* d0Ref, cmMidiByte_t* d1Ref )
{
  unsigned uv = v + 8192;
  cmMidiSplit14Bits(uv,d0Ref,d1Ref);
}

//====================================================================================================
const char*     cmMidiToSciPitch( cmMidiByte_t pitch, char* label, unsigned labelCharCnt )
{
  static char buf[ kMidiSciPitchCharCnt ];

  if( label == NULL || labelCharCnt == 0 )
  {
    label = buf;
    labelCharCnt = kMidiSciPitchCharCnt;
  }

  assert( labelCharCnt >= kMidiSciPitchCharCnt );

  if( /*pitch < 0 ||*/ pitch > 127 )
  {
    label[0] = 0;
    return label;
  }

  assert( labelCharCnt >= 5 && /*pitch >= 0 &&*/ pitch <= 127 );

  char     noteV[]      =  { 'C', 'C', 'D', 'D', 'E', 'F', 'F', 'G', 'G', 'A', 'A', 'B' };
  char     shrpV[]      =  { ' ', '#', ' ', '#', ' ', ' ', '#', ' ', '#', ' ', '#', ' ' };
  int      octave       =  (pitch / 12)-1;
  unsigned noteIdx      =  pitch % 12;
  char     noteCh       =  noteV[ noteIdx ];
  char     sharpCh      =  shrpV[ noteIdx ];
  unsigned idx          =  1;

  label[labelCharCnt-1] = 0;
  label[0]              = noteCh;

  if( sharpCh != ' ' )
  {
    label[1] = sharpCh;
    idx      = 2;
  }

  assert( -1 <= octave && octave <= 9);

  snprintf(label+idx,kMidiSciPitchCharCnt-idx-1,"%i",octave);

  return label;
}


cmMidiByte_t    cmSciPitchToMidi( const char* sciPitchStr )
{
  const char* cp      = sciPitchStr;
  bool        sharpFl = false;
  bool        flatFl  = false;
  int         octave;
  int         idx     = -1;

  if( sciPitchStr==NULL || strlen(sciPitchStr) > 5 )
    return kInvalidMidiPitch;

  switch(tolower(*cp))
  {
    case 'a': idx = 9;  break;
    case 'b': idx = 11; break;
    case 'c': idx = 0;  break;
    case 'd': idx = 2;  break;
    case 'e': idx = 4;  break;
    case 'f': idx = 5;  break;
    case 'g': idx = 7;  break;
    default:
      return kInvalidMidiPitch;
  }

  ++cp;

  if( !(*cp) )
    return kInvalidMidiPitch;

  
  if((sharpFl = *cp=='#') == true )
    ++idx;
  else
    if((flatFl  = *cp=='b') == true )
      --idx;

  if( sharpFl || flatFl )
  {
    ++cp;

    if( !(*cp) )
      return kInvalidMidiPitch;
  }
  
  if( isdigit(*cp) == false && *cp!='-' )
    return kInvalidMidiPitch;

  octave = atoi(cp);

  unsigned rv =  (octave*12) + idx + 12;
  
  if( rv <= 127 )
    return rv;

  return kInvalidMidiPitch;
  
}
