/*****************************************************************
 * gmerlin - a general purpose multimedia framework and applications
 *
 * Copyright (c) 2001 - 2012 Members of the Gmerlin project
 * gmerlin-general@lists.sourceforge.net
 * http://gmerlin.sourceforge.net
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * *****************************************************************/

#include <config.h>

#include <x11/x11.h>
#include <x11/x11_window_private.h>

#include <interface/mmal/mmal.h>
#include <interface/mmal/util/mmal_default_components.h>
#include <interface/mmal/util/mmal_util_params.h>
#include <interface/mmal/util/mmal_util.h>

#include <interface/mmal/vc/mmal_vc_api.h>

#include <gavl/hw.h>
#include <gavl/hw_mmal.h>

#include <gmerlin/translation.h>
#include <gmerlin/utils.h>
#include <gmerlin/log.h>
#define LOG_DOMAIN "video_mmal"


typedef struct
  {
  gavl_hw_context_t * hwctx;
  gavl_hw_context_t * hwctx_priv;

  MMAL_COMPONENT_T * renderer;
  MMAL_POOL_T * pool;
  int renderer_port_enabled;
  
  } mmal_priv_t;

static int init_mmal(driver_data_t * d)
  {
  mmal_priv_t * priv;
  priv = calloc(1, sizeof(*priv));

  mmal_vc_init();
    
  if(!(priv->hwctx_priv = gavl_hw_ctx_create_mmal()))
    {
    free(priv);
    return 0;
    }

  d->priv = priv;
  
  d->img_formats = gavl_hw_ctx_get_image_formats(priv->hwctx_priv);
  d->ovl_formats = gavl_hw_ctx_get_overlay_formats(priv->hwctx_priv);
  
  return 1;
  }

static void set_format(driver_data_t * d)
  {
  
  }

static void input_cb(MMAL_PORT_T * port, MMAL_BUFFER_HEADER_T * buffer)
  {
  mmal_buffer_header_release(buffer);
  }

static int set_display_region(driver_data_t * d)
  {
  bg_x11_window_t * w;
  mmal_priv_t * priv;
  MMAL_PORT_T *input;
  MMAL_DISPLAYREGION_T region;
  MMAL_STATUS_T status;
  
  
  w = d->win;
  priv = d->priv;

  input = priv->renderer->input[0];

  if(priv->renderer_port_enabled)
    {
    if((status = mmal_port_disable(input)) != MMAL_SUCCESS)
      {
      gavl_log(GAVL_LOG_ERROR, LOG_DOMAIN, "Disabling input port failed (status=%"PRIx32" %s)",
             status, mmal_status_to_string(status));
      }
    priv->renderer_port_enabled = 0;
    }


  
  region.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
  region.hdr.size = sizeof(MMAL_DISPLAYREGION_T);

  region.fullscreen = MMAL_TRUE;

  region.dest_rect.x      = w->dst_rect.x;
  region.dest_rect.y      = w->dst_rect.y;
  region.dest_rect.width  = w->dst_rect.w;
  region.dest_rect.height = w->dst_rect.h;
  
  region.src_rect.x      = w->src_rect.x;
  region.src_rect.y      = w->src_rect.y;
  region.src_rect.width  = w->src_rect.w;
  region.src_rect.height = w->src_rect.h;
  
  region.layer = 128;
  region.alpha = 0xff | MMAL_DISPLAY_ALPHA_FLAGS_DISCARD_LOWER_LAYERS;

  //2 for HDMI0, 7 for HDMI1  
  region.display_num = 7;

  region.set = MMAL_DISPLAY_SET_NUM | \
    MMAL_DISPLAY_SET_FULLSCREEN | \
    MMAL_DISPLAY_SET_SRC_RECT | \
    MMAL_DISPLAY_SET_DEST_RECT | \
    MMAL_DISPLAY_SET_LAYER | \
    MMAL_DISPLAY_SET_ALPHA;

  status = mmal_port_parameter_set(input, &region.hdr);

  if (status != MMAL_SUCCESS)
    {
    gavl_log(GAVL_LOG_ERROR, LOG_DOMAIN, "Failed to set display region (status=%"PRIx32" %s)",
           status, mmal_status_to_string(status));
    return 0;
    } 
  
  return 1;
  }


static void control_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
  {
  MMAL_STATUS_T status;
  
  if (buffer->cmd == MMAL_EVENT_ERROR)
    {
    status = *(uint32_t *)buffer->data;
    gavl_log(GAVL_LOG_ERROR, LOG_DOMAIN, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status));
    }
  
  mmal_buffer_header_release(buffer);
  }

static int open_mmal(driver_data_t * d)
  {
  bg_x11_window_t * w;
  mmal_priv_t * priv;
  MMAL_PORT_T *input;
  MMAL_STATUS_T status;

  
  w = d->win;

  priv = d->priv;
  
  if(mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &priv->renderer) != MMAL_SUCCESS)
    {
    gavl_log(GAVL_LOG_ERROR, LOG_DOMAIN, "Creating renderer component failed");
    return 0;
    }

  
  gavl_log(GAVL_LOG_INFO, LOG_DOMAIN, "Created renderer component %d %d", priv->renderer->input_num,  priv->renderer->output_num);
  
  input = priv->renderer->input[0];

  input->format->type = MMAL_ES_TYPE_VIDEO;
  input->format->es->video.width  = w->video_format.frame_width;
  input->format->es->video.height = w->video_format.frame_height;
  input->format->es->video.crop.x = 0;
  input->format->es->video.crop.y = 0;
  input->format->es->video.crop.width  = w->video_format.image_width;
  input->format->es->video.crop.height = w->video_format.image_height;
  
  if(w->video_format.hwctx)
    {
    gavl_log(GAVL_LOG_INFO, LOG_DOMAIN, "Using MMAL (direct)");

    priv->hwctx = w->video_format.hwctx;
    SET_FLAG(w, FLAG_NO_GET_FRAME);
    
    input->format->encoding = MMAL_ENCODING_OPAQUE;
    }
  else
    {
    gavl_log(GAVL_LOG_INFO, LOG_DOMAIN, "Using MMAL (indirect)");
    priv->hwctx = priv->hwctx_priv;
    
    /* RAM video not supported yet */
    }
  
  if((status = mmal_port_format_commit(input)) != MMAL_SUCCESS)
    {
    gavl_log(GAVL_LOG_ERROR, LOG_DOMAIN, "Setting format failed (status=%"PRIx32" %s)",
           status, mmal_status_to_string(status));
    }
  
  if((status = mmal_component_enable(priv->renderer)) != MMAL_SUCCESS)
    {
    gavl_log(GAVL_LOG_ERROR, LOG_DOMAIN, "Enabling component failed (status=%"PRIx32" %s)",
           status, mmal_status_to_string(status));
    } 

  if(w->video_format.hwctx)
    mmal_port_parameter_set_boolean(input, MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE);


  input->buffer_size = input->buffer_size_recommended;
  input->buffer_num = input->buffer_num_recommended;

  priv->pool = mmal_port_pool_create(input, input->buffer_num, input->buffer_size);
  
  if(input->buffer_num < 2)
    input->buffer_num = 2;
  
  
  if((status = mmal_port_enable(priv->renderer->control, control_cb)) != MMAL_SUCCESS)
    {
    gavl_log(GAVL_LOG_ERROR, LOG_DOMAIN, "Enabling control port failed (status=%"PRIx32" %s)",
           status, mmal_status_to_string(status));
    }
  
  return 1;
  }

static gavl_video_frame_t * create_frame_mmal(driver_data_t * d)
  {
  /* Ignore silently */
  
  // fprintf(stderr, "create_frame_mmal not implemented\n");
  return NULL;
  }

static void put_frame_mmal(driver_data_t * d, gavl_video_frame_t * f)
  {
  bg_x11_window_t * w;
  mmal_priv_t * priv;
  MMAL_STATUS_T status;
  MMAL_PORT_T *input;
  MMAL_BUFFER_HEADER_T * buf_src = f->user_data;
  MMAL_BUFFER_HEADER_T * buf_dst;
  
  w = d->win;
  priv = d->priv;
  
  if(TEST_FLAG(w, FLAG_DRAWING_COORDS_CHANGED))
    {
    fprintf(stderr, "Drawing coords changed %d x %d + %d + %d\n",
            w->window_rect.w, w->window_rect.h, w->window_rect.x, w->window_rect.y);

    set_display_region(d);
    CLEAR_FLAG(w, FLAG_DRAWING_COORDS_CHANGED);
    }

  input = priv->renderer->input[0];
  
  if(!priv->renderer_port_enabled)
    {
    if((status = mmal_port_enable(input, input_cb)) != MMAL_SUCCESS)
      {
      gavl_log(GAVL_LOG_ERROR, LOG_DOMAIN, "Enabling input port failed (status=%"PRIx32" %s)",
             status, mmal_status_to_string(status));
      }
    priv->renderer_port_enabled = 1;
    }

  if(!(buf_dst = mmal_queue_wait(priv->pool->queue)))
    {
    gavl_log(GAVL_LOG_ERROR, LOG_DOMAIN, "mmal_queue_wait failed");
    return;
    }

  if((status = mmal_buffer_header_replicate(buf_dst, buf_src) != MMAL_SUCCESS))
    {
    gavl_log(GAVL_LOG_ERROR, LOG_DOMAIN, "Replicating buffer failed (status=%"PRIx32" %s)",
           status, mmal_status_to_string(status));
    return;
    }
  
  if((status = mmal_port_send_buffer(input, buf_dst)) != MMAL_SUCCESS)
    {
    gavl_log(GAVL_LOG_ERROR, LOG_DOMAIN, "Sending buffer failed (status=%"PRIx32" %s)",
           status, mmal_status_to_string(status));
    }
    
  //  fprintf(stderr, "put_frame_mmal not implemented %p\n", f->user_data);
  }

static gavl_video_frame_t *
create_overlay_mmal(driver_data_t* d, overlay_stream_t * str)
  {
  fprintf(stderr, "create_overlay_mmal not implemented\n");
  return NULL;
  }

static void init_overlay_stream_mmal(driver_data_t* d, overlay_stream_t * str)
  {
  
  }

static void close_mmal(driver_data_t * d)
  {
  
  }

static void cleanup_mmal(driver_data_t * d)
  {
  
  }

static void set_overlay_mmal(driver_data_t* d, overlay_stream_t * str)
  {
  
  }
  
const video_driver_t mmal_driver =
  {
    .name                = "MMAL",
    .can_scale           = 1,
    .hwtype             = GAVL_HW_MMAL,
    .init                = init_mmal,
    .open                = open_mmal,
    //    .set_brightness      = set_brightness_mmal,
    //    .set_saturation      = set_saturation_mmal,
    //    .set_contrast        = set_contrast_mmal,

    .create_frame        = create_frame_mmal,
    .put_frame           = put_frame_mmal,
    .close               = close_mmal,
    .cleanup             = cleanup_mmal,
    .init_overlay_stream = init_overlay_stream_mmal,
    .create_overlay      = create_overlay_mmal,
    .set_overlay         = set_overlay_mmal,
  };

