//      sourceaudio.c
//      
//      Copyright 2012 Richard Shann <rshann@debian2>
//      
//      This program is free software; you can redistribute it and/or modify
//      it under the terms of the GNU General Public License as published by
//      the Free Software Foundation; either version 2 of the License, or
//      (at your option) any later version.
//      
//      This program is distributed in the hope that it will be useful,
//      but WITHOUT ANY WARRANTY; without even the implied warranty of
//      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//      GNU General Public License for more details.
//      
//      You should have received a copy of the GNU General Public License
//      along with this program; if not, write to the Free Software
//      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
//      MA 02110-1301, USA.


#include <stdio.h>
#include <sndfile.h>
#include <fcntl.h>
#include "view.h"
#include "midi.h"
#include "sourceaudio.h"
#include "keyresponses.h"
#include "audiointerface.h"
#if GTK_MAJOR_VERSION==3
#include <gdk/gdkkeysyms-compat.h>      //FIXME Look for something more gtk3 like
#endif

static gint leadin = 0;         //number of frames of silence before playing audio
static gboolean playing = FALSE;

gboolean
get_audio_sample (float *sample)
{
  if (!playing)
    return FALSE;
  gboolean ret = FALSE;
  if (leadin)
    {
      *sample = *(sample + 1) = 0.0;
      leadin--;
      ret = TRUE;
    }
  else
    {
      if (Denemo.gui->si && Denemo.gui->si->audio && Denemo.gui->si->audio->sndfile)
        {
          ret = (2 == sf_read_float (Denemo.gui->si->audio->sndfile, sample, 2));
          if (ret)
            *sample *= Denemo.gui->si->audio->volume;
          *(sample + 1) *= Denemo.gui->si->audio->volume;
        }
    }
  return ret;
}

gboolean
open_source_audio (gchar * filename)
{
  SF_INFO sfinfo;
  DenemoAudio *temp;
  sfinfo.format = 0;
  //FIXME a better name for the mutex which originally was just for midi data, but will work for audio data too.
  if (Denemo.gui->si->audio && Denemo.gui->si->audio->sndfile)
    {
      temp = Denemo.gui->si->audio;
      g_static_mutex_lock (&smfmutex);
      Denemo.gui->si->audio = NULL;
      g_static_mutex_unlock (&smfmutex);
      sf_close (temp->sndfile);
      g_free (temp->filename);
      g_free (temp);

    }
  if (filename)
    {
      gpointer sndfile = sf_open (filename, SFM_READ, &sfinfo);
      if (sndfile)
        {
          temp = (DenemoAudio *) g_malloc (sizeof (DenemoAudio));
          temp->sndfile = sndfile;
          temp->filename = g_strdup (filename);
          temp->samplerate = sfinfo.samplerate;
          temp->channels = sfinfo.channels;
          g_print ("sndfile: %s sample rate is %d channels %d containing %d \n", 
                   sf_strerror (temp->sndfile), 
                   sfinfo.samplerate, 
                   sfinfo.channels, 
                   (int) sf_seek (temp->sndfile, -1, SEEK_END));
          //FIXME warn if samplerate != Denemo.prefs.samplerate
          temp->volume = 1.0;
          g_static_mutex_lock (&smfmutex);
          Denemo.gui->si->audio = temp;
          g_static_mutex_unlock (&smfmutex);
          update_leadin_widget (-1.0);
        }
    }
  Denemo.gui->si->audio ? gtk_widget_show (Denemo.audio_vol_control) : gtk_widget_hide (Denemo.audio_vol_control);
  return (Denemo.gui->si->audio != NULL);
}

gboolean
close_source_audio (void)
{
  gboolean ret = (Denemo.gui->si->audio != NULL);
  (void) open_source_audio (NULL);
  return ret;
}

void
rewind_audio (void)
{
  if (Denemo.gui->si->audio)
    {
      if (Denemo.gui->si->audio->sndfile == NULL)
        {
          gint leadin = Denemo.gui->si->audio->leadin;  /* not part of the audio file itself */
          open_source_audio (Denemo.gui->si->audio->filename);
          if (Denemo.gui->si->audio->samplerate)
            {
              Denemo.gui->si->audio->leadin = leadin;
              update_leadin_widget (((double) leadin) / Denemo.gui->si->audio->samplerate);
            }
        }
      gdouble start = get_start_time ();
      if (start < 0.0)
        start = 0.0;
      gint startframe = start * Denemo.gui->si->audio->samplerate;
      startframe += Denemo.gui->si->audio->leadin;
      if (startframe < 0)
        {
          leadin = -startframe;
          startframe = 0;
        }
      else
        leadin = 0;
      sf_seek (Denemo.gui->si->audio->sndfile, startframe, SEEK_SET);
    }
  else
    gtk_widget_hide (Denemo.audio_vol_control);
}

gboolean
set_lead_in (gdouble secs)
{
  if (Denemo.gui->si->audio)
    {
      if (Denemo.gui->si->audio->sndfile == NULL)
        open_source_audio (Denemo.gui->si->audio->filename);
      if (Denemo.gui->si->audio->sndfile)
        {
          Denemo.gui->si->audio->leadin = secs * Denemo.gui->si->audio->samplerate;
          return TRUE;
        }
    }
  return FALSE;
}




gboolean
audio_is_playing (void)
{
  return playing;
}



static gboolean annotating = FALSE;
static GQueue *timings = NULL;  //list of measure start times in microseconds from start, as indicated by the user.
static gint
record_timing (GtkWidget * widget, GdkEventKey * event)
{
  if (event->keyval == GDK_Escape)
    {
      call_out_to_guile ("(if (defined? 'DenemoAudioAnnotateFinished) (DenemoAudioAnnotateFinished))");
      stop_audio_playing ();
    }
  else
    {
      g_queue_push_tail (timings, GINT_TO_POINTER ((gint) (1000000 * get_playback_time ())));
      call_out_to_guile ("(DenemoAudioAnnotate)");
    }
  return TRUE;
}

gdouble
get_audio_timing (void)
{
  gdouble ret = -1.0;
  if (!g_queue_is_empty (timings))
    {
      ret = (GPOINTER_TO_INT (g_queue_pop_head (timings))) / 1000000.0;
    }
  return ret;
}

void
start_audio_playing (gboolean annotate)
{
  if (annotate)
    {
      annotating = TRUE;
      if (timings)
        g_queue_clear (timings);
      else
        timings = g_queue_new ();
      g_signal_connect (G_OBJECT (Denemo.scorearea), "key_press_event", G_CALLBACK (record_timing), NULL);
      g_signal_handlers_block_by_func (G_OBJECT (Denemo.scorearea), G_CALLBACK (scorearea_keypress_event), NULL);
      g_signal_handlers_block_by_func (G_OBJECT (Denemo.scorearea), G_CALLBACK (scorearea_button_press), NULL);
      g_signal_handlers_block_by_func (G_OBJECT (Denemo.scorearea), G_CALLBACK (scorearea_button_release), NULL);


    }
  rewind_audio ();

  initialize_until_time ();
  audio_play ();
  playing = TRUE;
}

void
stop_audio_playing (void)
{
  playing = FALSE;
  if (annotating)
    {
      annotating = FALSE;
      g_signal_handlers_disconnect_by_func (G_OBJECT (Denemo.scorearea), record_timing, NULL);
      g_signal_handlers_unblock_by_func (G_OBJECT (Denemo.scorearea), G_CALLBACK (scorearea_keypress_event), NULL);
      g_signal_handlers_unblock_by_func (G_OBJECT (Denemo.scorearea), G_CALLBACK (scorearea_button_press), NULL);
      g_signal_handlers_unblock_by_func (G_OBJECT (Denemo.scorearea), G_CALLBACK (scorearea_button_release), NULL);
    }
}

gboolean
open_source_audio_file (void)
{
  gboolean ret = FALSE;
  GtkWidget *dialog = gtk_file_chooser_dialog_new ("Open Audio Source File",
                                                   NULL,
                                                   GTK_FILE_CHOOSER_ACTION_OPEN,
                                                   GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                                                   GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
                                                   NULL);
  if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
    {
      char *filename;
      filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
      ret = open_source_audio (filename);
      g_free (filename);
    }
  gtk_widget_destroy (dialog);
  return ret;
}
