#include <gst/gst.h>
#include <signal.h>
#include <cstdlib>


void FPE_ExceptionHandler(int nSig,int nErrType,int * /*pnReglist*/)
{
    switch(nErrType)
    {
        case FPE_INTDIV:  
            g_print("Integer divide by zero");
            break;
        case FPE_INTOVF:  
            g_print("Integer overflow");
            break;
        case FPE_FLTDIV:  
            g_print("Floating-point divide by zero");
            break;
        case FPE_FLTOVF:  
            g_print("Floating-point overflow");
            break;
        case FPE_FLTUND: 
            g_print("Floating-point underflow");
            break;
        case FPE_FLTRES:
            g_print("Floating-point inexact result");
            break;
        case FPE_FLTINV: 
            g_print("Invalid floating-point operation");
            break;
        case FPE_FLTSUB:
            g_print("Subscript out of range");
            break;
        default:
            g_print("Got floating point exception");
    }
    g_print("\n");
    exit(EXIT_FAILURE);
}


static gboolean bus_call(GstBus *bus, GstMessage *msg, gpointer data)
{
    GMainLoop *loop = static_cast<GMainLoop*>(data);

    switch (GST_MESSAGE_TYPE (msg)) 
    {
        case GST_MESSAGE_EOS:
            g_print ("End-of-stream\n");
            g_main_loop_quit (loop);
            break;
        case GST_MESSAGE_ERROR: 
            {
                gchar *debug = NULL;
                GError *err = NULL;

                gst_message_parse_error (msg, &err, &debug);

                g_print ("Error: %s\n", err->message);
                g_error_free (err);

                if (debug) {
                    g_print ("Debug details: %s\n", debug);
                    g_free (debug);
                }

                g_main_loop_quit (loop);
                break;
            }
        default:
            break;
    }

    return TRUE;
}

gint main (gint argc, gchar *argv[])
{
    // attach floating point exception signal handler
    signal(SIGFPE, (void (*)(int))FPE_ExceptionHandler);
    GstStateChangeReturn ret;
    GstElement *pipeline, *src, *srcAconv, *capsFilter, *sinkAconv, *sink;
    GMainLoop *loop;
    GstBus *bus;

    /* initialization */
    gst_init (&argc, &argv);
    loop = g_main_loop_new (NULL, FALSE);

    /* create elements */
    pipeline = gst_pipeline_new ("my_pipeline");

    /* watch for messages on the pipeline's bus (note that this will only
     * work like this when a GLib main loop is running) */
    bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
    gst_bus_add_watch (bus, bus_call, loop);
    gst_object_unref (bus);

    src = gst_element_factory_make ("audiotestsrc", NULL);

    GstCaps *caps = 0;
    caps = gst_caps_from_string("audio/x-raw-int, channels=8, clock-rate=48000");
    capsFilter = gst_element_factory_make ("capsfilter", NULL);
    g_object_set(capsFilter, "caps", caps, NULL);

    srcAconv = gst_element_factory_make ("audioconvert", "srcAconv");
    sinkAconv = gst_element_factory_make ("audioconvert", "sinkAconv");
    sink = gst_element_factory_make("jackaudiosink", NULL);
    g_object_set(sink, "buffer-time", 1000LL, NULL);

    if (!sink)
        g_print ("output could not be found - check your install\n");


    gst_bin_add_many (GST_BIN (pipeline), src, srcAconv, capsFilter, sinkAconv, sink, NULL);

    /* link everything together */
    if (!gst_element_link_many(src, srcAconv, capsFilter, sinkAconv, sink, NULL)) {
        g_print ("Failed to link one or more elements!\n");
        return -1;
    }

    /* run */
    ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
    if (ret == GST_STATE_CHANGE_FAILURE) {
        g_print ("Failed to start up pipeline!\n");

        /* check if there is an error message with details on the bus */
        GstMessage *msg = gst_bus_poll (bus, GST_MESSAGE_ERROR, 0);
        if (msg) {
            GError *err = NULL;

            gst_message_parse_error (msg, &err, NULL);
            g_print ("ERROR: %s\n", err->message);
            g_error_free (err);
            gst_message_unref (msg);
        }
        return -1;
    }

        g_main_loop_run (loop);

    /* clean up */
    gst_element_set_state (pipeline, GST_STATE_NULL);
    gst_object_unref (pipeline);

    return 0;
}
