#ifndef AUDIO_C //AUDIO_C
#define AUDIO_C //AUDIO_C

#include "audio.h"

#define PWMAL (9)
#define PWMAR (8)

unsigned int sliceAL,sliceAR,chanAL,chanAR;
unsigned int freqdiv=11;  //11=>44100ish
volatile const char* nowData;
volatile const char* nextData;
volatile unsigned int nowSize, nextSize, nowPtr;
unsigned int audioState=0;

unsigned int audioSetRate(unsigned int s){ //returns actual rate
  unsigned int pwmf= clock_get_hz(clk_sys)/256;
  if(s>0){
    freqdiv=(pwmf+(s/2))/s; //+s/2 gets us nearest rounding
  }
  if(freqdiv<1){freqdiv=1;}
  return pwmf/freqdiv;
}

unsigned int audioBytes(){  //number of bytes left to play
  unsigned int r=0;
  if(nextData && nextSize){   //if data in second
    r=nextSize;
  }
  if(nowData && nowSize){   //continue if valid    
    return nowSize-nowPtr+r;
  }
  return 0; //nothing to play if current buffer is empty
}

unsigned int audioSpace(){ //is there space to Queue?
  if(nextData && nextSize){   //if data in second, no room
    return 0;
  }else{
    return 1;
  }
}

unsigned int audioQueue(const char* b, unsigned int s){
  if(audioState==AUDIO_STOPPED){
    if(nowData && nowSize){   //if data in 1st
      if(nextData && nextSize){   //if data in second, no room
        return 0;
      }else{
         nextData=b;
         nextSize=s;
      }
    }else{
      nowData=b;
      nowSize=s;            
    }
  }else{
    if(nextData && nextSize){   //if data in second, no room
      return 0;
    }else{
       nextData=b;
       nextSize=s;
    }
  }
  return s;   //s bytes queued
}

unsigned int audioPlay(unsigned int s){  //takes 1/2 mono/stereo, will resume current state if paused
  if(audioState==AUDIO_STOPPED){
    if(nowData && nowSize){   //continue if valid    
      if(s==AUDIO_PLAY_MONO){audioState=AUDIO_PLAY_MONO;}
      if(s==AUDIO_PLAY_STEREO){audioState=AUDIO_PLAY_STEREO;}
    }
  }else{
    if(audioState==AUDIO_PAUSE_MONO){audioState=AUDIO_PLAY_MONO;}
    if(audioState==AUDIO_PAUSE_STEREO){audioState=AUDIO_PLAY_STEREO;}
  }
  return audioState;  //return resulting state
}

void audioStop(){ //stop and put everything in an empty state
  audioState=AUDIO_STOPPED;  
  nowData=0;
  nowSize=0;
  nowPtr=0;
  nextData=0;
  nextSize=0;
}

unsigned int audioPause(){
  if(audioState==AUDIO_PLAY_MONO){audioState=AUDIO_PAUSE_MONO;}
  if(audioState==AUDIO_PLAY_STEREO){audioState=AUDIO_PAUSE_STEREO;}
  return audioState;  //return resulting state  
}

void audioInit(){
//set null
  nowData=0;
  nextData=0;
  audioState=AUDIO_STOPPED;
  freqdiv=11;  //11=>44100ish
//use C SDK to get access to interrupts
  gpio_set_function(PWMAL, GPIO_FUNC_PWM);
  gpio_set_function(PWMAR, GPIO_FUNC_PWM);
  //hardware could have the same PWM slice, but we won't assume it
  sliceAL=pwm_gpio_to_slice_num(PWMAL);
  sliceAR=pwm_gpio_to_slice_num(PWMAR);
  chanAL=pwm_gpio_to_channel(PWMAL);
  chanAR=pwm_gpio_to_channel(PWMAR);
  //use these globals to init irq using left PWM slice
  pwm_clear_irq(sliceAL);
  pwm_set_irq_enabled(sliceAL, true);
  irq_set_exclusive_handler(PWM_IRQ_WRAP, audioPWMINT);
  irq_set_enabled(PWM_IRQ_WRAP, true);
  //set up PWM output
  pwm_set_wrap(sliceAL, 255);
  pwm_set_wrap(sliceAR, 255);
  pwm_set_clkdiv_int_frac(sliceAL,1,0); //use highest available freq and divide down in IRQ
  pwm_set_clkdiv_int_frac(sliceAR,1,0);
  pwm_set_chan_level(sliceAL, chanAL, 0);    //off
  pwm_set_chan_level(sliceAR, chanAR, 0);    //off
  pwm_set_enabled(sliceAL, true);    //PWM running
  pwm_set_enabled(sliceAR, true);    //PWM running
}

void audioPWMINT(){
  static char level=0;
  static unsigned int fcount=0;
  static const char wave[]={0,0,0,0,0
    //0,10,20,30,40,50,60,70,80,90,100,110,120,130,140,150,160,170,180,190,200,210,220,230,240,250,
    //240,230,220,210,200,190,180,170,160,150,140,130,120,110,100,90,80,70,60,50,40,30,20,10
  };  
  pwm_clear_irq(sliceAL);  
  fcount++;
  if(fcount>=freqdiv){
    fcount=0;
  level++;
  if(level>=sizeof(wave)){level=0;}
    if(nowData){  //valid data
      if(audioState==AUDIO_PLAY_MONO){
        pwm_set_chan_level(sliceAL, chanAL, nowData[nowPtr]);
        pwm_set_chan_level(sliceAR, chanAR, nowData[nowPtr++]);  
      }else if(audioState==AUDIO_PLAY_STEREO){
        pwm_set_chan_level(sliceAL, chanAL, nowData[nowPtr++]);
        pwm_set_chan_level(sliceAR, chanAR, nowData[nowPtr++]);  
      }
      if(nowPtr>=nowSize){
        if(nextData && nextSize){   //continue if valid
          nowData=nextData;
          nowSize=nextSize;
          nowPtr=0;       
          nextData=0;
          nextSize=0;
        }else{
          nowData=0;
          nowSize=0;
          nowPtr=0;
          audioState=AUDIO_STOPPED;
        }
      }
    }
  }
}

#endif  //AUDIO_C
