// -*-C++-*-
// This file is part of the gmod package
// Copyright (C) 1997 by Andrew J. Robinson

#ifdef USE_LOCAL
#include "soundcard.h"
#else
#include <sys/soundcard.h>
#endif

#include <sys/ultrasound.h>

#include "commands.h"
#include "defines.h"
#include "structs.h"
#include "globals.h"
#include "protos.h"

#include "Sample.h"
#include "Sequencer.h"

static unsigned char s3mExtendTab[] =
{CMD_INFO, CMD_GLISSANDO, CMD_FINETUNE, CMD_VIBRA_WAVE,	/* S0 S1 S2 S3 */
 CMD_TREMOLO_WAVE, CMD_INFO, CMD_INFO, CMD_INFO,	/* S4 S5 S6 S7 */
 CMD_SET_PAN, CMD_INFO, CMD_INFO, CMD_PATTERN_LOOP,	/* S8 S9 SA SB */
 CMD_CUT_NOTE, CMD_DELAY_NOTE, CMD_DELAY_PAT, CMD_INFO};	/* SC SD SE SF */

void
cvtXmPlay (int channel, unsigned char *effect, unsigned char *parm)
{
  switch (*effect)
    {
    case CMD_SLIDEUP:
    case CMD_SLIDEDOWN:
    case CMD_SLIDETO:
    case CMD_VIBRATO:
    case CMD_PORTANDVOL:
    case CMD_VIBRAANDVOL:
    case CMD_TREMOLO:
    case CMD_VOLSLIDE:
    case CMD_FINEVOLUP:
    case CMD_FINEVOLDOWN:
    case CMD_PANSLIDE:
    case CMD_RETRIGVOL:
    case CMD_GLOBALVOL_SLIDE:
    case CMD_XFINEPORTUP:
    case CMD_XFINEPORTDOWN:
      if (*parm == 0)
	*parm = voices[channel].lastInfo;
      else
	voices[channel].lastInfo = *parm;
      break;
    case CMD_GLOBAL_VOL:
      if (*parm)
	voices[channel].lastInfo = *parm;

      // the following check is needed because *parm is an unsigned char
      if (*parm >= 64)
	*parm = 255;
      else if (*parm)
	*parm = (*parm * 4) - 1;

      break;
    default:
      if (*parm != 0)
	voices[channel].lastInfo = *parm;
      if (*effect == CMD_INFO)
	{
	  *effect = 0;
	  *parm = 0;
	}
    }
}

void
cvtS3mPlay (int channel, unsigned char *effect, unsigned char *parm)
{
  switch (*effect)
    {
    case 0:
      break;
    case CMD_VOLSLIDE:
      if (*parm == 0)
	*parm = voices[channel].lastInfo;
      else
	voices[channel].lastInfo = *parm;

      if ((*parm > 0xf0) && (*parm < 0xff))
	{
	  *effect = CMD_FINEVOLDOWN;
	  *parm &= 0x0f;
	}
      else if (((*parm & 0x0f) == 0x0f) && ((*parm & 0xf0) > 0))
	{
	  *effect = CMD_FINEVOLUP;
	  *parm = (*parm >> 4) & 0x0f;
	}
      //      else if (((*parm & 0x0f) != 0) && ((*parm & 0xf0) != 0))
      else if (*parm & 0x0f)    // slide down overrides slide up
	*parm &= 0x0f;
      break;
    case CMD_PORTANDVOL:
    case CMD_VIBRAANDVOL:
      if (*parm == 0)
	*parm = voices[channel].lastInfo;
      else
	voices[channel].lastInfo = *parm;
      if (*parm & 0x0f)
	*parm &= 0x0f;
      break;
    case CMD_SLIDEUP:
      if (*parm == 0)
	*parm = voices[channel].lastInfo;
      else
	voices[channel].lastInfo = *parm;

      if (*parm >= 0xf0)
	{
	  *effect = CMD_FINEPORTUP;
	  *parm &= 0x0f;
	}
      else if (*parm >= 0xe0)
	{
	  *effect = CMD_XFINEPORTUP;
	  *parm &= 0x0f;
	}
      break;
    case CMD_SLIDEDOWN:
      if (*parm == 0)
	*parm = voices[channel].lastInfo;
      else
	voices[channel].lastInfo = *parm;

      if (*parm >= 0xf0)
	{
	  *effect = CMD_FINEPORTDOWN;
	  *parm &= 0x0f;
	}
      else if (*parm >= 0xe0)
	{
	  *effect = CMD_XFINEPORTDOWN;
	  *parm &= 0x0f;
	}
      break;
    case CMD_ARPEG2:
      if (*parm == 0)
	*parm = voices[channel].lastInfo;
      else
	voices[channel].lastInfo = *parm;

      *effect = CMD_ARPEG;
      break;
    case CMD_TREMOR:
    case CMD_SLIDETO:
    case CMD_VIBRATO:
    case CMD_RETRIGVOL:
      if (*parm == 0)
	*parm = voices[channel].lastInfo;
      else
	voices[channel].lastInfo = *parm;
      break;
    case CMD_BREAK:
      voices[channel].lastInfo = *parm;
      *parm = ((*parm >> 4) & 0x0f) * 10 + (*parm & 0x0f);
      break;
    case CMD_FINETUNE:
      voices[channel].lastInfo = *parm;
      *parm &= 0x0f;
      if (*parm >= 8)
	*parm -= 8;
      else
	*parm += 8;
      break;
    case CMD_GLOBAL_VOL:
      voices[channel].lastInfo = *parm;
      if (*parm > 0)
	*parm = (*parm * 4) - 1;
      break;
    case CMD_EXTENDED:
      voices[channel].lastInfo = *parm;
      *effect = s3mExtendTab[(*parm & 0xf0) >> 4];
      *parm &= 0x0f;

      if (*effect == CMD_SET_PAN)
	*parm *= 17;

      break;
    default:
      voices[channel].lastInfo = *parm;
    }

  if (*effect == CMD_INFO)
    {
      *effect = 0;
      *parm = 0;
    }
}

int
doCommand(int channel, int command, int parm, int position, int pattern,
	  struct songInfo *songChar, struct effectInfo *effects )
{
#ifdef USE_X
  SEQ_DECLAREBUF();
#endif
  int jump = 0;

  switch (command)
    {
    case CMD_JUMP:
      jump = MOVE_JUMP;
      effects->position = parm;
      effects->pattern = 0;
      break;

    case CMD_BREAK:
      jump = MOVE_BREAK;
      if (((position + 1) < songChar->songlength) &&
	  (parm < patternLen[tune[position + 1]]))
	effects->pattern = parm;
      else
	effects->pattern = 0;
      break;

    case CMD_SET_TICKS:
      if (parm)
	TICKS_PER_DIVISION (parm);
      else
	{
#ifdef USE_X
	  syncTime ();
	  SEQ_ECHO_BACK (ECHO_SPEED0);
#else
	  jump = MOVE_EXIT;
#endif
	}
      break;

    case CMD_SET_BPM:
      TEMPO (parm, songChar->clockSpeed);
      break;

    case CMD_DELAY_PAT:
      effects->delayNotes = parm;
      break;

    case CMD_PATTERN_LOOP:
      if (parm == 0)
	voices[channel].pattern = pattern;
      else
	{
	  effects->loopChan = channel;
	  if (voices[channel].loopTimes == 0)
	    {
	      voices[channel].loopTimes = parm;
	      effects->pattern = voices[channel].pattern;
	      jump = MOVE_LOOP;
	    }
	  else if (--voices[channel].loopTimes > 0)
	    {
	      effects->pattern = voices[channel].pattern;
	      jump = MOVE_LOOP;
	    }
	}
      break;
    }

  return jump;
}

int
playNote (int channel, int position, int pattern, struct noteInfo *pat,
	   struct songInfo *songChar, struct effectInfo *effects,
	   struct optionsInfo *options)
{
  extern Sample *samples[];
  extern Sequencer *seq;

  int jump = 0;
  int note = pat->note;
  unsigned char command = pat->command[1], param = pat->parm1[1];

  if (songChar->type == MODULE_S3M)
    cvtS3mPlay (channel, &command, &param);
  else if (songChar->type == MODULE_XM)
    cvtXmPlay (channel, &command, &param);

  seq->resetEffects(channel, (command != CMD_NOP) || note);

  if (pat->sample)
    seq->sample(channel, samples[pat->sample - 1]);

  if (note == NOTE_STOP)
    {
      seq->keyOff(channel);
      note = 0;
    }
  else if (note && (pat->command[0] != CMD_SLIDETO) &&
	   (command != CMD_SLIDETO))
    seq->note(channel, note, songChar->slideType);

  if (!seq->doCommand(channel, pat->command[0], pat->parm1[0], pat->parm2[0],
		      pat->note, songChar, options))
    jump = doCommand(channel, pat->command[0], pat->parm1[0], position,
		     pattern, songChar, effects);

  if (!seq->doCommand(channel, command, param, pat->parm2[1], pat->note,
		      songChar, options))
    jump |= doCommand(channel, command, param, position, pattern, songChar,
		      effects);

  return jump;
}
