/* GSequencer - Advanced GTK Sequencer
 * Copyright (C) 2005-2015 Joël Krähemann
 *
 * This file is part of GSequencer.
 *
 * GSequencer 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 3 of the License, or
 * (at your option) any later version.
 *
 * GSequencer 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 GSequencer.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <ags/audio/recall/ags_play_audio_file.h>

#include <ags/object/ags_connectable.h>

#include <ags/audio/ags_recall_id.h>

#include <ags/i18n.h>

void ags_play_audio_file_class_init(AgsPlayAudioFileClass *play_audio_file);
void ags_play_audio_file_connectable_interface_init(AgsConnectableInterface *connectable);
void ags_play_audio_file_init(AgsPlayAudioFile *play_audio_file);
void ags_play_audio_file_set_property(GObject *gobject,
				      guint prop_id,
				      const GValue *value,
				      GParamSpec *param_spec);
void ags_play_audio_file_get_property(GObject *gobject,
				      guint prop_id,
				      GValue *value,
				      GParamSpec *param_spec);
void ags_play_audio_file_connect(AgsConnectable *connectable);
void ags_play_audio_file_disconnect(AgsConnectable *connectable);
void ags_play_audio_file_finalize(GObject *gobject);

void ags_play_audio_file_run_inter(AgsRecall *recall);
void ags_play_audio_file_remove(AgsRecall *recall);
void ags_play_audio_file_cancel(AgsRecall *recall);

enum{
  PROP_0,
  PROP_SOUNDCARD,
  PROP_AUDIO_FILE,
  PROP_CURRENT_FRAME,
};

static gpointer ags_play_audio_file_parent_class = NULL;
static AgsConnectableInterface *ags_play_audio_file_parent_connectable_interface;

GType
ags_play_audio_file_get_type()
{
  static GType ags_type_play_audio_file = 0;

  if(!ags_type_play_audio_file){
    static const GTypeInfo ags_play_audio_file_info = {
      sizeof (AgsPlayAudioFileClass),
      NULL, /* base_init */
      NULL, /* base_finalize */
      (GClassInitFunc) ags_play_audio_file_class_init,
      NULL, /* class_finalize */
      NULL, /* class_data */
      sizeof (AgsPlayAudioFile),
      0,    /* n_preallocs */
      (GInstanceInitFunc) ags_play_audio_file_init,
    };

    static const GInterfaceInfo ags_connectable_interface_info = {
      (GInterfaceInitFunc) ags_play_audio_file_connectable_interface_init,
      NULL, /* interface_finalize */
      NULL, /* interface_data */
    };

    ags_type_play_audio_file = g_type_register_static(AGS_TYPE_RECALL,
						      "AgsPlayAudioFile",
						      &ags_play_audio_file_info,
						      0);

    g_type_add_interface_static(ags_type_play_audio_file,
				AGS_TYPE_CONNECTABLE,
				&ags_connectable_interface_info);
  }

  return (ags_type_play_audio_file);
}

void
ags_play_audio_file_class_init(AgsPlayAudioFileClass *play_audio_file)
{
  GObjectClass *gobject;
  AgsRecallClass *recall;
  GParamSpec *param_spec;

  ags_play_audio_file_parent_class = g_type_class_peek_parent(play_audio_file);

  /* GObjectClass */
  gobject = (GObjectClass *) play_audio_file;

  gobject->set_property = ags_play_audio_file_set_property;
  gobject->get_property = ags_play_audio_file_get_property;

  gobject->finalize = ags_play_audio_file_finalize;

  /* properties */
  param_spec = g_param_spec_gtype("soundcard",
				  i18n_pspec("assigned soundcard"),
				  i18n_pspec("The soundcard this recall is assigned to"),
				  G_TYPE_OBJECT,
				  G_PARAM_READABLE | G_PARAM_WRITABLE);
  g_object_class_install_property(gobject,
				  PROP_SOUNDCARD,
				  param_spec);

  param_spec = g_param_spec_gtype("audio_file",
				  i18n_pspec("assigned audio file"),
				  i18n_pspec("The audio file this recall is assigned to"),
				  G_TYPE_OBJECT,
				  G_PARAM_READABLE | G_PARAM_WRITABLE);
  g_object_class_install_property(gobject,
				  PROP_AUDIO_FILE,
				  param_spec);

  param_spec = g_param_spec_gtype("current",
				  i18n_pspec("current frame"),
				  i18n_pspec("The current frame this recall is playing"),
				  G_TYPE_UINT,
				  G_PARAM_READABLE | G_PARAM_WRITABLE);
  g_object_class_install_property(gobject,
				  PROP_CURRENT_FRAME,
				  param_spec);

  /* AgsRecallClass */
  recall = (AgsRecallClass *) play_audio_file;
  
  recall->run_inter = ags_play_audio_file_run_inter;
  recall->remove = ags_play_audio_file_remove;
  recall->cancel = ags_play_audio_file_cancel;
}

void
ags_play_audio_file_connectable_interface_init(AgsConnectableInterface *connectable)
{
  ags_play_audio_file_parent_connectable_interface = g_type_interface_peek_parent(connectable);

  connectable->connect = ags_play_audio_file_connect;
  connectable->disconnect = ags_play_audio_file_disconnect;
}

void
ags_play_audio_file_init(AgsPlayAudioFile *play_audio_file)
{
  play_audio_file->audio_file = NULL;
  play_audio_file->current_frame = 0;

  play_audio_file->soundcard = NULL;
}

void
ags_play_audio_file_set_property(GObject *gobject,
				 guint prop_id,
				 const GValue *value,
				 GParamSpec *param_spec)
{
  AgsPlayAudioFile *play_audio_file;

  play_audio_file = AGS_PLAY_AUDIO_FILE(gobject);

  switch(prop_id){
  case PROP_SOUNDCARD:
    {
      GObject *soundcard;

      soundcard = (GObject *) g_value_get_object(value);

      if(play_audio_file->soundcard == soundcard)
	return;

      if(play_audio_file->soundcard != NULL)
	g_object_unref(play_audio_file->soundcard);

      if(soundcard != NULL)
	g_object_ref(soundcard);

      play_audio_file->soundcard = soundcard;
    }
    break;
  case PROP_AUDIO_FILE:
    {
      AgsAudioFile *audio_file;

      audio_file = (AgsAudioFile *) g_value_get_object(value);

      if(play_audio_file->audio_file == audio_file)
	return;

      if(play_audio_file->audio_file != NULL)
	g_object_unref(play_audio_file->audio_file);

      if(play_audio_file != NULL)
	g_object_ref(play_audio_file);

      play_audio_file->audio_file = audio_file;
    }
    break;
  case PROP_CURRENT_FRAME:
    {
      play_audio_file->current_frame = (guint) g_value_get_uint(value);

      /*
       * TODO:JK: implement seeking over the buffer
       */
    }
    break;
  default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
    break;
  }
}

void
ags_play_audio_file_get_property(GObject *gobject,
				 guint prop_id,
				 GValue *value,
				 GParamSpec *param_spec)
{
  AgsPlayAudioFile *play_audio_file;

  play_audio_file = AGS_PLAY_AUDIO_FILE(gobject);

  switch(prop_id){
  case PROP_SOUNDCARD:
    {
      g_value_set_object(value, play_audio_file->soundcard);
    }
    break;
  case PROP_AUDIO_FILE:
    {
      g_value_set_object(value, play_audio_file->audio_file);
    }
    break;
  case PROP_CURRENT_FRAME:
    {
      g_value_set_uint(value, play_audio_file->current_frame);
    }
    break;
  default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
    break;
  }
}

void
ags_play_audio_file_connect(AgsConnectable *connectable)
{
  ags_play_audio_file_parent_connectable_interface->connect(connectable);

  /* empty */
}

void
ags_play_audio_file_disconnect(AgsConnectable *connectable)
{
  ags_play_audio_file_parent_connectable_interface->disconnect(connectable);

  /* empty */
}

void
ags_play_audio_file_finalize(GObject *gobject)
{
  AgsPlayAudioFile *play_audio_file;

  play_audio_file = AGS_PLAY_AUDIO_FILE(gobject);

  g_object_unref(G_OBJECT(play_audio_file->audio_file));

  g_object_unref(G_OBJECT(play_audio_file->soundcard));

  G_OBJECT_CLASS(ags_play_audio_file_parent_class)->finalize(gobject);
}

void
ags_play_audio_file_run_inter(AgsRecall *recall)
{
  /* DEPRECATED
     AgsPlayAudioFile *play_audio_file;
     signed short *buffer;
     guint i0, i1, j, stop;
     gboolean play_done;

     AGS_RECALL_CLASS(ags_play_audio_file_parent_class)->run_inter(recall);

     play_audio_file = (AgsPlayAudioFile *) recall;

     if((AGS_SOUNDCARD_BUFFER0 & play_audio_file->soundcard->flags) != 0){
     buffer = play_audio_file->soundcard->buffer[1];
     }else if((AGS_SOUNDCARD_BUFFER1 & play_audio_file->soundcard->flags) != 0){
     buffer = play_audio_file->soundcard->buffer[2];
     }else if((AGS_SOUNDCARD_BUFFER2 & play_audio_file->soundcard->flags) != 0){
     buffer = play_audio_file->soundcard->buffer[3];
     }else if((AGS_SOUNDCARD_BUFFER3 & play_audio_file->soundcard->flags) != 0){
     buffer = play_audio_file->soundcard->buffer[0];
     }

     i0 = play_audio_file->current_frame;
     stop = i0 + play_audio_file->soundcard->buffer_size;

     if(stop < play_audio_file->audio_file->frames)
     play_done = FALSE;
     else{
     stop = play_audio_file->audio_file->frames;
     play_done = TRUE;
     }

     for(i1 = 0; i0 < stop; i0++, i1++){
     for(j = 0; j < play_audio_file->audio_file->channels || j < play_audio_file->soundcard->dsp_channels; j++)
     buffer[i1 * play_audio_file->soundcard->dsp_channels + j] = ((buffer[i1 * play_audio_file->soundcard->dsp_channels + j]) / 2) + ((play_audio_file->audio_file->buffer[i0 * play_audio_file->audio_file->channels + j]) / 2);
     }

     play_audio_file->current_frame = i0;

     if(play_done)
     ags_recall_done(recall);
  */
}

void
ags_play_audio_file_remove(AgsRecall *recall)
{
  AGS_RECALL_CLASS(ags_play_audio_file_parent_class)->remove(recall);
}

void
ags_play_audio_file_cancel(AgsRecall *recall)
{
  AGS_RECALL_CLASS(ags_play_audio_file_parent_class)->cancel(recall);
}

AgsPlayAudioFile*
ags_play_audio_file_new(AgsAudioFile *audio_file,
			GObject *soundcard)
{
  AgsPlayAudioFile *play_audio_file;

  play_audio_file = (AgsPlayAudioFile *) g_object_new(AGS_TYPE_PLAY_AUDIO_FILE,
						      "audio_file", audio_file,
						      "soundcard", soundcard,
						      NULL);

  return(play_audio_file);
}

