/*
 * 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 "gstomxddpdec.h"

GST_DEBUG_CATEGORY_STATIC (gst_omx_ddp_dec_debug_category);
#define GST_CAT_DEFAULT gst_omx_ddp_dec_debug_category

/* prototypes */
static gboolean gst_omx_ddp_dec_set_format (GstOMXAudioDec * dec, GstOMXPort * port, GstCaps * caps);
static gboolean gst_omx_ddp_dec_is_format_change (GstOMXAudioDec * dec, GstOMXPort * port, GstCaps * caps);
static gint gst_omx_ddp_dec_get_samples_per_frame (GstOMXAudioDec * dec, GstOMXPort * port);
static gboolean gst_omx_ddp_dec_get_channel_positions (GstOMXAudioDec * dec, GstOMXPort * port, GstAudioChannelPosition position[OMX_AUDIO_MAXCHANNELS]);
static void gst_omx_ddp_dec_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
static gboolean gst_omx_ddp_dec_set_parament(GstOMXAudioDec * dec);


enum
{
	PROP_0,
    PROP_DRC_MODE,
    PROP_DOWNMIX_MODE
};


/* class initialization */

#define DEBUG_INIT \
  GST_DEBUG_CATEGORY_INIT (gst_omx_ddp_dec_debug_category, "omxddpdec", 0, \
      "debug category for gst-omx ddp audio decoder");

G_DEFINE_TYPE_WITH_CODE (GstOMXDDPDec, gst_omx_ddp_dec, GST_TYPE_OMX_AUDIO_DEC, DEBUG_INIT);

static void
gst_omx_ddp_dec_class_init (GstOMXDDPDecClass * 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_ddp_dec_set_property;

    audiodec_class->set_format = GST_DEBUG_FUNCPTR (gst_omx_ddp_dec_set_format);
    audiodec_class->is_format_change = GST_DEBUG_FUNCPTR (gst_omx_ddp_dec_is_format_change);
    audiodec_class->get_samples_per_frame = GST_DEBUG_FUNCPTR (gst_omx_ddp_dec_get_samples_per_frame);
    audiodec_class->get_channel_positions = GST_DEBUG_FUNCPTR (gst_omx_ddp_dec_get_channel_positions);
    audiodec_class->set_parament = GST_DEBUG_FUNCPTR (gst_omx_ddp_dec_set_parament);

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

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

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

    g_object_class_install_property (gobject_class, PROP_DRC_MODE,
                                     g_param_spec_int("drc-mode", "DRC MODE", "setting DRC MODE",
                                                          0, 8,
                                                          2, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
	
    g_object_class_install_property (gobject_class, PROP_DOWNMIX_MODE,
                                     g_param_spec_int("downmix-mode", "DOWNMIX MODE", "setting DOWN MIX MODE",
                                                          1, 2,
                                                          1, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
}

static void
gst_omx_ddp_dec_init (GstOMXDDPDec * self)
{
    self->spf = -1;
}

static gboolean
gst_omx_ddp_dec_set_format (GstOMXAudioDec * dec, GstOMXPort * port,
                            GstCaps * caps)
{
    GstOMXDDPDec *self = GST_OMX_DDP_DEC (dec);
    OMX_PARAM_PORTDEFINITIONTYPE port_def;
    OMX_AUDIO_PARAM_DDPTYPE ddp_param;
    OMX_ERRORTYPE err;
    GstStructure *s;
    gint rate, channels, layer, mpegaudioversion;
	GST_ERROR_OBJECT (self, "[YF][DDPDEC]gst_omx_ddp_dec_set_format");

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

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

    err =gst_omx_component_get_parameter (dec->dec, OMX_IndexParamAudioDdp, &ddp_param);
    if (err != OMX_ErrorNone)
    {
        GST_ERROR_OBJECT (self,
                          "Failed to get DDP 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);

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

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

    return TRUE;
}

static gboolean
gst_omx_ddp_dec_is_format_change (GstOMXAudioDec * dec, GstOMXPort * port, GstCaps * caps)
{
    GstOMXDDPDec *self = GST_OMX_DDP_DEC (dec);
    OMX_AUDIO_PARAM_DDPTYPE ddp_param;
    OMX_ERRORTYPE err;
    GstStructure *s;
    gint rate, channels, layer, mpegaudioversion;

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

    err = gst_omx_component_get_parameter (dec->dec, OMX_IndexParamAudioDdp, &ddp_param);
    if (err != OMX_ErrorNone)
    {
        GST_ERROR_OBJECT (self,
                          "Failed to get DDP 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 (ddp_param.nChannels != channels)
        return TRUE;

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

    return FALSE;
}

static gint
gst_omx_ddp_dec_get_samples_per_frame (GstOMXAudioDec * dec, GstOMXPort * port)
{
    //return GST_OMX_DDP_DEC (dec)->spf;
    return 1536;
}

static gboolean
gst_omx_ddp_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_ddp_dec_set_parament(GstOMXAudioDec * dec)
{
    GstOMXDDPDec *self = GST_OMX_DDP_DEC (dec);
    OMX_AUDIO_PARAM_DDPTYPE ddp_param;

	g_print("[YF][ddpdec]set_parameter[0x%08X], self->drc_mode=%d, self->downmix_mode=%d\n", OMX_IndexConfigDDPDecMode, self->drc_mode, self->downmix_mode);
	ddp_param.nDrcmode = self->drc_mode;
	ddp_param.nDownmixmode = self->downmix_mode;
	gst_omx_component_set_parameter (dec->dec, OMX_IndexConfigDDPDecMode, &ddp_param);
}

static void
gst_omx_ddp_dec_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
{
    GstOMXDDPDec *filter = (GstOMXDDPDec*) (object);
	
    switch (prop_id)
    {
	    case PROP_DRC_MODE:
	        filter->drc_mode = g_value_get_int (value);
	        g_print("[gstomxddpdec] set drc mode: %d\n", filter->drc_mode);
	        break;
			
	    case PROP_DOWNMIX_MODE:
	        filter->downmix_mode = g_value_get_int (value);
	        g_print("[gstomxddpdec] set downmix mode: %d\n", filter->downmix_mode);
	        break;
	    default:
	        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
	        break;
    }
}
