



#include "Eagle/backends/Allegro5Backend.hpp"
#include "Eagle.hpp"


/// Data(t) = Amplitude(t)*waveform(t)


typedef float (*WAVEFORM) (float);
typedef float (*VOLUMEFORM) (float);
typedef float (*FREQFORM) (float);
typedef float (*AUDIOFORM) (float);

float SINWAVE (float pct) {
   return sin(pct*2*M_PI);
}
float TRIANGLEWAVE (float pct) {
   /// maps percent [0.0f,1.0f] to /\\/ triangle wave
   pct = fmod(pct , 1.0);
   if (pct <= 0.25) {
      return 0.0f + 1*pct*4.0f;
   }
   else if (pct <= 0.75) {
      return 1.0f + (-2.0f*(pct - 0.25f)*2.0f);
   }
   else {
      return -1.0f + 1.0f*(pct - 0.75f);
   }
   return 0.0f;/// Not reached
}
float SQUAREWAVE (float pct) {
   pct = fmod(pct , 1.0);
   return (pct <= 0.5)?1.0:-1.0;
}


float RAMP (float pct) {
   pct = fmod(pct , 1.0f);
   return pct*pct*pct;
}

float RAMP2 (float pct) {
   pct = fmod(pct , 1.0f);
   pct = 1.0 + 99.0f*pct;
   return log10(pct)/2.0f;
}

float DAMP (float pct) {
   pct = fmod(pct , 1.0f);
   return (1.0f - pct)*(1.0f - pct);
}

float HALFVOL(float pct) {return 0.5f;}

float RAMPANDDAMP (float pct) {
   pct = fmod(pct , 1.0f);
   if (pct <= 0.5f) {
      return RAMP(pct*2.0f);
   }
   else {
      return DAMP(pct*2.0f - 1.0f);
   }
   return 0.0f;/// Not reached
}



void waveform(float* buffer, int32_t samples, float time , float vfreq) {
  for(int32_t i = 0; i < samples; ++i) {
    float ti = time + i/vfreq;//pt * samples/vfreq;

//    buffer[i] = RAMP(fmod(ti,5.0f)/5.0)*TRIANGLEWAVE(440.0f*ti*2.0*M_PI);
    buffer[i] = SINWAVE((220.0f + 220.0f*DAMP(ti/7.0f))*ti*2.0f*M_PI);
  }
}


float FREQ220(float pct) {return 220.0f + 110.0f*RAMP(pct);}

template<class Type>
class Interp {
public :
   Type start;
   Type finish;
   
   Interp(Type begin , Type end) :
      start(begin),
      finish(end)
   {
      
   }
//   void operator(float pct);
   Type operator()(float pct) {
      pct = fmod(pct , 1.0f);
      return start + (finish-start)*pct;
   }
};
             
class Audio {
   int buffer_fragment_count;///< Fragment count
   int samples_per_buffer;///< Sample count
   float samples_per_second;///< Mixer frequency
   double secs_per_sample;///< Duration per sample

   double sound_duration;
   
   VOLUMEFORM volform;
   WAVEFORM waveform;
   FREQFORM freqform;

   double volume;
   double freq;
   
public :
   Audio() :
         buffer_fragment_count(8),
         samples_per_buffer(1024),
         samples_per_second(44100.0f),
         secs_per_sample(1.0/samples_per_second),
         sound_duration(5.0),
         volform(HALFVOL),
         waveform(SINWAVE),
         freqform(FREQ220),
         volume(0.0),
         freq(220.0)
   {}
   
   
//   void FillBuffer(float* buf , float audiotime);
   void FillBuffer(float* buf , float audiotime) {
      double ti = audiotime;
      for (unsigned int i = 0 ; i < (unsigned int)samples_per_buffer ; ++i) {
//         buf[i] = waveform(freqform(ti/sound_duration)*ti*2.0*M_PI/sound_duration);
//         buf[i] = waveform(220.0f*ti*2.0*M_PI);
//         buf[i] = waveform(Interp<float>(220.0f , 330.0f)(ti/sound_duration)*ti*2.0*M_PI);
///         buf[i] = Interp<float>(SINWAVE(220.0f*ti*2.0f*M_PI/3.0f) , SQUAREWAVE(220.0f*ti*2.0f*M_PI/3.0f))(ti/3.0f);
         buf[i] = volume*waveform(freq*2.0*M_PI*ti);
         ti += secs_per_sample;
      }
   }
   void SetVolume(double pct) {
      if (pct < 0.0) {pct = 0.0;}
      if (pct > 1.0) {pct = 1.0;}
      volume = pct;
   }
   void SetFrequency(double hz) {
      freq = hz;
   }
   void SetWaveForm(WAVEFORM wf) {
      waveform = wf;
   }
};


#include <iostream>


int main(int argc , char** argv) {
   
   (void)argc;
   (void)argv;
   
   
   SendOutputToFile("sfx.log.txt" , "" , false);
   EagleLogger::RemoveOutputStream(std::cout);
   
   Allegro5System* a5sys = GetAllegro5System();
   
   if (a5sys->Initialize(EAGLE_STANDARD_SETUP) != EAGLE_STANDARD_SETUP) {
      return -1;
   }
   
   ALLEGRO_VOICE* voice = al_create_voice(44100 , ALLEGRO_AUDIO_DEPTH_INT16 , ALLEGRO_CHANNEL_CONF_2);
   ALLEGRO_MIXER* mixer = al_create_mixer(44100 , ALLEGRO_AUDIO_DEPTH_INT16 , ALLEGRO_CHANNEL_CONF_2);
   ALLEGRO_AUDIO_STREAM* stream = al_create_audio_stream(8 , 1024 , 44100 , ALLEGRO_AUDIO_DEPTH_FLOAT32 , ALLEGRO_CHANNEL_CONF_1);
   
   if (!voice || !mixer || !stream) {
      EagleLog() << "Setup error" << std::endl;
      if (!voice) {
         EagleWarn() << "Failed to setup voice" << std::endl;
         return -1;
      }
      if (!mixer) {
         EagleWarn() << "Failed to setup mixer" << std::endl;
         return -2;
      }
      if (!stream) {
         EagleWarn() << "Failed to setup stream" << std::endl;
         return -3;
      }
      
   }

   bool mixer_attached = al_attach_mixer_to_voice(mixer, voice);

   if (!mixer_attached) {
      EagleWarn() << "Failed to attach mixer to voice" << std::endl;
      return -4;
   }
   
   bool stream_attached = al_attach_audio_stream_to_mixer(stream, mixer);

   if (!stream_attached) {
      EagleWarn() << "Failed to attach stream to mixer" << std::endl;
      return -5;
   }
   
   if (al_get_audio_stream_fragments(stream) != 8) {
      EagleLog() << "Didn't get 8 fragments.\n";
   }
   if (al_get_audio_stream_channels(stream) != ALLEGRO_CHANNEL_CONF_1) {
      EagleLog() << "Not a mono stream!\n";
   }
   if (!al_get_audio_stream_attached(stream)) {
      EagleLog() << "Stream not attached!\n";
   }
   if (al_get_audio_stream_frequency(stream) != 44100) {
      EagleLog() << "Not mixing at 44100 HZ!\n";
   }
   
   int sw = 800;
   int sh = 600;
   
   EagleGraphicsContext* win = a5sys->CreateGraphicsContext("Main window" , sw , sh , EAGLE_OPENGL | EAGLE_WINDOWED);
   
   WidgetHandler gui(win);
   gui.SetWidgetArea(WIDGETAREA(Rectangle(0 , 0 , sw , sh) , false));
   gui.SetupBuffer(sw,sh , win);

   RelativeLayout rlayout;
   gui.SetRootLayout(&rlayout);

   Slider sl1,sl2,sl3;
   rlayout.AddWidget(&sl1 , LayoutRectangle(0.05 , 0.2 , 0.05 , 0.5));
   rlayout.AddWidget(&sl2 , LayoutRectangle(0.35 , 0.2 , 0.05 , 0.5));
   rlayout.AddWidget(&sl3 , LayoutRectangle(0.65 , 0.2 , 0.05 , 0.5));
   
   EagleFont* f = win->DefaultFont();
   
   BasicText lbl1,lbl2,lbl3;
   lbl1.SetText("VOL" , f);
   lbl2.SetText("HZ" , f);
   lbl3.SetText("WAVE" , f);
   
   rlayout.AddWidget(&lbl1 , LayoutRectangle(0.05 , 0.02 , 0.3 , 0.1));
   rlayout.AddWidget(&lbl2 , LayoutRectangle(0.35 , 0.02 , 0.3 , 0.1));
   rlayout.AddWidget(&lbl3 , LayoutRectangle(0.65 , 0.02 , 0.3 , 0.1));
   
   BasicText t1,t2,t3;
   t1.SetText("" , f);
   t2.SetText("" , f);
   t3.SetText("" , f);

   rlayout.AddWidget(&t1 , LayoutRectangle(0.05 , 0.12 , 0.3 , 0.25));
   rlayout.AddWidget(&t2 , LayoutRectangle(0.35 , 0.12 , 0.3 , 0.25));
   rlayout.AddWidget(&t3 , LayoutRectangle(0.65 , 0.12 , 0.3 , 0.25));
   
   
   
   Audio audio;
   
   double vol = 0.5;
   double  freq = 220.0f;
   WAVEFORM wf = SINWAVE;
   
   audio.SetVolume(vol);
   audio.SetFrequency(freq);
   audio.SetWaveForm(wf);
   
   sl1.SetPercent(0.5);
   sl2.SetPercent(0.25);
   sl3.SetPercent(0.0);
   
   t1.SetText("0.5");
   t2.SetText("220.0");
   t3.SetText("SINWAVE");
   
   double start = ProgramTime::Now();
   double time = start;
   double audiotime = 0.0;

   bool quit = false;
   bool redraw = true;

   EagleEventHandler* sysqueue = a5sys->GetSystemQueue();
   
   sysqueue->ListenTo(&gui);
   
   a5sys->GetSystemTimer()->Start();
   
   
   while(!quit) {
      if (redraw) {
         win->Clear();
         gui.Display(win , 0 , 0);
         win->FlipDisplay();
      }
      
      do {
         EagleEvent e = a5sys->WaitForSystemEventAndUpdateState();
         if (e.type == EAGLE_EVENT_TIMER) {
            /// Check if the audio stream is empty
            void* buffer = 0;
            while ((buffer = al_get_audio_stream_fragment(stream))) {
               EagleLog() << "FillingBuffer...";
               audio.FillBuffer((float*)buffer , audiotime);
               audiotime += 1024.0/44100.0;
               al_set_audio_stream_fragment(stream , buffer);
            }
            gui.Update(e.timer.eagle_timer_source->SPT());
            redraw = true;
         }
         if (e.type == EAGLE_EVENT_DISPLAY_CLOSE) {
            quit = true;
         }
         if (e.type == EAGLE_EVENT_KEY_DOWN && e.keyboard.keycode == EAGLE_KEY_ESCAPE) {
            quit = true;
         }
         if (e.type == EAGLE_EVENT_WIDGET) {
            WIDGET_EVENT_DATA d = e.widget;
            if (d.from == &sl1 || d.from == &sl2 || d.from == &sl3) {
               if (d.topic == TOPIC_SLIDER) {
                  if (d.msgs == SLIDER_VALUE_CHANGED) {
                     if (d.from == &sl1) {
                        vol = sl1.GetPercent();
                        audio.SetVolume(vol);
                        t1.SetText(StringPrintF("VOL %1.4lf" , vol));
                     }
                     if (d.from == &sl2) {
                        freq = 110.0 + 440.0*sl2.GetPercent();
                        audio.SetFrequency(freq);
                        t2.SetText(StringPrintF("HZ %1.4lf" , freq));
                     }
                     if (d.from == &sl3) {
                        double val = sl3.GetPercent();
                        if (val <= 0.34) {
                           wf = SINWAVE;
                           t3.SetText("SINWAVE");
                        }
                        else if (val <= 0.68) {
                           wf = TRIANGLEWAVE;
                           t3.SetText("TRIWAVE");
                        }
                        else {
                           wf = SQUAREWAVE;
                           t3.SetText("SQRWAVE");
                        }
                        audio.SetWaveForm(wf);
                     }
                  }
               }
            }
         }
         else {
            gui.HandleEvent(e);
         }
      } while (sysqueue->HasEvent());
   }



   return 0;
}
