/*
   Risc os Digital Signal Processor
   (C) 2017 A.V.Bartram

  THIS SOFTWARE IS PROVIDED  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  IN NO EVENT SHALL A.V.Bartram BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL
  ,SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO
  ),PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS
  ;OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "kernel.h"
#include "swis.h"
#include "cmodulehdr.h"
#include "synth.h"

// TODO SWI handling
// TODO Legacy mode envelope


/*
 * RDSP : Based on the C Module example.
 * Risc os Digital Signal Processor.
 *
 * TODO
 * It will respond to SWIs in the range 0x88000 + [0..63], or as otherwise
 * specified in the Module Header description CModuleHdr.
 *
 */

/* SharedSound SWI numbers */
#define SharedSound_InstallHandler	0x4B440
#define SharedSound_SampleRate		0x4B446
#define SharedSound_RemoveHandler	0x4B441

#define  TM                     "RSynth"
#define  UNKNOWN_CMD_SERVICE    4
#define  UNUSED(x)              (x = x)

static _kernel_oserror tm_error = { 0x88000, "*** " TM " error ***" };

static int sound_handler_id;
static int unknown_cmd_count;
static int other_service_call_count;
static int xxx;

// Instance of the syntheiser
RISC_SYNTH g_synth;

// TODO: Rate must be a member of the synthesiser as well as a timestretch
// constant.
static int g_rate = 0;

static int g_initialised = 0;

// For diagnostics
static int g_verbose = 0;

// For increased BBC Micro compatibility
// n.b. At the moment this will be limited to the envelope command
//      Sound channel zero and SOUND command have reasonable defaults.
static int g_legacy = 0;

// Move to synth.h
typedef unsigned char* BYTE;





int init_sharedsound(void)
{
  if (g_initialised)
  {
    if (g_verbose)
      printf("Already '*rstart'ed. No action taken\n");
      return 0;
  }

  extern RISC_SYNTH g_synth;
  _kernel_swi_regs regs;
  _kernel_oserror *err;

  int i;
    // Ensure synth channel is zero - TODO - call synthInit
  memset(&g_synth, 0, sizeof(RISC_SYNTH));
  for (i = 0; i < MAX_POLY; ++i)
  {
    g_synth.m_channel[i].m_lpf.cutoff = 255;
    g_synth.m_channel[i].m_lpf.resonance = 0;
    g_synth.m_channel[i].m_dco.m_sample.m_data = NULL;
    g_synth.m_channel[i].m_dco.m_sample.m_dataLength = 0;
  }
    g_synth.tonetable = _toneTable;
    g_synth.sampleFreqTable = _sampleFreqTable;
    for (i = 0; i < 256; ++i)
      g_synth.m_sample[i].m_data = NULL;

    // Default to 1/2 second and 100/256 feedback
    g_synth.m_effects.m_delayLine.m_record = 50 * 441;
    g_synth.m_effects.m_delayLine.m_play = 0;
    g_synth.m_effects.m_delayLine.m_feedback = 100;

	/* Try to register sharedsound handler, return nonzero on failure */

	/* First load SharedSound if it isn't already loaded
	   A *command is the easiest way to do this (usually in your programs !Run file, but this demo doesn't have one) */
	system("RMEnsure SharedSound 0.00 IfThere System:Modules.SSound Then RMLoad System:Modules.SSound");
	/* Now check whether it is loaded, so we can print a more friendly error message than 'SWI xxx not found' */
	regs.r[0] = 18;
	regs.r[1] = (int) "SharedSound";
	if ((err = _kernel_swi(OS_Module,&regs,&regs)))
	{
		printf("Error: SharedSound module not loaded, and not in System:Modules!\n");
		return 1;
	 }

  /* Request 44 khz audio. This should succeed on all modern platforms */
  /* So in most cases scaling our output buffers will not be required */
	/* Get sample rate we have been assigned */
	regs.r[0] = sound_handler_id;
	regs.r[1] = 44100*1024;
	_kernel_swi(SharedSound_SampleRate,&regs,&regs);
	g_rate = regs.r[1];

    if (g_verbose)
	  printf("RDSP: Shared sound running at %d / 1024 Hz\n", regs.r[1]);

    if (regs.r[1] != 45158400)
    {
      printf("RDSP: Warning! RDSP currently only supports 44 Khz audio\n");
      printf("RDSP:          Sounds and samples will not play at correct pitch.\n");
      printf("RDSP: RISC OS has returned %d / 1024 Hz for shared sound\n", regs.r[1]);
    }

	/* Now we can register our handler */
	regs.r[0] = (int) buffer_fill;
	regs.r[1] = (int)&g_synth;
	regs.r[2] = 1;
	regs.r[3] = (int) "Sound demo";
	regs.r[4] = 0;
	if ((err = _kernel_swi(SharedSound_InstallHandler,&regs,&regs)))
	{
		printf("Error: SharedSound_InstallHandler returned error %d, %s\n",err->errnum,err->errmess);
		return 1;
	}
	sound_handler_id = regs.r[0];
	/* Install error handlers */
//	atexit(shutdown_sharedsound);
//	regs.r[0] = 1;
//	regs.r[1] = (int) error_handler;
//	regs.r[2] = 0;
//	_kernel_swi(OS_Claim,&regs,&regs);


   g_initialised = 1;

   if (g_verbose)
     printf("Shared sound installed ok\n");

	return 0;
}

void shutdown_sharedsound(void)
{
	 _kernel_swi_regs regs;
	 int i = 0;
	 extern RISC_SYNTH g_synth;

    if (g_initialised == 0)
      return;

	/* Deregister sharedsound handler */
	if(sound_handler_id)
	{
		regs.r[0] = sound_handler_id;
		_kernel_swi(SharedSound_RemoveHandler,&regs,&regs);
		sound_handler_id = 0;
	 }


	regs.r[0] = 7;
	regs.r[1] = (int)&rosesound_entry;
	regs.r[2] = 0;
//	_kernel_swi(OS_Release,&regs,&regs);

	/* Remove error handler */
//	regs.r[0] = 1;
//	regs.r[1] = (int) error_handler;
//	regs.r[2] = 0;
 //	_kernel_swi(OS_Release,&regs,&regs);

 // Free up any samples that were loaded
 for (i = 0; i < 256; ++i)
 {
   if (g_synth.m_sample[i].m_data != NULL)
   {
     free(g_synth.m_sample[i].m_data);
     g_synth.m_sample[i].m_data = NULL;
   }
 }

  if (g_verbose)
	   printf("Shut down\n");
  g_initialised = 0;
}


int HandleRSound(_kernel_swi_regs *registers)
{
  extern RISC_SYNTH g_synth;
  int    error = 0;
  int    channel = (registers->r[0]-1) & 0x7;
  // Ignore sound channel for now
  // *rsound <channel> <flags> <wave | instrument> <volume> <pitch> <duration>

  g_synth.m_channel[channel].m_mode = registers->r[1];

  // Check if a waveform is required or an instrument/envelope is to
  // be played.
  if (isdigit(((char*)registers->r[2])[0]))
  {
    int wave =  atoi((char*)registers->r[2]);

    if (wave <= 0xff)
      g_synth.m_channel[channel].m_wave = wave;
    else
    {
      // Is a sample required to be played?
      wave = wave - 0x100;
      if (wave < 0x200)
      {
        g_synth.m_channel[channel].m_wave = 0;
        g_synth.m_channel[channel].m_dco.m_osc_lookup = 0;
        g_synth.m_channel[channel].m_dco.m_sample_lookup_msw = 0;
        g_synth.m_channel[channel].m_dco.m_sample.m_dataLength = g_synth.m_sample[wave].m_length;
        g_synth.m_channel[channel].m_dco.m_sample.m_data = g_synth.m_sample[wave].m_data;

      }
    }

    g_synth.m_channel[channel].m_adr.m_flags = 0;
    g_synth.m_channel[channel].m_adr.m_counterSpeed = 0;
    g_synth.m_channel[channel].m_amp = registers->r[3];

    g_synth.m_channel[channel].m_tone = registers->r[4];
    g_synth.m_channel[channel].m_adsr.m_stage = 0;
    g_synth.m_channel[channel].m_adsr.m_stageCounter = 0;
    g_synth.m_channel[channel].m_adsr.m_endState = 3;
    g_synth.m_channel[channel].m_adsr.m_counterSpeed = 1;
    g_synth.m_channel[channel].m_adsr.m_numADSR[0] = 1;
    g_synth.m_channel[channel].m_adsr.m_numADSR[1] = 0;
    g_synth.m_channel[channel].m_adsr.m_numADSR[2] = registers->r[5];
    g_synth.m_channel[channel].m_adsr.m_numADSR[3] = 1;
    g_synth.m_channel[channel].m_adsr.m_stepADSR[0] = 1;
    g_synth.m_channel[channel].m_adsr.m_stepADSR[1] = 0;
    g_synth.m_channel[channel].m_adsr.m_stepADSR[2] = 0;
    g_synth.m_channel[channel].m_adsr.m_stepADSR[3] = 1;

  }
  else
  {

      // TODO - allow envelope command to activate sample dco
      // waves over 0xff are samples

    // Find the instrument in the table
    int found = 0;
    int i = 0;
    while ((!found) && (i < 256))
    {
      if (strcmp(g_synth.m_instrument[i].m_name, (char*)registers->r[2]) == 0)
        found = 1;
      else
        ++i;
    }

    if (found)
    {
      // Attack, decay and release are portions of duration
      int duration =  registers->r[5];  // map to sustain
      int volume = registers->r[3];
      int attackDelta = 0;
      int attackSteps = 0;
      int decayStep = 0;
      int decayDelta = 0;
      g_synth.m_channel[channel].m_amp = 0;
      g_synth.m_channel[channel].m_wave = g_synth.m_instrument[i].m_wave;
      g_synth.m_channel[channel].m_tone = registers->r[4];
      g_synth.m_channel[channel].m_adsr.m_counterSpeed = 1;
      g_synth.m_channel[channel].m_adsr.m_stage = 0;
      g_synth.m_channel[channel].m_adsr.m_stageCounter = 0;
      g_synth.m_channel[channel].m_adsr.m_endState = 4;

      g_synth.m_channel[channel].m_adsr.m_flags = g_synth.m_instrument[i].m_options;

      if ((volume >= g_synth.m_instrument[i].m_attack) &&
          ( g_synth.m_instrument[i].m_attack != 0))
      {
        attackDelta = volume / g_synth.m_instrument[i].m_attack;
        attackSteps =  g_synth.m_instrument[i].m_attack;
      }
      else
      {
        attackDelta = 1;
        attackSteps = g_synth.m_instrument[i].m_attack;
      }

      g_synth.m_channel[channel].m_adsr.m_stepADSR[0] = attackDelta;
      g_synth.m_channel[channel].m_adsr.m_numADSR[0] = attackSteps;

      // Decay is time. Sustain is proportion of volume
      if (g_synth.m_instrument[i].m_decay >
                   g_synth.m_instrument[i].m_sustain)
      {
        // For now linear decay (possibly logarthimic would be more correct
        decayStep = 16; // Should be proportional to decay
        decayDelta = ((g_synth.m_instrument[i].m_decay - g_synth
                                  .m_instrument[i].m_sustain) / 16) * -1;
        if (decayDelta == 0)
          decayDelta = -1;
      }
      else
      {
        decayStep = 0;
        decayDelta = 0;
      }

      g_synth.m_channel[channel].m_adsr.m_stepADSR[1] =decayDelta;
      g_synth.m_channel[channel].m_adsr.m_numADSR[1] = decayStep;

      g_synth.m_channel[channel].m_adsr.m_stepADSR[2] = 0;
      g_synth.m_channel[channel].m_adsr.m_numADSR[2] = duration;

      g_synth.m_channel[channel].m_adsr.m_numADSR[3] = g_synth.m_instrument[i].m_release;
      {
        int stepSize = g_synth.m_instrument[i].m_release;
        if (stepSize > 0)
          stepSize = 255 / g_synth.m_instrument[i].m_release;
        stepSize = (stepSize * volume) >> 8;
        if (stepSize == 0)
          stepSize = 1;

          g_synth.m_channel[channel].m_adsr.m_stepADSR[3] = stepSize * -1;
      }

      g_synth.m_channel[channel].m_adr.m_stage = 0;
      g_synth.m_channel[channel].m_adr.m_stageCounter = 0;
      g_synth.m_channel[channel].m_adr.m_endState = 4;

      g_synth.m_channel[channel].m_adr.m_counterSpeed = g_synth.m_instrument[i].m_steptime;
      g_synth.m_channel[channel].m_adr.m_flags = g_synth.m_instrument[i].m_options;
      g_synth.m_channel[channel].m_adr.m_stepADSR[0] = g_synth.m_instrument[i].m_asrAttackDelta;
      g_synth.m_channel[channel].m_adr.m_stepADSR[1] = 0;
      g_synth.m_channel[channel].m_adr.m_stepADSR[2] = g_synth.m_instrument[i].m_asrSustainDelta;
      g_synth.m_channel[channel].m_adr.m_stepADSR[3] = g_synth.m_instrument[i].m_asrReleaseDelta;

      g_synth.m_channel[channel].m_adr.m_numADSR[0] =  g_synth.m_instrument[i].m_asrStepsAttack;
      g_synth.m_channel[channel].m_adr.m_numADSR[1] =  0;
      g_synth.m_channel[channel].m_adr.m_numADSR[2] =  g_synth.m_instrument[i].m_asrStepsSustain;
      g_synth.m_channel[channel].m_adr.m_numADSR[3] =  g_synth.m_instrument[i].m_asrStepsRelease;

      g_synth.m_channel[channel].m_lpf.cutoff =  g_synth.m_instrument[i].m_cutoff;
      g_synth.m_channel[channel].m_lpf.resonance = g_synth.m_instrument[i].m_res;



    }
    else
    {
      error = 1;
    }

  }

  if (g_synth.m_channel[channel].m_wave >= 0x01 && g_synth.m_channel[channel].m_wave <= 0x1f)
  {
    // 711 = 22050 / 31 i.e. for half of sample rate when wave = 31 width = 22050 or 100%
     g_synth.m_channel[channel].m_pulse_width = 711 * g_synth.m_channel[channel].m_wave;
  }

  g_synth.m_channel[channel].m_prng = 1;     // Maybe move to initialisation
    g_synth.m_channel[channel].m_noise_throttle = 0;


return error;
}

void HandleRFilter(_kernel_swi_regs *registers)
{
  extern RISC_SYNTH g_synth;
  int channel = (registers->r[0]-1) & 0x7;

   g_synth.m_channel[channel].m_lpf.cutoff = registers->r[1];
   g_synth.m_channel[channel].m_lpf.resonance = registers->r[2];

}

void HandleREnvelope(_kernel_swi_regs *registers)
{
  extern RISC_SYNTH g_synth;
  int entry = (registers->r[0] & 0xff);
  char *name = (char*)(registers->r[1]);
  int wave =  (registers->r[2] & 0xff);
  int cutoff = ((registers->r[3]) & 0xff00) >> 8;
  int resonance = ((registers->r[3]) & 0xff);
  int options = (registers->r[4] & 0xff00) >> 8;
  int steptime =  (registers->r[4] & 0xff);
  int asrStepsAttack = (registers->r[5] & 0xff0000) >> 16;
  int asrStepsSustain = (registers->r[5] & 0xff00) >> 8;
  int asrStepsRelease = (registers->r[5] & 0xff);
  int asrDeltaAttack = (registers->r[6] & 0xff0000) >> 16;
  int asrDeltaSustain = (registers->r[6] & 0xff00) >> 8;
  int asrDeltaRelease = (registers->r[6] & 0xff);
  int adsrDeltaAttack = (registers->r[7] & 0xff000000) >> 24;
  int adsrDeltaDecay = (registers->r[7] & 0xff000000) >> 24;
  int adsrDeltaSustain = (registers->r[7] & 0xff00) >> 8;
  int adsrDeltaRelease = (registers->r[7] & 0xff);
  INSTRUMENT_TABLE *instrument = (g_synth.m_instrument)+entry;

  if ((name != NULL) && (strlen(name) < 15))
  {
    strcpy(instrument->m_name, name);
  }

  instrument->m_wave = wave;
  instrument->m_options = options;
  instrument->m_steptime = steptime;
  instrument->m_cutoff = cutoff;
  instrument->m_res = resonance;
  instrument->m_asrStepsAttack = asrStepsAttack;
  instrument->m_asrStepsSustain = asrStepsSustain;
  instrument->m_asrStepsRelease = asrStepsRelease;
  instrument->m_asrAttackDelta =  ((signed char)asrDeltaAttack);
  instrument->m_asrSustainDelta = ((signed char)asrDeltaSustain);
  instrument->m_asrReleaseDelta = ((signed char)asrDeltaRelease);
  instrument->m_attack = adsrDeltaAttack;
  instrument->m_decay = adsrDeltaDecay;
  instrument->m_sustain = adsrDeltaSustain;
  instrument->m_release = adsrDeltaRelease;

}

void HandleRFX(_kernel_swi_regs *registers)
{
  if ((registers->r[0] >= 0) &&
      (registers->r[0] <= 15))
  {
    extern RISC_SYNTH g_synth;
    g_synth.m_effects.m_mode = registers->r[0];
  }
}

void HandleRFXMix(_kernel_swi_regs *registers)
{
  if ((registers->r[0] >= 1) &&
      (registers->r[0] <= 8))
  {
    extern RISC_SYNTH g_synth;
    g_synth.m_effectChannel[registers->r[0]-1] =
                                             registers->r[1];
  }
}

void HandleRDelay(_kernel_swi_regs *registers)
{
    extern RISC_SYNTH g_synth;

    if ((registers->r[0] < 147) && (registers->r[1] < 256))
    {
      g_synth.m_effects.m_delayLine.m_record = registers->r[0] * 441;
      g_synth.m_effects.m_delayLine.m_play = 0;
      g_synth.m_effects.m_delayLine.m_feedback = registers->r[1];
    }
}

int HandleRLoad(_kernel_swi_regs *registers)
{
  int error = 0;
  extern RISC_SYNTH g_synth;
  int sampleID = registers->r[0];
  char *wav_file = (char*)registers->r[1];
  int i;
  int found = 0;

  if (g_initialised == 0)
  {
    error = 1;
    return error;
  }

  // No channel must be playing when this function is called
  for (i = 0; i < 8; ++i)
  {
    if (g_synth.m_channel[i].m_adsr.m_stage !=  g_synth.m_channel[i].m_adsr.m_endState)
      found = 1;
  }
  if (found)
  {
    error = 1;
  }
  else
  {
    FILE *fptr;
    int length = 0;
    // Header for valid wave file RIFF<chunk size>Wavefmt,16 bit, mono,
    // 44khz
    unsigned char wave_header_44[] = {0x52,0x49,0x46,0x46,0x68,0x40,0x40,0x00,
                                       0x57,0x41,0x56,0x45,0x66,0x6d,0x74,0x20,
                                       0x12,0x00,0x00,0x00,0x01,0x00,0x01,0x00,
                                       0x44,0xac,0x00,0x00,0x88,0x58,0x01,0x00,
                                       0x02,0x00,0x10,0x00,0x00,0x00,0x64,0x61,
                                       0x74,0x61};

    // to scan with hex
    unsigned char wave_header_22[] = {0x52,0x49,0x46,0x46,0xf2,0x04,0x02,0x00,
                                       0x57,0x41,0x56,0x45,0x66,0x6d,0x74,0x20,
                                       0x12,0x00,0x00,0x00,0x01,0x00,0x01,0x00,
                                       0x22,0x56,0x00,0x00,0x88,0x58,0x01,0x00,
                                       0x02,0x00,0x10,0x00,0x00,0x00,0x64,0x61,
                                       0x74,0x61};

    #define VALID_HEADER_SIZE 42

    unsigned char wave_header[VALID_HEADER_SIZE];

    int result = 1;

    // verify file & header
    if ((fptr = fopen(wav_file, "rb")) == NULL)
    {
        result = 0;
    }

    if (result == 1)
    {
		// read header (RIFF)
        result = 0;
        fread(wave_header, sizeof(unsigned char), 4, fptr);

        if ((memcmp(wave_header_44,
                                              wave_header,
                                              4) == 0))
        {
            result = 1;
        }

    }
    if (result == 1)
    {
        int i = 30;
		int founddata = 0;

		while ((i < 50) && (!founddata))
		{
    		char tmp_buffer[4] = {0};
			// try offset
			fseek(fptr, i, SEEK_SET);

			fread(tmp_buffer, 1, 4, fptr);

			if (strncmp(tmp_buffer, "data", 4) == 0)
			{
				founddata = 1;
			}

			i++;
		}

		if (founddata)
		{
	        // read header
		    fread(&length, sizeof(int), 1, fptr);
		}
		else
		{
			result = 0;
		}


    }


    // TODO If sample is not 44khz 16 bit then normalize on load
    //      i.e. convert to 16 bit 44khz (avoids complexity and processing
    //           later).

    if (result)
    {
      if (g_synth.m_sample[sampleID].m_data != NULL)
      {
        free(g_synth.m_sample[sampleID].m_data);
      }

      g_synth.m_sample[sampleID].m_data = (short*)malloc(length);
      g_synth.m_sample[sampleID].m_length = length / sizeof(short);
      if (g_synth.m_sample[sampleID].m_data == NULL)
      {
        error = 1;
      }
      else
        {

        fread(g_synth.m_sample[sampleID].m_data, sizeof(short),
              g_synth.m_sample[sampleID].m_length, fptr);
      }
    }
    // For now just report that the length could be read
    if (fptr != NULL)
        fclose(fptr);
  }

  return error;
}

void HandleRList()
{
  extern RISC_SYNTH g_synth;
  int i;

  // List all envelopes
  printf("number,name,wave,options,steptime,cut-off,res,steps,delta,attack,decay,sustain ,release\n");
  for (i = 0; i < 256; ++i)
  {
    if (strcmp(g_synth.m_instrument[i].m_name, "") != 0)
    {
      printf("%d, %s,%d,%d,%d,%d,%d,%d %d %d,%d %d %d,%d,%d,%d,%d\n",
      i,
      g_synth.m_instrument[i].m_name,
      g_synth.m_instrument[i].m_wave,
      g_synth.m_instrument[i].m_options,
      g_synth.m_instrument[i].m_steptime,
      g_synth.m_instrument[i].m_cutoff,
      g_synth.m_instrument[i].m_res,
      g_synth.m_instrument[i].m_asrStepsAttack,
      g_synth.m_instrument[i].m_asrStepsSustain,
      g_synth.m_instrument[i].m_asrStepsRelease,
      g_synth.m_instrument[i].m_asrAttackDelta,
      g_synth.m_instrument[i].m_asrSustainDelta,
      g_synth.m_instrument[i].m_asrReleaseDelta,
      g_synth.m_instrument[i].m_attack,
      g_synth.m_instrument[i].m_decay,
      g_synth.m_instrument[i].m_sustain,
      g_synth.m_instrument[i].m_release);
    }
  }
}

// End SWI implementation


_kernel_oserror *rs_swi(int swi_no, _kernel_swi_regs *r, void *private_word)
{
  // TODO: SWI call re-directs to handler functions
  UNUSED(swi_no);
  UNUSED(r);
  UNUSED(private_word);
/*    printf("%s: SWI no %d, r0 = %8x\n", TM, swi_no, r->r[0]);
    printf(
        "(%u unknown commands and %u other service calls since last call)\n",
        unknown_cmd_count, other_service_call_count);
    unknown_cmd_count = other_service_call_count = 0;
    if (swi_no % 4 == 0)
        return &tm_error;
    else if (swi_no == 1)
    xxx = 10 + swi_no / (swi_no-1);  /* this will fail, divide by zero */

    return 0;
}

typedef enum
{
  RSTART,
  RSTOP,
  RLOAD,
  RSAMPLE,
  RSOUND,
  RFILTER,
  RPAN,
  RFXMIX,
  RFX,
  RDELAY,
  RDRIVE,
  RCHORUS,
  RENVELOPE,
  RLIST,
  RVERBOSE,
  RLEGACY,
  RADVANCED,
  RUNKNOWN
} STAR_COMMAND;

typedef enum
{
  ERROR_NONE,
  ERROR_UNKNOWN_COMMAND,
  ERROR_MISSING_PARAMETER,
  ERROR_OUT_OF_RANGE
} COMMAND_ERROR;

// Command format table
typedef enum
{
  ET_STRING,
  ET_INTEGER,
  ET_HEX,
  ET_END
} ELEMENT_TYPE;

typedef struct
{
  ELEMENT_TYPE m_type;
  int          m_minRange;
  int          m_maxRange;
} COMMAND_PARAM;

#define MAX_COMMAND 256
#define MAX_PARAM   8
typedef struct
{
  char *m_name;
  STAR_COMMAND m_cmdid;
  COMMAND_PARAM m_param[9];
} COMMAND_STRUCT;

#define _EMPTY {ET_END, 0, 0}
#define _EMPTY_8 _EMPTY, _EMPTY, _EMPTY, _EMPTY, _EMPTY, _EMPTY, _EMPTY, _EMPTY
#define _EMPTY_2 _EMPTY, _EMPTY
#define _EMPTY_5 _EMPTY, _EMPTY, _EMPTY, _EMPTY, _EMPTY
#define _EMPTY_6 _EMPTY, _EMPTY, _EMPTY, _EMPTY, _EMPTY, _EMPTY
#define _EMPTY_7 _EMPTY, _EMPTY, _EMPTY, _EMPTY, _EMPTY, _EMPTY, _EMPTY

COMMAND_STRUCT m_commands[] = {
  { "*rstart", RSTART, {{ET_END, 0,0}, _EMPTY_8}},
  { "*rstop", RSTOP, {{ET_END, 0,0}, _EMPTY_8}},
  { "*rfilter", RFILTER, {{ET_INTEGER, 1, 8}, {ET_INTEGER , 0, 255}, {ET_INTEGER, 0, 12}, _EMPTY_5}},
  { "*rsound", RSOUND, {{ET_INTEGER, 1, 8}, {ET_INTEGER, 0, 2}, {ET_STRING, 0, 0}, {ET_INTEGER, 0, 255 }, {ET_INTEGER, 0, 364}, {ET_INTEGER, 0, 0xffffff}, _EMPTY_2}},
  //                           Env No                 Name        wave       Cutoff-res
  { "*renvelope", RENVELOPE, {{ET_HEX, 0, 255}, {ET_STRING, 0,0}, {ET_HEX, 0, 0xff},  {ET_HEX, 0, 0xffff},
  //  options,ASR steptime       steptime          ASR steps
  {ET_HEX, 0, 0xffffff}, {ET_HEX, 0, 0xffffff},
  // ASR delta                ADSR
  {ET_HEX, 0, 0xffffff}, {ET_HEX, 0, 0}, _EMPTY}},
  { "*rfxmix", RFXMIX, {{ET_INTEGER, 1, 8}, {ET_INTEGER, 0, 1}, _EMPTY_7}},
  { "*rfx", RFX, {{ET_INTEGER, 1, 8}, _EMPTY_8}},
  { "*rdelay", RDELAY, {{ET_INTEGER, 0, 255}, {ET_INTEGER, 0, 255}, _EMPTY_7}},
  { "*rload", RLOAD, {{ET_INTEGER, 0,255}, {ET_STRING, 0,0}, _EMPTY_7}},
  { "*rverbose", RVERBOSE, {{ET_END, 0,0}, _EMPTY_8}},
  { "*rlegacy", RLEGACY, {{ET_END, 0,0}, _EMPTY_8}},
  { "*radvanced", RADVANCED, {{ET_END, 0,0}, _EMPTY_8}},
  { "*rlist", RLIST, {{ET_END, 0,0}, _EMPTY_8}},

  // Not implemented yet.
  { "*rsample", RSAMPLE, {{ET_INTEGER, 1, 8}, {ET_STRING, 0,0}, {ET_INTEGER, 0, 255}, _EMPTY_5}},      // todo remove


  { "*rpan", RPAN, {{ET_INTEGER, 1, 8}, {ET_INTEGER, -127, 128}, _EMPTY_7}},


  { "*rdrive", RDRIVE, {{ET_INTEGER, 0, 255}, {ET_INTEGER, 0, 64}, _EMPTY_7}},
  { "*rchorus", RCHORUS, {{ET_INTEGER, 0, 255}, {ET_INTEGER, 0, 255}, {ET_INTEGER, 0, 65}, _EMPTY_6}},
  { NULL, RUNKNOWN, {{ET_END, 0,0}, _EMPTY_8}}
};

COMMAND_ERROR GetCommand(int cmd_no, const char *arg_list,
                STAR_COMMAND *command,
                _kernel_swi_regs *regs)
{
  int i = 0;
  int found = 0;
  COMMAND_ERROR err = ERROR_UNKNOWN_COMMAND;
  char cmd_buffer[4096];
  char *cmdToken = cmd_buffer;

  // TODO size checks etc.
  cmd_buffer[0] = '\0';
  strcpy(cmd_buffer+1, arg_list);

   int j = 0;
   i = cmd_no;
   *command = m_commands[i].m_cmdid;
   err = ERROR_NONE;
   found = 1;
   while ((m_commands[i].m_param[j].m_type != ET_END) &&
          (err == ERROR_NONE) && j < 9)
   {
     switch (m_commands[i].m_param[j].m_type)
     {
         case ET_STRING:
             // workaround possibly strtok in GCC is broken
             // Will revise when I move to DDE
           cmdToken = strtok(cmdToken+strlen(cmdToken)+1, " \t\n");

           if (cmdToken == NULL)
             err = ERROR_MISSING_PARAMETER;
           else
             regs->r[j] =(int)cmdToken;
             break;
         case ET_INTEGER:
           cmdToken = strtok(cmdToken+strlen(cmdToken)+1, " \t\n");

           if (cmdToken == NULL)
             err = ERROR_MISSING_PARAMETER;
           else
           {
             int value = atoi(cmdToken);
             if (((value < m_commands[i].m_param[j].m_minRange) ||
                  (value > m_commands[i].m_param[j].m_maxRange)) &&
                  (m_commands[i].m_param[j].m_maxRange !=
                   m_commands[i].m_param[j].m_minRange))
             {
               err = ERROR_OUT_OF_RANGE;
             }
             else
               regs->r[j] =value;
           }
           break;
         case ET_HEX:
           cmdToken = strtok(cmdToken+strlen(cmdToken)+1, " \t\n");

           if (cmdToken == NULL)
             err = ERROR_MISSING_PARAMETER;
           else
           {
             int value = 0;
             sscanf(cmdToken, "%x", &value);

             if (((value < m_commands[i].m_param[j].m_minRange) ||
                  (value > m_commands[i].m_param[j].m_maxRange)) &&
                  (m_commands[i].m_param[j].m_maxRange !=
                   m_commands[i].m_param[j].m_minRange))
             {
               err = ERROR_OUT_OF_RANGE;
             }
             else
               regs->r[j] =value;
           }
           break;
         default:
       //  printf("default %d\n, ", m_commands[i].m_param[j].m_type);
           break;
        }
        ++j;
      }


  return err;
}


int snd = 0;
long store[4] = {0};

_kernel_oserror *rs_cmd(const char *arg_string, int argc, int cmd_no, void *pw)
{
    COMMAND_ERROR error = ERROR_UNKNOWN_COMMAND;
    STAR_COMMAND command;
    _kernel_swi_regs regs;
    const char *s = arg_string;
    int i;
    UNUSED(pw);

    error = GetCommand(cmd_no, arg_string, &command, &regs);

    if (g_verbose)
    {
      switch (error)
      {
         case ERROR_NONE:
          break;
        case ERROR_UNKNOWN_COMMAND:
          printf("Command not implemented\n");
          break;
        case ERROR_MISSING_PARAMETER:
          printf("Missing Parameter\n");
          break;
        case ERROR_OUT_OF_RANGE:
          printf("Parameter value out of range\n");
          break;
        default:
          break;

      }
    }

/*   {
     extern RISC_SYNTH g_synth;
     printf("debug = %d\n", g_synth.debug);
     }*/
     // Next can call SWI implementation
    switch (command)
    {
      case RSTART:
        init_sharedsound();
        break;
      case RSTOP:
        shutdown_sharedsound();
        break;
      case RSOUND:
        HandleRSound(&regs);
        break;
      case RFILTER:
        HandleRFilter(&regs);
        break;
      case RENVELOPE:
        HandleREnvelope(&regs);
        break;
      case RFX:
        HandleRFX(&regs);
        break;
      case RFXMIX:
        HandleRFXMix(&regs);
        break;
      case RPAN:
        // TODO
        break;
      case RDELAY:
        HandleRDelay(&regs);
        break;
      case RLOAD:
      {
        int error =  HandleRLoad(&regs);
        if (g_verbose)
        {
          if (error == 0)
            printf("Sample loaded successfully");
          else
            printf("Sample load failed\n");
        }
        break;
      }
      case RVERBOSE:
        g_verbose = 1;
        break;
      case RLEGACY:
        printf("Legacy mode is not support in this RDSP release.\n");
        break;
      case RADVANCED:
        g_legacy = 0;
        break;
      case RLIST:
        HandleRList();
        break;
      default:
        break;

    }

    return 0;
}

void rs_service(int service_number, _kernel_swi_regs *regs, void *private_word)
{
    UNUSED(private_word);
/*
 *  In general, it is a Bad Thing to use printf inside an arbitrary
 *  service call handler (e.g. in one that catches errors - call 6 -
 *  as this one does. Therefore we merely count what's happening and
 * print it out when the TestCModule's SWI is called.
 */
    if (service_number == UNKNOWN_CMD_SERVICE)
        ++unknown_cmd_count;
    else
      ++other_service_call_count;

    if (regs->r[1] == 8)
    {
      if (regs->r[2] == 8)
      {
        snd++;
        store[0] = ((unsigned char*)regs->r[3])[0];
        store[1] = ((unsigned char*)regs->r[3])[1];
        store[2] = ((unsigned char*)regs->r[3])[2];
      }
    }
}

_kernel_oserror *rs_initialise(const char *cmd_tail, int podule_base, void *private_word)
{
    _kernel_swi_regs regs;
    UNUSED(cmd_tail);
    UNUSED(podule_base);
    //    UNUSED(private_word);

 // Now claim OS WORD 7. Vector is 7 for OS WORD itself
	regs.r[0] = 7;
	regs.r[1] = (int)&rosesound_entry;
	regs.r[2] = (int)private_word;
 	_kernel_swi(OS_Claim,&regs,&regs);

    unknown_cmd_count = other_service_call_count = 0;
    return NULL;
}

_kernel_oserror *rs_finalise(int fatal, int podule_base, void *private_word)
{
    _kernel_swi_regs regs;
    UNUSED(fatal);
    UNUSED(podule_base);
    //    UNUSED(private_word);

 // Now claim OS WORD 7. Vector is 7 for OS WORD itself
	regs.r[0] = 7;
	regs.r[1] = (int)&rosesound_entry;
	regs.r[2] = (int)private_word;
 	_kernel_swi(OS_Release,&regs,&regs);

    g_verbose = 0;
    shutdown_sharedsound();

    unknown_cmd_count = other_service_call_count = 0;
    return NULL;
}

// Handler for OS_WORD 7 & 8
int rosesound_handler( _kernel_swi_regs *r, void *pw)
{


  if (r->r[0] == 7)
  {
      int frequency = 0;
      int amplitude = 0;
      int wave = 0;
      _kernel_swi_regs regs;
      char wavestr[255];
      unsigned char *paramBlock = (unsigned char*)(r->r[1]);
      wavestr[0] = 0;
      // Raise this on a forum thread for discussion on ROOL tomorrow.

      // LSB MSB - sound uses 16 bit values
      // As per Rick's post
      // Make semi-backwards compatible
      // channel flags wave/instrument volume pitch duration


      //
      // pitch can go through all values - If you Start at &0100 you get full
      // range
      // Duration is up to 65536
      //
      // Samples can be indexed by negative pitches
      // So the wave parameter becomes the sample number selector
      // i.e. SOUND &21, &00FF, 100, 100
      // Plays sample 0 at pitch 1 for 1 second
      // To play instrument 1 on channel 1 I can split the last byte
      // So volume can be 16 value logarithmic for negative numbers
      // If TT = 255 Then treat this as square wave for legacy VDU 7 etc
      // SOUND <PFSC>,<TTVV>, <IRNN>, <Duration>
      // P = Pan
      // F = flags &10 = Sync, &20 = mute, &40 = xor
      // S = sound &00 = wave &10 = Envelop, &20 = Sample,
      // I = cutoff (if zero then this is open as optional) bits 16,15,14,13
      // R = resonance bits (12,11,10)
      // N = note bits 1 to 9

      //   n.b. This allows The sample to be used for an instrument
      //        i.e. The sample will loop over the ADSR.
      // C = Channel &01 to &08
      // TT = Tone = Wave, Sample number or instrument
      // VV = volume
      //
      // n.b. revise *RLOAD to <filename> <instrument name> <loop point> <slot>
      // *RLEGACY will set pitch range to start at BBC Micro range
      //          and map envelope parameters to legacy parameters.

    amplitude = paramBlock[2];

    if (paramBlock[0] & 0x20)
    {
      wave = (((int)paramBlock[3]) + 256) & 0x1ff;
      sprintf(wavestr, "%d", wave);
    }
    else
    if (paramBlock[0] & 0x10)
    {
      extern RISC_SYNTH g_synth;

      strcpy(wavestr, g_synth.m_instrument[paramBlock[3]&0xff].m_name);
    }
    else
    {
       // If SOUND channel,-15 or SOUND channel,200 (positive volume)
       // default to square wave
       if ((paramBlock[3] != 0) && (paramBlock[3] != 0xff))
         sprintf(wavestr, "%d", paramBlock[3]);
       else
         strcpy(wavestr, "31");

       if ((paramBlock[3] == 0xff) && (paramBlock[2] > 0xf0))
       {
         amplitude = 255 - (((paramBlock[2] & 0x0f)-1) * 0x10);
       }
    }

    // If SOUND 0 is supplied, then map this to channel 8
    // and choose a matching noise
    if ((paramBlock[0] & 0x0f) == 0)
    {
      int table[] = {212, 164,116,0};
      if ((paramBlock[4] & 0x7) < 4)
      {
        strcpy(wavestr, "8");

      }
      else
      {
        strcpy(wavestr, "128");
      }
      if ((paramBlock[4] & 3) < 3)
      {
        frequency = table[paramBlock[4] & 3];
      }
      else
      {
        // Sync to channel 1
        frequency =g_synth.m_channel[0].m_tone;
      }
    }
    else
    {
      frequency = ((int)paramBlock[4]) | (((int)(paramBlock[5]&1)) << 8);
    }

    regs.r[0] = paramBlock[0] & 0x0f;
    regs.r[1] = (paramBlock[1] & 0x0f) << 4;
    regs.r[2] = (int)wavestr;
    regs.r[3] = amplitude;
    regs.r[4] = frequency;
    // TODO: Consider mapping 255 to 2^31 (i.e. 68 years)
    // TODO: How do I get a module to respond to <interrupt>
    // As per BBC sound command - duration is in 20ths of a second.
    regs.r[5] = ((int)paramBlock[6]) | (((int)paramBlock[7]) << 8) * 5;

    {
      // Not sure if linear is better.
      int cutoffvalue[] = {255,2,3,4,6,8,11,16,23,32,45,64,90,128,181,255};
      int cutoff = cutoffvalue[(paramBlock[5]&0xf0)>>4];
      int res = ((paramBlock[5]&0x0e) >> 1);
      _kernel_swi_regs filterParam;

      filterParam.r[0] = paramBlock[0] & 0x0f;
      filterParam.r[1] = cutoff;
      filterParam.r[2] = res;
      HandleRFilter(&filterParam);
    }

    if (strcmp(wavestr, "") != 0)
      HandleRSound(&regs);


    return 0;
    }

    // OS Word 8 - for the new envelope model and not
    //             for the legacy mode option
    //             <env-number 7 bit. bit 8=0 wave, 1 sample> <wave>
    //             <cutoff |resonance> <options)
    //        <ASR steptime>
    //        <ASR steps section 2>
    //        <ASR steps section 1 & 3>
    //        <ASR delta-attack>
    //        <ASR delta-sustain>
    //        <ASR delta-release>
    //               <Attack> <Decay> <sustain> <release>
    // To fit this in the BASIC command I will trade off some flexibility
    // cut off resonance can be reduced to 4 bit values
    //

    // Just realised. I could make the ADSR proportional to the sound call
    // So ADSR are fractions of total (A+D+S+R) and the duration parameter will
    // do something.
    if (r->r[0] == 8)
    {
 //       if (g_legacy == 0)
        {
          _kernel_swi_regs regs;
          unsigned char *paramBlock = (unsigned char*)(r->r[1]);
          int i;
          char name[255];
          // User just needs to know this is a range of min to max
          // 1.414 ^ n
          int cutoffvalue[] = {1,2,3,4,6,8,11,16,23,32,45,64,90,128,181,255};

          // Could consider an offset value somewhere?
          //

          int cutoff, resonance;
          int options, steptime;
          int attack, decay, sustain, release;
          int adr1step, adr2step, adr3step;
          int adr1delta, adr2delta, adr3delta;
          //for (i = 0; i < 14; ++i)
          //printf("param block = %d = %d", i, paramBlock[i]);
          //printf("\n");

          sprintf(name, "envelope%d", paramBlock[0]);

          cutoff = cutoffvalue[((paramBlock[2] & 0xf0) >> 4)];
          resonance = paramBlock[2] & 0x0f;

          options = paramBlock[3];
          steptime = paramBlock[4];

          adr1step = paramBlock[5];
          adr2step = paramBlock[6];
          adr3step = paramBlock[5];
          adr1delta = paramBlock[7];
          adr2delta = paramBlock[8];
          adr3delta = paramBlock[9];

          attack = paramBlock[10];
          decay = paramBlock[11];
          sustain = paramBlock[12];
          release = paramBlock[13];

          regs.r[0] = (paramBlock[0] & 0x7F);
          regs.r[1] = (int)name;
          regs.r[2] = paramBlock[1] + (((int)paramBlock[0] & 0x80)<<8) ;
          regs.r[3] = (cutoff << 8) | resonance;
          regs.r[4] = ((int)(options << 8)) | (int)steptime;
          regs.r[5] = (adr1step << 16) | (adr2step << 8) | adr1step;
          regs.r[6] = (adr1delta << 16) | (adr2delta << 8) | adr3delta;
          regs.r[7] = (attack << 24) | (decay << 16) | (sustain << 8) | release;
          HandleREnvelope(&regs);
        }
//        else
  /*      {
          // I need to re-interpret further - so do not deliver legacy
          // mode in 1st release of alpha.
          //
          extern RISC_SYNTH g_synth;
          unsigned char *paramBlock = (unsigned char*)(r->r[1]);
          INSTRUMENT_TABLE *instrument = (g_synth.m_instrument)+paramBlock[0];
          int i;
          char name[255];

          int steptime;
          int options = 0;
          int attack, decay, sustain, release;
          int adr1step, adr2step, adr3step;
          int adr1delta, adr2delta, adr3delta;

          sprintf(name, "envelope%d", paramBlock[0]);

          if (paramBlock[1] & 128)
            options = 0;
          else
            options = 0x80;

          // Legacy mode locks the 3 stage envelope to pitch
          options += 0x10;

          steptime = paramBlock[1] & 0x7f;

          if ((name != NULL) && (strlen(name) < 15))
          {
            strcpy(instrument->m_name, name);
          }

          instrument->m_wave = 0x1f;
          instrument->m_options = options;
          instrument->m_steptime = steptime;
          instrument->m_cutoff = 255;
          instrument->m_res = 0;

          adr1step = paramBlock[5];
          adr2step = paramBlock[6];
          adr3step = paramBlock[7];
          adr1delta = paramBlock[2];
          adr2delta = paramBlock[3];
          adr3delta = paramBlock[4];

          instrument->m_asrStepsAttack = paramBlock[5];
          instrument->m_asrStepsSustain = paramBlock[6];
          instrument->m_asrStepsRelease = paramBlock[7];
          instrument->m_asrAttackDelta =  ((signed char)paramBlock[2]);
          instrument->m_asrSustainDelta = ((signed char)paramBlock[3]);
          instrument->m_asrReleaseDelta = ((signed char)paramBlock[4]);
          instrument->m_attack  = (unsigned char)paramBlock[12];
          instrument->m_decay   = (unsigned char)paramBlock[13];
          instrument->m_sustain = 0x80;
          instrument->m_release = ((signed char)paramBlock[11]);

        }*/
    }
  return 1;
}

int main(int argc, char *argv[])
{
    int i;

    // TODO possibly disallow this.
    printf("%s: run code entered - ", TM);
    if (argc == 0)
        printf("no args\n");
    else
    {
        for (i = 0; i < argc; i++) printf(" %s", argv[i]);
        printf("\n");
    }
    return 0;
}

