/*
 * Copyright (C) 2014, Sebastian Dröge <sebastian@centricular.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation
 * version 2.1 of the License.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <gst/gst.h>

#include "gstomxdtsxdec.h"

GST_DEBUG_CATEGORY_STATIC (gst_omx_dtsx_dec_debug_category);
#define GST_CAT_DEFAULT gst_omx_dtsx_dec_debug_category

#define DEFAULT_PROP_SPKCFG "lr"


/* prototypes */
static gboolean gst_omx_dtsx_dec_set_format (GstOMXAudioDec * dec, GstOMXPort * port, GstCaps * caps);
static gboolean gst_omx_dtsx_dec_is_format_change (GstOMXAudioDec * dec, GstOMXPort * port, GstCaps * caps);
static gint gst_omx_dtsx_dec_get_samples_per_frame (GstOMXAudioDec * dec, GstOMXPort * port);
static gboolean gst_omx_dtsx_dec_get_channel_positions (GstOMXAudioDec * dec, GstOMXPort * port, GstAudioChannelPosition position[OMX_AUDIO_MAXCHANNELS]);
static void gst_omx_dtsx_dec_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
static gboolean gst_omx_dtsx_dec_set_parament(GstOMXAudioDec * dec);

enum
{
	PROP_0,
    PROP_ENABLE_DIRECT_MODE_X,
    PROP_DRC_PERCENT_X,
    PROP_SET_DIALOG_CONTROL_X,    
    PROP_SPEAKER_CONFIG
};

/* class initialization */

#define DEBUG_INIT \
  GST_DEBUG_CATEGORY_INIT (gst_omx_dtsx_dec_debug_category, "omxdtsxdec", 0, \
      "debug category for gst-omx dtsx audio decoder");

G_DEFINE_TYPE_WITH_CODE (GstOMXDTSXDec, gst_omx_dtsx_dec, GST_TYPE_OMX_AUDIO_DEC, DEBUG_INIT);

static void
gst_omx_dtsx_dec_class_init (GstOMXDTSXDecClass * klass)
{
	GObjectClass *gobject_class = (GObjectClass *) klass;

    GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
    GstOMXAudioDecClass *audiodec_class = GST_OMX_AUDIO_DEC_CLASS (klass);
	gobject_class->set_property = gst_omx_dtsx_dec_set_property;

    audiodec_class->set_format = GST_DEBUG_FUNCPTR (gst_omx_dtsx_dec_set_format);
    audiodec_class->is_format_change = GST_DEBUG_FUNCPTR (gst_omx_dtsx_dec_is_format_change);
    audiodec_class->get_samples_per_frame = GST_DEBUG_FUNCPTR (gst_omx_dtsx_dec_get_samples_per_frame);
    audiodec_class->get_channel_positions = GST_DEBUG_FUNCPTR (gst_omx_dtsx_dec_get_channel_positions);
	audiodec_class->set_parament = GST_DEBUG_FUNCPTR (gst_omx_dtsx_dec_set_parament);

    audiodec_class->cdata.default_sink_template_caps = "audio/x-dts, " "rate=(int)[8000,48000], " "channels=(int)[1,8], " "parsed=(boolean) true";

    gst_element_class_set_static_metadata (element_class,
                                           "OpenMAX DTSX Audio Decoder",
                                           "Codec/Decoder/Audio",
                                           "Decode DTSX audio streams",
                                           "Yuan.Fang<yuan.fang@mediatek.com>");

    gst_omx_set_default_role (&audiodec_class->cdata, "audio_decoder.dtsx");

	g_object_class_install_property (gobject_class, PROP_ENABLE_DIRECT_MODE_X,
                                     g_param_spec_int("enable-direct-mode", "DIRECT MODE", "enable direct mode",
                                                          0, 1,
                                                          0, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
	
    g_object_class_install_property (gobject_class, PROP_DRC_PERCENT_X,
                                     g_param_spec_int("drc-percent", "DRC PERCENT", "setting drc percent",
                                                          0, 100,
                                                          0, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property (gobject_class, PROP_SET_DIALOG_CONTROL_X,
                                     g_param_spec_int("dialog-control", "DIALOG CONTROL", "setting dialog control",
                                                          0, 100,
                                                          0, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property (gobject_class, PROP_SPEAKER_CONFIG,
                                 	g_param_spec_string ("speaker-config", "SPEAKER CONFIG","setting speaker config",
                                         DEFAULT_PROP_SPKCFG, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));

}

static GstMessage *
gst_cs_message_new_audio_info (GstElement * ele, gint64 stream_type, gint64 dts_comp_gain, gint64 dts_stream_num, gint64 dts_stream_mask)
{
  GstStructure *s;

  s = gst_structure_new ("audio_info",
      "stream-type", G_TYPE_INT64, stream_type,
      "dts-comp-gain", G_TYPE_INT64, dts_comp_gain,
      "dts-stream-num", G_TYPE_INT64, dts_stream_num,
      "dts-stream-mask", G_TYPE_INT64, dts_stream_mask,
      NULL);

  return gst_message_new_element(GST_OBJECT(ele), s);
}

static void
gst_cs_message_post_audio_info (GstElement *ele, gint64 stream_type, gint64 dts_comp_gain, gint64 dts_stream_ch_num, gint64 dts_stream_ch_mask)
{
  GstMessage *m = gst_cs_message_new_audio_info (ele, stream_type, dts_comp_gain, dts_stream_ch_num, dts_stream_ch_mask);

  gst_element_post_message(GST_ELEMENT (ele), m);
}


static OMX_ERRORTYPE
gst_omx_dtsx_dec_event_handler (OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_EVENTTYPE eEvent,
    OMX_U32 nData1, OMX_U32 nData2, OMX_PTR pEventData, void* plugin_data)
{
    GstOMXDTSXDec *self = GST_OMX_DTSX_DEC (plugin_data);
    GST_INFO_DTSX* info = (GST_INFO_DTSX*) pEventData;

#if 1 // for debug
    g_print ("[gstomxdtsxdec]  self %p info %p\n", self, info);
#endif

    if (info)
    {
      g_print ("[gstomxdtsxdec] stream_type:0x%x, gain:%d\n", info->stream_type, info->dts_analog_compgain);
	  g_print ("[gstomxdtsxdec] stream_channle_num:0x%x\n", info->dts_stream_ch_num);
	  g_print ("[gstomxdtsxdec] stream_channle_mask: 0x%x\n", info->dts_stream_ch_mask);
      gst_cs_message_post_audio_info (self, info->stream_type, info->dts_analog_compgain, info->dts_stream_ch_num, info->dts_stream_ch_mask);
    }
}

static void
gst_omx_dtsx_dec_init (GstOMXDTSXDec * self)
{
    self->spf = -1;
    self->dialog_control = -1;
    self->drc_percent = -1;
    self->enable_direct_mode = -1;

    gst_omx_component_set_codec_info_callback (self->parent.dec, gst_omx_dtsx_dec_event_handler, self);
}

static gboolean
gst_omx_dtsx_dec_set_format (GstOMXAudioDec * dec, GstOMXPort * port, GstCaps * caps)
{
    GstOMXDTSXDec *self = GST_OMX_DTSX_DEC (dec);
    OMX_PARAM_PORTDEFINITIONTYPE port_def;
    OMX_AUDIO_PARAM_DTSXTYPE dtsx_param;
    OMX_ERRORTYPE err;
    GstStructure *s;
    gint rate, channels, layer, mpegaudioversion;
	GST_ERROR_OBJECT (self, "[YF][DTSXDEC]gst_omx_dtsx_dec_set_format");

    gst_omx_port_get_port_definition (port, &port_def);
    port_def.format.audio.eEncoding = OMX_AUDIO_CodingDTSX;
    err = gst_omx_port_update_port_definition (port, &port_def);
    if (err != OMX_ErrorNone)
    {
        GST_ERROR_OBJECT (self,
                          "Failed to set DTSX format on component: %s (0x%08x)",
                          gst_omx_error_to_string (err), err);
        return FALSE;
    }

    GST_OMX_INIT_STRUCT (&dtsx_param);
    dtsx_param.nPortIndex = port->index;

    err =gst_omx_component_get_parameter (dec->dec, OMX_IndexParamAudioDtsx, &dtsx_param);
    if (err != OMX_ErrorNone)
    {
        GST_ERROR_OBJECT (self,
                          "Failed to get DTSX parameters from component: %s (0x%08x)",
                          gst_omx_error_to_string (err), err);
        return FALSE;
    }

    s = gst_caps_get_structure (caps, 0);

    if (!gst_structure_get_int (s, "rate", &rate) || !gst_structure_get_int (s, "channels", &channels))
    {
        GST_ERROR_OBJECT (self, "Incomplete caps");
        return FALSE;
    }

    //self->spf = (mpegaudioversion == 1 ? 1152 : 576);

    dtsx_param.nChannels = channels;
    dtsx_param.nBitRate = 0;       /* unknown */
    dtsx_param.nSampleRate = rate;
    dtsx_param.nAudioBandWidth = 0;        /* decoder decision */
    dtsx_param.eChannelMode = 0;   /* FIXME */    

    err = gst_omx_component_set_parameter (dec->dec, OMX_IndexParamAudioDtsx, &dtsx_param);
    if (err != OMX_ErrorNone)
    {
        GST_ERROR_OBJECT (self, "Error setting DTSX parameters: %s (0x%08x)", gst_omx_error_to_string (err), err);
        return FALSE;
    }

    return TRUE;
}

static gboolean
gst_omx_dtsx_dec_is_format_change (GstOMXAudioDec * dec, GstOMXPort * port, GstCaps * caps)
{
    GstOMXDTSXDec *self = GST_OMX_DTSX_DEC (dec);
    OMX_AUDIO_PARAM_DTSXTYPE dtsx_param;
    OMX_ERRORTYPE err;
    GstStructure *s;
    gint rate, channels, layer, mpegaudioversion;

    GST_OMX_INIT_STRUCT (&dtsx_param);
    dtsx_param.nPortIndex = port->index;

    err = gst_omx_component_get_parameter (dec->dec, OMX_IndexParamAudioDtsx, &dtsx_param);
    if (err != OMX_ErrorNone)
    {
        GST_ERROR_OBJECT (self,
                          "Failed to get DTSX parameters from component: %s (0x%08x)",
                          gst_omx_error_to_string (err), err);
        return FALSE;
    }

    s = gst_caps_get_structure (caps, 0);

    if (!gst_structure_get_int (s, "rate", &rate) || !gst_structure_get_int (s, "channels", &channels))
    {
        GST_ERROR_OBJECT (self, "Incomplete caps");
        return FALSE;
    }

    if (dtsx_param.nChannels != channels)
        return TRUE;

    if (dtsx_param.nSampleRate != rate)
        return TRUE;

    return FALSE;
}

static gint
gst_omx_dtsx_dec_get_samples_per_frame (GstOMXAudioDec * dec, GstOMXPort * port)
{
    //return GST_OMX_DTSX_DEC (dec)->spf;
    return 512;
}

static gboolean
gst_omx_dtsx_dec_get_channel_positions (GstOMXAudioDec * dec, GstOMXPort * port, GstAudioChannelPosition position[OMX_AUDIO_MAXCHANNELS])
{
    OMX_AUDIO_PARAM_PCMMODETYPE pcm_param;
    OMX_ERRORTYPE err;

    GST_OMX_INIT_STRUCT (&pcm_param);
    pcm_param.nPortIndex = port->index;
    err = gst_omx_component_get_parameter (dec->dec, OMX_IndexParamAudioPcm, &pcm_param);
    if (err != OMX_ErrorNone)
    {
        GST_ERROR_OBJECT (dec, "Failed to get PCM parameters: %s (0x%08x)", gst_omx_error_to_string (err), err);
        return FALSE;
    }

    switch (pcm_param.nChannels)
    {
    case 1:
        position[0] = GST_AUDIO_CHANNEL_POSITION_MONO;
        break;
    case 2:
        position[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
        position[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
        break;
    case 3:
      position[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER;
      position[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
      position[2] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
      break;
    case 4:
      position[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER;
      position[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
      position[2] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
      position[3] = GST_AUDIO_CHANNEL_POSITION_REAR_CENTER;
      break;
    case 5:
      position[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER;
      position[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
      position[2] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
      position[3] = GST_AUDIO_CHANNEL_POSITION_REAR_LEFT;
      position[4] = GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT;
      break;
    case 6:
      position[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER;
      position[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
      position[2] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
      position[3] = GST_AUDIO_CHANNEL_POSITION_REAR_LEFT;
      position[4] = GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT;
      position[5] = GST_AUDIO_CHANNEL_POSITION_LFE1;
      break;
    default:
        return FALSE;
    }

    return TRUE;
}

static gboolean
gst_omx_dtsx_dec_set_parament(GstOMXAudioDec * dec)
{ 
    GstOMXDTSXDec *self = GST_OMX_DTSX_DEC (dec);
    OMX_AUDIO_DTSX_APP_PARAM dtsx_app_param;
	memset(&dtsx_app_param,0, sizeof(OMX_AUDIO_DTSX_APP_PARAM));
 
	dtsx_app_param.nDirectmode = self->enable_direct_mode;
	dtsx_app_param.nDrcpercent = self->drc_percent;
	dtsx_app_param.nDialogcontrol = self->dialog_control;
	strncpy(dtsx_app_param.nspkconfig, self->speak_config, 128);
	
 	gst_omx_component_set_parameter (dec->dec, OMX_IndexConfigDTSXDecMode, &dtsx_app_param);
}

static void
gst_omx_dtsx_dec_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
{
    GstOMXDTSXDec *filter = (GstOMXDTSXDec*) (object);
	
    switch (prop_id)
    {
	    case PROP_ENABLE_DIRECT_MODE_X:
	        filter->enable_direct_mode = g_value_get_int (value);
	        g_print("[gstomxdtsxdec] enable direct mdoe: %d\n", filter->enable_direct_mode);
	        break;
			
	    case PROP_DRC_PERCENT_X:
	        filter->drc_percent = g_value_get_int (value);
	        g_print("[gstomxdtsxdec] set drc percent: %d\n", filter->drc_percent);
	        break;
		case PROP_SET_DIALOG_CONTROL_X:
	        filter->dialog_control = g_value_get_int (value);
	        g_print("[gstomxdtsxdec] set dialog control: %d\n", filter->dialog_control);
	        break;
		 case PROP_SPEAKER_CONFIG:
			 strcpy(filter->speak_config, g_value_get_string (value));
			 if (filter->speak_config == NULL)
			 {
				 strcpy(filter->speak_config, DEFAULT_PROP_SPKCFG);
			 }
	        g_print("[gstomxdtsxdec] set speaker config: %s\n", filter->speak_config);
	        break;
	    default:
	        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
	        break;
    }
}

