#include "gstreamer.hpp"

#include "audio_s.hpp"
#include "updater.hpp"

#include <string>

#include <iostream>

static int stop_status = 0;

int Gst::is_playing()
{
  Audio_s *audio_state = Audio_s::get_instance();

  if (audio_state->p_pause())
    return 1;

  GstState state;

  if (gst_element_get_state(player, &state, 0, GST_CLOCK_TIME_NONE) && 
      (state == GST_STATE_PLAYING || state == GST_STATE_PAUSED))
    return 1;
  else
    return 0;
}

int Gst::is_buffering()
{
  return 0;
}

int Gst::is_mute()
{
  return (old_volume != 0);
}

void Gst::gather_info()
{
  GstFormat fmt = GST_FORMAT_TIME;
  gint64 pos = 0, len = 0;

 if (gst_element_query_position(player, &fmt, &pos) && 
     gst_element_query_duration(player, &fmt, &len)) {
   total_time = len / GST_SECOND;
   cur_time = pos / GST_SECOND;
 }
}

void Gst::setfile(const Simplefile& file)
{
  std::string path;

  if ((file.path.find("cdda://") != std::string::npos) || file.type == "web")
    path = file.path;
  else
    path = "file://" + file.path;

  g_object_set(G_OBJECT (player), "uri", path.c_str(), NULL);
}

void Gst::addfile(const Simplefile &file)
{
  stop_player();

  setfile(file);

  gst_element_set_state(player, GST_STATE_PLAYING);

  Audio_s *audio_state = Audio_s::get_instance();

  audio_state->set_playing(true);
  audio_state->set_pause(false);
}

void Gst::play()
{
  Audio_s *audio_state = Audio_s::get_instance();

  if (audio_state->p_pause()) {
    gst_element_set_state(player, GST_STATE_PLAYING);
    audio_state->set_pause(false);
  } else {
    setfile(cur_nr);
    setpos(0);
    gst_element_set_state(player, GST_STATE_PLAYING);

    Audio_s::get_instance()->set_playing(true);
  }
}

void Gst::stop_player()
{
  Audio_s *audio_state = Audio_s::get_instance();
  audio_state->set_playing(false);

  gst_element_set_state(player, GST_STATE_READY);
  gst_element_set_state (GST_ELEMENT (audiosink), GST_STATE_NULL);//unlock
								  //audiodevice

  audio_state->p->set_title("");
  audio_state->p->set_artist("");
  audio_state->p->set_album("");
}

void Gst::pause()
{
  Audio_s *audio_state = Audio_s::get_instance();

  GstState state;

  if (gst_element_get_state(player, &state, 0, GST_CLOCK_TIME_NONE) && 
      state != GST_STATE_PAUSED) {
    gst_element_set_state(player, GST_STATE_PAUSED);
    audio_state->set_pause(true);
  } else {
    gst_element_set_state(player, GST_STATE_PLAYING);
    audio_state->set_pause(false);
  }
}

void Gst::ff()
{
  gather_info();

  if (!((cur_time + 10) > total_time))
    setpos(cur_time + 10);
}

void Gst::fb()
{
  gather_info();

  if (cur_time == 0)
    return;
  
  if ((cur_time - 10) < 0)
    setpos(0);
  else
    setpos(cur_time - 10);
}

int Gst::getpos()
{
  return cur_time;
}

void Gst::setpos(int p)
{
  gst_element_seek(player, 1.0, GST_FORMAT_TIME,
		   GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET,
		   p * GST_SECOND, 
		   GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
}

void Gst::mute()
{
  int vol = getvol(); 

  if (vol == 0) {
    setvol(old_volume);
    old_volume = 0;
  } else {
    old_volume = vol;
    setvol(0);
  }
}

void Gst::volup()
{
  int vol = getvol() + 4;
  if (vol > 100)
    vol = 100;
  setvol(vol);
}

void Gst::voldown()
{
  int vol = getvol() - 4;
  if (vol < 0)
    vol = 0;
  setvol(vol);
}

void Gst::setvol(int volume)
{
  g_object_set (G_OBJECT(player), "volume",
		(gdouble) (1. * volume / 100), NULL);
}

int Gst::getvol()
{
  gdouble vol;

  g_object_get (G_OBJECT(player), "volume", &vol, NULL);

  return (gint) (vol * 100 + 0.5);
}

void Gst::update_cdrom(const std::string& cdrom)
{}

void Gst::collect_info(const std::string& filename)
{
  Simplefile s;
  s.path = filename;
  s.type = "file";

  setfile(s);
  gather_info();
}

void process_tag(const GstTagList *taglist, const gchar *tag)
{
  Audio_s *audio_state = Audio_s::get_instance();

  char* data = NULL;

  if (gst_tag_list_get_string(taglist, GST_TAG_TITLE, &data) && data)
    audio_state->p->set_title(data);

  if (gst_tag_list_get_string(taglist, GST_TAG_ARTIST, &data) && data)
    audio_state->p->set_artist(data);

  if (gst_tag_list_get_string(taglist, GST_TAG_ALBUM, &data) && data)
    audio_state->p->set_album(data);
}

void bus_cb(GstBus *bus, GstMessage *message, void*)
{
  switch (GST_MESSAGE_TYPE(message)) {
  case GST_MESSAGE_TAG: {
    GstTagList *tags;
    gst_message_parse_tag(message, &tags);

    gst_tag_list_foreach(tags, (GstTagForeachFunc)process_tag, 0);
    gst_tag_list_free(tags);
    break;
  }
  case GST_MESSAGE_EOS: {
    stop_status = 1;
  }
  }
}

void Gst::check_bus() 
{
  if (stop_status) {

    gst_element_set_state(player, GST_STATE_READY);

    play_next_track = true;

    stop_status = 0;
  }

  gst_bus_poll(gst_element_get_bus(player), GST_MESSAGE_EOS, 0);
  gst_bus_poll(gst_element_get_bus(player), GST_MESSAGE_TAG, 0);
}

int Gst::check_bus_interval() 
{
  return 200; // ms
}

Gst::Gst()
  : Player("", "", "", 0, 0, 0)
{
  gst_init (0, 0);
  player = gst_element_factory_make("playbin", "play");
  
  audiosink = gst_element_factory_make ("osssink", "audiosink");
  g_object_set(G_OBJECT(audiosink), "device", conf->p_audio_dev().c_str(), NULL); //device name <- config file
  g_object_set(player, "audio-sink", audiosink, NULL);

  gst_bus_add_watch(gst_element_get_bus(player),
  		    (GstBusFunc)bus_cb, 0);

  // updater
  Updater &updater = Updater::get_instance();

  TimeFunctori<Gst> *time_func = new TimeFunctori<Gst>(this, &Gst::check_bus_interval);
  TimeFunctorv<Gst> *call_func = new TimeFunctorv<Gst>(this, &Gst::check_bus);
  updater.timer.add(TimeElement("gst check bus", time_func, call_func));
}

Gst::~Gst()
{
  stop_player();

  gst_object_unref (GST_OBJECT (player));
  if (audiosink != NULL)
    gst_object_unref (GST_OBJECT (audiosink));
}
