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

#include "clutter-stage-fruity.h"
#include "clutter-fruity.h"
#include "clutter-backend-fruity.h"

#include "../clutter-main.h"
#include "../clutter-feature.h"
#include "../clutter-color.h"
#include "../clutter-util.h"
#include "../clutter-event.h"
#include "../clutter-enum-types.h"
#include "../clutter-private.h"
#include "../clutter-debug.h"
#include "../clutter-units.h"
#include "../clutter-stage.h"
#include "../clutter-stage-window.h"

static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface);

G_DEFINE_TYPE_WITH_CODE (ClutterStageEGL,
                         clutter_stage_egl,
                         CLUTTER_TYPE_ACTOR,
                         G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW,
                                                clutter_stage_window_iface_init));

static void
clutter_stage_egl_show (ClutterActor *actor)
{
  CLUTTER_ACTOR_SET_FLAGS (actor, CLUTTER_ACTOR_MAPPED);
}

static void
clutter_stage_egl_hide (ClutterActor *actor)
{
  CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_MAPPED);
}

static void
clutter_stage_egl_unrealize (ClutterActor *actor)
{
  ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (actor);

  CLUTTER_MARK();

  if (CLUTTER_ACTOR_CLASS (clutter_stage_egl_parent_class)->unrealize != NULL)
    CLUTTER_ACTOR_CLASS (clutter_stage_egl_parent_class)->unrealize (actor);

  if (stage_egl->egl_surface)
    {
      eglDestroySurface (clutter_fruity_get_egl_display (),
                         stage_egl->egl_surface);
      stage_egl->egl_surface = EGL_NO_SURFACE;
    }
}

static void
clutter_stage_egl_realize (ClutterActor *actor)
{
  ClutterStageEGL     *stage_egl = CLUTTER_STAGE_EGL (actor);
  ClutterBackendEGL   *backend_egl;
  EGLConfig            configs[2];
  EGLint               config_count;
  EGLBoolean           status;
  EGLint               cfg_attribs[] = {
      EGL_BUFFER_SIZE,    EGL_DONT_CARE,
      EGL_RED_SIZE,       5,
      EGL_GREEN_SIZE,     6,
      EGL_BLUE_SIZE,      5,
      EGL_DEPTH_SIZE,     16,
      EGL_ALPHA_SIZE,     EGL_DONT_CARE,
      EGL_STENCIL_SIZE,   2,
      EGL_SURFACE_TYPE,   EGL_WINDOW_BIT,
      EGL_NONE };

  CLUTTER_NOTE (BACKEND, "Realizing main stage");

  backend_egl = CLUTTER_BACKEND_EGL (clutter_get_default_backend ());

  status = eglGetConfigs (backend_egl->edpy,
                          configs,
                          2,
                          &config_count);

  if (status != EGL_TRUE)
    g_warning ("eglGetConfigs failed");

  status = eglChooseConfig (backend_egl->edpy,
                            cfg_attribs,
                            configs,
                            G_N_ELEMENTS (configs),
                            &config_count);

  if (status != EGL_TRUE)
    {
      g_critical ("eglChooseConfig failed");
      CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
      return;
    }

  if (stage_egl->egl_surface != EGL_NO_SURFACE)
    {
      eglDestroySurface (backend_egl->edpy, stage_egl->egl_surface);
      stage_egl->egl_surface = EGL_NO_SURFACE;
    }

   if (backend_egl->egl_context)
     {
        eglDestroyContext (backend_egl->edpy, backend_egl->egl_context);
        backend_egl->egl_context = NULL;
     }

  stage_egl->egl_surface =
    eglCreateWindowSurface (backend_egl->edpy,
                            configs[0],
                            NULL,
                            NULL);



  if (stage_egl->egl_surface == EGL_NO_SURFACE)
    {
      g_critical ("Unable to create an EGL surface");
      CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
      return;
    }

  eglQuerySurface (backend_egl->edpy,
                   stage_egl->egl_surface,
                   EGL_WIDTH,
                   &stage_egl->surface_width);

  eglQuerySurface (backend_egl->edpy,
                   stage_egl->egl_surface,
                   EGL_HEIGHT,
                   &stage_egl->surface_height);

  CLUTTER_NOTE (BACKEND, "EGL surface is %ix%i",
                stage_egl->surface_width,
                stage_egl->surface_height);


  if (G_UNLIKELY (backend_egl->egl_context == NULL))
    {
      CLUTTER_NOTE (GL, "Creating EGL Context");

      backend_egl->egl_context = eglCreateContext (backend_egl->edpy,
                                                   configs[0],
                                                   EGL_NO_CONTEXT,
                                                   NULL);

      if (backend_egl->egl_context == EGL_NO_CONTEXT)
        {
          g_critical ("Unable to create a suitable EGL context");

          CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
          return;
        }
    }

  /* this will make sure to set the current context */
  CLUTTER_NOTE (BACKEND, "Setting context");

  /* this should be done in ClutterBackend::ensure_context */
  status = eglMakeCurrent (backend_egl->edpy,
                           stage_egl->egl_surface,
                           stage_egl->egl_surface,
                           backend_egl->egl_context);

  if (status != EGL_TRUE)
    {
      g_critical ("eglMakeCurrent failed");
      CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED);
      return;
    }
}

static void
clutter_stage_egl_get_preferred_width (ClutterActor *self,
                                       gfloat        for_height,
                                       gfloat       *min_width_p,
                                       gfloat       *natural_width_p)
{
  ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (self);

  if (min_width_p)
    *min_width_p = CLUTTER_UNITS_FROM_DEVICE (stage_egl->surface_width);

  if (natural_width_p)
    *natural_width_p = CLUTTER_UNITS_FROM_DEVICE (stage_egl->surface_width);
}

static void
clutter_stage_egl_get_preferred_height (ClutterActor *self,
                                        gfloat        for_width,
                                        gfloat       *min_height_p,
                                        gfloat       *natural_height_p)
{
  ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (self);

  if (min_height_p)
    *min_height_p = CLUTTER_UNITS_FROM_DEVICE (stage_egl->surface_height);

  if (natural_height_p)
    *natural_height_p = CLUTTER_UNITS_FROM_DEVICE (stage_egl->surface_height);
}

static void
clutter_stage_egl_dispose (GObject *gobject)
{
  ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (gobject);

  G_OBJECT_CLASS (clutter_stage_egl_parent_class)->dispose (gobject);
}

static void
clutter_stage_egl_class_init (ClutterStageEGLClass *klass)
{
  GObjectClass      *gobject_class = G_OBJECT_CLASS (klass);
  ClutterActorClass *actor_class   = CLUTTER_ACTOR_CLASS (klass);

  gobject_class->dispose = clutter_stage_egl_dispose;

  actor_class->show           = clutter_stage_egl_show;
  actor_class->hide           = clutter_stage_egl_hide;
  actor_class->realize        = clutter_stage_egl_realize;
  actor_class->unrealize      = clutter_stage_egl_unrealize;
  actor_class->get_preferred_width = clutter_stage_egl_get_preferred_width;
  actor_class->get_preferred_height = clutter_stage_egl_get_preferred_height;
}

static void
clutter_stage_egl_set_fullscreen (ClutterStageWindow *stage_window,
                                  gboolean            fullscreen)
{
  g_warning ("Stage of type '%s' do not support ClutterStage::set_fullscreen",
             G_OBJECT_TYPE_NAME (stage_window));
}

static ClutterActor *
clutter_stage_egl_get_wrapper (ClutterStageWindow *stage_window)
{
  return CLUTTER_ACTOR (CLUTTER_STAGE_EGL (stage_window)->wrapper);
}

static void
clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
{
  iface->set_fullscreen = clutter_stage_egl_set_fullscreen;
  iface->set_title = NULL;
  iface->get_wrapper = clutter_stage_egl_get_wrapper;
}

static void
clutter_stage_egl_init (ClutterStageEGL *stage)
{
}
