#include <string>
#include <gst/gst.h>
#include <gst/app/gstappsrc.h>
#include <gst/app/gstappsink.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>


GstElement* m_pPipeline = NULL;
GstElement* m_videoSink = NULL;
GstElement* m_alsasink = NULL;

std::string filename = "file:///tmp/USB0/VideoFolder/05_H264_1280x720_25.avi";

gboolean gstBusCallback(GstBus* bus, GstMessage* message, gpointer data)
{
   switch (GST_MESSAGE_TYPE(message))
   {
      case GST_MESSAGE_ERROR:
      {
         GError *err;
         gchar* debugInfo;

         gst_message_parse_error(message, &err, &debugInfo);
         printf("gstBusCallback() Got %s message from Element \"%s\": %s Debug Info: %s\n",
               GST_MESSAGE_TYPE_NAME(message), GST_OBJECT_NAME(message->src), err->message,
               debugInfo ? debugInfo : "none");
         g_error_free(err);
         g_free(debugInfo);
         break;
      }
      case GST_MESSAGE_STATE_CHANGED:
      {
         GstState oldState, newState;
         gst_message_parse_state_changed(message, &oldState, &newState, NULL);

         if (strstr(GST_OBJECT_NAME(message->src), "Pipeline"))
         {
            printf("CgstBasePlayer::gstBusCallback() Got %s message: Element \"%s\" state %s --> %s\n",
                  GST_MESSAGE_TYPE_NAME(message), GST_OBJECT_NAME(message->src),
                  gst_element_state_get_name(oldState), gst_element_state_get_name(newState));
         }
         break;
      }
      case GST_MESSAGE_EOS:
      {
		GstState oldState, newState;
         gst_message_parse_state_changed(message, &oldState, &newState, NULL);
		 
         printf("EOS Got %s message, %s\n",
               GST_MESSAGE_TYPE_NAME(message), GST_OBJECT_NAME(message->src));
		  if (strstr(GST_OBJECT_NAME(message->src), "Pipeline"))
         {
            printf("Got %s message: Element \"%s\" state %s --> %s\n",
                  GST_MESSAGE_TYPE_NAME(message), GST_OBJECT_NAME(message->src),
                  gst_element_state_get_name(oldState), gst_element_state_get_name(newState));
         }
         break;
      }
      default:
      {
         //printf("CgstBasePlayer::gstBusCallback()(default) Got %s message\n",
         //      GST_MESSAGE_TYPE_NAME(message));
         break;
      }
   }
   return true;
}


bool createPipeline(const std::string& file)
{
   /*gst-launch-1.0 filesrc location=./aapvideo_12-35-42.665.h264 ! 'video/x-h264,stream-format=(string)byte-stream'  
    ! h264parse ! 'video/x-h264,width=800,height=480,parsed=true,stream-format=avc,alignment=au,profile=baseline'
    ! vpudec ! 'video/x-raw,format=NV12,width=800,height=480,interlace-mode=progressive' ! imxipuvideotransform 
    ! imxeglvivsink window-width=800 window-height=480
    */
	m_pPipeline = gst_element_factory_make("playbin", "URI Pipeline");
	if(!m_pPipeline)
	{
		printf("!!!!Create pipeline failed\n");
		return false;
	}
	
	m_videoSink = gst_element_factory_make("waylandsink", "vsink");
   if (m_videoSink)
   {
      g_object_set(G_OBJECT(m_pPipeline), "video-sink", m_videoSink, NULL);
	  g_object_set(G_OBJECT(m_videoSink), "surface-id", 999, NULL);

      //probe video first frame reached
      //mFirstVideoFrameReached = false;

	  #if 0
      GstPad *pad;
      pad = gst_element_get_static_pad(m_videoSink, "sink");
      if (pad)
      {
         gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, (GstPadProbeCallback)cb_probe_videodata, this, NULL);
         gst_object_unref(pad);
      }
	  #endif
   }
   else
   {
      printf("create glimagesink failed\n");
   }
   #if 0
   m_alsasink = gst_element_factory_make("alsasink", "asink");
	 if (m_alsasink)
	 {
		g_object_set(G_OBJECT(m_alsasink), "device", "media_p", NULL);
		g_object_set(G_OBJECT(m_pPipeline), "audio-sink", m_alsasink, NULL);
		g_object_set(G_OBJECT(m_alsasink), "latency-time", 16000, NULL);
		g_object_set(G_OBJECT(m_alsasink), "buffer-time", 64000, NULL);
	 }
	 else
	 {
		 printf("create alsasink failed\n");
	 }
#endif
   GstBus* bus = gst_pipeline_get_bus (GST_PIPELINE (m_pPipeline));
   if(bus)
   {
      gst_bus_add_watch(bus, gstBusCallback, NULL);
      gst_object_unref(GST_OBJECT(bus));
   }
   else
   {
      printf("????? set bus failed\r\n");
   }

   return true;
}

void HandleStopVideo()
{
   printf("\r\n*********** Start Stop Video ************\r\n");
   GstStateChangeReturn    stateChangeResult;
   int    err;

  for(int count=0; count<5; count++)
  {
     GstState state, pending;
     //set timeout to prevent blocking
     GstStateChangeReturn stateChange = gst_element_get_state(m_pPipeline, &state, &pending, 5000 * GST_MSECOND);

     printf("GSTPlayer::stop() Obtained the play state:[%s] pending:[%s]\n",
           gst_element_state_get_name(state), gst_element_state_get_name(pending));
     if( state >= GST_STATE_PLAYING )
     {
        break;
     }
  }

  stateChangeResult = gst_element_set_state(m_pPipeline, GST_STATE_NULL);
  if (stateChangeResult == GST_STATE_CHANGE_FAILURE) {
      printf ("Unable to set the pipeline to the playing state to null.\n");
      gst_object_unref (m_pPipeline);
      m_pPipeline = NULL;
  }
  printf("*********** End Stop Video ************\r\n");
  
  g_object_set(G_OBJECT(m_pPipeline), "uri", filename.c_str(), NULL);
}
   
void HandleStartOfVideo()
{
   if(m_pPipeline)
   {
      printf("\r\n*********** Start Play Video ************\r\n");
      GstStateChangeReturn    stateChangeResult;
      int    err;
	
      // Starting pipelineB in a seperate thread.
      stateChangeResult = gst_element_set_state(m_pPipeline, GST_STATE_PLAYING);
      if (stateChangeResult == GST_STATE_CHANGE_SUCCESS) {
         printf("*********** Play Video Success************\r\n");
      }
      else if(GST_STATE_CHANGE_ASYNC == stateChangeResult)
      {
         printf("set state to PLAYING async\n");
      }
      else if(stateChangeResult == GST_STATE_CHANGE_FAILURE)
      {
         printf("*********** Play Video Failed ************\r\n");
      }
   }
}

GMainLoop* m_GMainLoop = NULL;

gpointer GMainThread(gpointer data)
{
   printf("GMainThread Started\n");
   m_GMainLoop = g_main_loop_new(NULL, FALSE);
   g_main_loop_run(m_GMainLoop);
   printf("GMainThread Exited\n");
   
   return NULL;
}

int main(int argc, char **argv)
{
   printf("************ Test ************\n");
   printf("usage: %s filename sec times\n", argv[0]);
   
   filename = argv[1];
   if(argc != 4)
   {
      printf("please enter the file path\n");
      return -1;
   }

   gst_init(NULL, NULL);

   if(!createPipeline(argv[1]))
   {
      printf("create pipeline failed\n");
      return -1;
   }

   g_object_set(G_OBJECT(m_pPipeline), "uri", filename.c_str(), NULL);
   
   HandleStartOfVideo();
   
   int sec = atoi(argv[2]);
   int times = atoi(argv[3]);

   //GMainLoop* mGMainLoop = g_main_loop_new(NULL, FALSE);
   //g_main_loop_run(mGMainLoop);
   g_thread_new("GMainLoop thread", GMainThread, NULL);
   
   do
   {
      usleep(sec * 1000000);
      HandleStopVideo();
   
      usleep(1000000);
      HandleStartOfVideo();
      
   }while(times-- > 0);

   gst_deinit();
   
}

