/*
   (c) Copyright 2001-2009  The world wide DirectFB Open Source Community (directfb.org)
   (c) Copyright 2000-2004  Convergence (integrated media) GmbH

   All rights reserved.

   Written by Denis Oliver Kropp <dok@directfb.org>,
              Andreas Hundt <andi@fischlustig.de>,
              Sven Neumann <neo@directfb.org>,
              Ville Syrjälä <syrjala@sci.fi> and
              Claudio Ciccani <klan@users.sf.net>.

   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; either
   version 2 of the License, or (at your option) any later version.

   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., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*/

#ifndef __CORE__SURFACE_H__
#define __CORE__SURFACE_H__

#include <directfb.h>

#include <direct/list.h>
#include <direct/serial.h>
#include <direct/util.h>

#include <fusion/object.h>
#include <fusion/reactor.h>

#include <core/coredefs.h>
#include <core/coretypes.h>


typedef enum {
     CSNF_NONE           = 0x00000000,

     CSNF_SIZEFORMAT     = 0x00000001,  /* width, height, format */
     CSNF_SYSTEM         = 0x00000002,  /* system instance information */
     CSNF_VIDEO          = 0x00000004,  /* video instance information */
     CSNF_DESTROY        = 0x00000008,  /* surface is about to be destroyed */
     CSNF_FLIP           = 0x00000010,  /* surface buffer pointer swapped */
     CSNF_FIELD          = 0x00000020,  /* active (displayed) field switched */
     CSNF_PALETTE_CHANGE = 0x00000040,  /* another palette has been set */
     CSNF_PALETTE_UPDATE = 0x00000080,  /* current palette has been altered */
     CSNF_ALPHA_RAMP     = 0x00000100,  /* alpha ramp was modified */

     CSNF_ALL            = 0x000001FF
} CoreSurfaceNotificationFlags;

typedef struct {
     CoreSurfaceNotificationFlags  flags;
     CoreSurface                  *surface;
} CoreSurfaceNotification;


typedef enum {
     CSCONF_NONE         = 0x00000000,

     CSCONF_SIZE         = 0x00000001,
     CSCONF_FORMAT       = 0x00000002,
     CSCONF_CAPS         = 0x00000004,

     CSCONF_PREALLOCATED = 0x00000010,

     CSCONF_PREALLOCATED_IN_VIDEO = 0x00000020,

     CSCONF_ALL          = 0x00000037
} CoreSurfaceConfigFlags;

typedef enum {
     CSTF_NONE           = 0x00000000,

     CSTF_LAYER          = 0x00000001,  /* surface for layer */
     CSTF_WINDOW         = 0x00000002,  /* surface for window */
     CSTF_CURSOR         = 0x00000004,  /* surface for cursor */
     CSTF_FONT           = 0x00000008,  /* surface for font */

     CSTF_SHARED         = 0x00000010,  /* accessable by other processes */

     CSTF_INTERNAL       = 0x00000100,  /* system memory */
     CSTF_EXTERNAL       = 0x00000200,  /* video memory */

     CSTF_PREALLOCATED   = 0x00000400,  /* preallocated memory */

     CSTF_PREALLOCATED_IN_VIDEO = 0x00000800,  /* preallocated memory in video memory */

     CSTF_MIU0           = 0x00001000,/* allocated video memory in MIU0*/
     CSTF_MIU1           = 0x00002000,/* allocated video memory in MIU1*/
     CSTF_DECLARABLE       = 0x00004000,/* should been allocated in spec pool*/
     CSTF_DEDICATED      = 0x00008000, /* should be allocated from devmem for imageprovider gopc & vec*/

     CSTF_ALL            = 0x0000FF1F
} CoreSurfaceTypeFlags;

typedef struct {
     CoreSurfaceConfigFlags   flags;

     DFBDimension             size;
     DFBSurfacePixelFormat    format;
     DFBSurfaceCapabilities   caps;

     struct {
          void *addr;
          int   pitch;
     }                        preallocated[MAX_SURFACE_BUFFERS];

     DFBDimension             min_size;
} CoreSurfaceConfig;

typedef enum {
     CSP_SYSTEMONLY      = 0x00000000,  /* never try to swap
                                           into video memory */
     CSP_VIDEOLOW        = 0x00000001,  /* try to store in video memory,
                                           low priority */
     CSP_VIDEOHIGH       = 0x00000002,  /* try to store in video memory,
                                           high priority */
     CSP_VIDEOONLY       = 0x00000003   /* always and only
                                           store in video memory */
} CoreSurfacePolicy;

typedef enum {
     CSAF_NONE           = 0x00000000,

     CSAF_READ           = 0x00000001,  /* accessor may read */
     CSAF_WRITE          = 0x00000002,  /* accessor may write */

     CSAF_SHARED         = 0x00000010,  /* other processes can read/write at the same time (shared mapping) */

     CSAF_ALL            = 0x00000013
} CoreSurfaceAccessFlags;

typedef enum {
     CSAID_NONE          = 0x00000000,  /* none or unknown accessor */

     CSAID_CPU           = 0x00000001,  /* local processor, where DirectFB is running on, could be app or sw fallback */

     CSAID_GPU           = 0x00000002,  /* primary accelerator, as in traditional 'gfxcard' core (ACCEL0) */

     CSAID_ACCEL0        = 0x00000002,  /* accelerators, decoders etc. (CSAID_ACCEL0 + accel_id<0-5>) */
     CSAID_ACCEL1        = 0x00000003,
     CSAID_ACCEL2        = 0x00000004,
     CSAID_ACCEL3        = 0x00000005,
     CSAID_ACCEL4        = 0x00000006,
     CSAID_ACCEL5        = 0x00000007,

     CSAID_LAYER0        = 0x00000008,  /* display layers, registered by layer core (CSAID_LAYER0 + layer_id<0-7>) */
     CSAID_LAYER1        = 0x00000009,
     CSAID_LAYER2        = 0x0000000a,
     CSAID_LAYER3        = 0x0000000b,
     CSAID_LAYER4        = 0x0000000c,
     CSAID_LAYER5        = 0x0000000d,
     CSAID_LAYER6        = 0x0000000e,
     CSAID_LAYER7        = 0x0000000f,

     _CSAID_NUM          = 0x00000010,  /* number of statically assigned IDs for usage in static arrays */

     CSAID_ANY           = 0x00000100,  /* any other accessor needs to be registered using IDs starting from here */
} CoreSurfaceAccessorID;

typedef enum {
     CSBR_FRONT          = 0,
     CSBR_BACK           = 1,
     CSBR_IDLE           = 2
} CoreSurfaceBufferRole;

typedef enum {
     CSSF_NONE           = 0x00000000,

     CSSF_DESTROYED      = 0x00000001,  /* surface is being or has been destroyed */

     CSSF_ALL            = 0x00000001
} CoreSurfaceStateFlags;

struct __DFB_CoreSurface
{
     FusionObject             object;
     int                      magic;

     FusionSkirmish           lock;

     CoreSurfaceStateFlags    state;

     CoreSurfaceConfig        config;
     CoreSurfaceTypeFlags     type;
     unsigned long            resource_id;   /* layer id, window id, or user specified */

     int                      rotation;

     CoreSurfaceNotificationFlags notifications;

     DirectSerial             serial;

     int                      field;
     u8                       alpha_ramp[4];

     CoreSurfaceBuffer       *buffers[MAX_SURFACE_BUFFERS];
     int                      num_buffers;

     int                      buffer_indices[MAX_SURFACE_BUFFERS];

     unsigned int             flips;

     CorePalette             *palette;
     GlobalReaction           palette_reaction;

     FusionSHMPoolShared     *shmpool;
};


/*
 * Creates a pool of surface objects.
 */
FusionObjectPool *dfb_surface_pool_create( const FusionWorld *world );

/*
 * Generates dfb_surface_ref(), dfb_surface_attach() etc.
 */
FUSION_OBJECT_METHODS( CoreSurface, dfb_surface )


DFBResult dfb_surface_create        ( CoreDFB                      *core,
                                      const CoreSurfaceConfig      *config,
                                      CoreSurfaceTypeFlags          type,
                                      unsigned long                 resource_id,
                                      CorePalette                  *palette,
                                      CoreSurface                 **ret_surface );

DFBResult dfb_surface_create_simple ( CoreDFB                      *core,
                                      int                           width,
                                      int                           height,
                                      DFBSurfacePixelFormat         format,
                                      DFBSurfaceCapabilities        caps,
                                      CoreSurfaceTypeFlags          type,
                                      unsigned long                 resource_id,
                                      CorePalette                  *palette,
                                      CoreSurface                 **ret_surface );

DFBResult dfb_surface_init_palette  ( CoreDFB                      *core,
                                      CoreSurface                  *surface );

DFBResult dfb_surface_notify        ( CoreSurface                  *surface,
                                      CoreSurfaceNotificationFlags  flags);

DFBResult dfb_surface_flip          ( CoreSurface                  *surface,
                                      bool                          swap );

DFBResult dfb_surface_reconfig      ( CoreSurface                  *surface,
                                      const CoreSurfaceConfig      *config );

DFBResult dfb_surface_destroy_buffers( CoreSurface                 *surface );

DFBResult dfb_surface_destroy_allocations( CoreSurface *surface );

DFBResult dfb_surface_sync_allocations( CoreSurface *surface, DFBSurfaceSyncFlags flag);


DFBResult dfb_surface_lock_buffer   ( CoreSurface                  *surface,
                                      CoreSurfaceBufferRole         role,
                                      CoreSurfaceAccessorID         accessor,
                                      CoreSurfaceAccessFlags        access,
                                      CoreSurfaceBufferLock        *ret_lock );

DFBResult dfb_surface_unlock_buffer ( CoreSurface                  *surface,
                                      CoreSurfaceBufferLock        *lock );

DFBResult dfb_surface_read_buffer   ( CoreSurface                  *surface,
                                      CoreSurfaceBufferRole         role,
                                      void                         *destination,
                                      int                           pitch,
                                      const DFBRectangle           *rect );

DFBResult dfb_surface_write_buffer  ( CoreSurface                  *surface,
                                      CoreSurfaceBufferRole         role,
                                      const void                   *source,
                                      int                           pitch,
                                      const DFBRectangle           *rect );

DFBResult dfb_surface_dump_buffer   ( CoreSurface                  *surface,
                                      CoreSurfaceBufferRole         role,
                                      const char                   *path,
                                      const char                   *prefix );

DFBResult dfb_surface_set_palette   ( CoreSurface                  *surface,
                                      CorePalette                  *palette );

DFBResult dfb_surface_set_field     ( CoreSurface                  *surface,
                                      int                           field );

DFBResult dfb_surface_set_alpha_ramp( CoreSurface                  *surface,
                                      u8                            a0,
                                      u8                            a1,
                                      u8                            a2,
                                      u8                            a3 );


static inline DirectResult
dfb_surface_lock( CoreSurface *surface )
{
     D_MAGIC_ASSERT( surface, CoreSurface );

     return fusion_skirmish_prevail( &surface->lock );
}

static inline DirectResult
dfb_surface_trylock( CoreSurface *surface )
{
     D_MAGIC_ASSERT( surface, CoreSurface );

     return fusion_skirmish_swoop( &surface->lock );
}

static inline DirectResult
dfb_surface_unlock( CoreSurface *surface )
{
     D_MAGIC_ASSERT( surface, CoreSurface );

     return fusion_skirmish_dismiss( &surface->lock );
}

static inline CoreSurfaceBuffer *
dfb_surface_get_buffer( CoreSurface           *surface,
                        CoreSurfaceBufferRole  role )
{
     D_MAGIC_ASSERT( surface, CoreSurface );
     D_ASSERT( role == CSBR_FRONT || role == CSBR_BACK || role == CSBR_IDLE );

     D_ASSERT( surface->num_buffers > 0 );

     return surface->buffers[ surface->buffer_indices[(surface->flips + role) % surface->num_buffers] ];
}

static inline void *
dfb_surface_data_offset( const CoreSurface *surface,
                         void              *data,
                         int                pitch,
                         int                x,
                         int                y )
{
     D_ASSERT( surface != NULL );
     D_ASSERT( data != NULL );
     D_ASSERT( pitch > 0 );
     D_ASSERT( x >= 0 );
     D_ASSERT( x < surface->config.size.w );
     D_ASSERT( y >= 0 );
     D_ASSERT( y < surface->config.size.h );

     if (surface->config.caps & DSCAPS_SEPARATED) {
          if (y & 1)
               y += surface->config.size.h;

          y >>= 1;
     }

     return (u8*)data + pitch * y + DFB_BYTES_PER_LINE( surface->config.format, x );
}

static inline void
dfb_surface_calc_buffer_size( CoreSurface *surface,
                              int          byte_align,
                              int          pixel_align,
                              int         *ret_pitch,
                              int         *ret_size )
{
     DFBSurfacePixelFormat format;
     int                   width;
     int                   pitch;

     D_MAGIC_ASSERT( surface, CoreSurface );

     format = surface->config.format;

     width = direct_util_align( surface->config.size.w, pixel_align );
     pitch = direct_util_align( DFB_BYTES_PER_LINE( format, width ), byte_align );

     if (ret_pitch)
          *ret_pitch = pitch;

     if (ret_size)
          *ret_size = pitch * DFB_PLANE_MULTIPLY( format, surface->config.size.h );
}

static inline void
dfb_surface_caps_apply_policy( CoreSurfacePolicy       policy,
                               DFBSurfaceCapabilities *caps )
{
     switch (policy) {
          case CSP_SYSTEMONLY:
               *caps = (DFBSurfaceCapabilities)((*caps & ~(DSCAPS_VIDEOONLY|DSCAPS_VIDEOHIGH)) | DSCAPS_SYSTEMONLY);
               break;

          case CSP_VIDEOONLY:
               *caps = (DFBSurfaceCapabilities)((*caps & ~(DSCAPS_SYSTEMONLY|DSCAPS_VIDEOHIGH)) | DSCAPS_VIDEOONLY);
               break;

          case CSP_VIDEOHIGH:
               *caps = (DFBSurfaceCapabilities)((*caps & ~(DSCAPS_SYSTEMONLY | DSCAPS_VIDEOONLY)) | DSCAPS_VIDEOHIGH);
               break;

          default:
               *caps = (DFBSurfaceCapabilities)(*caps & ~(DSCAPS_SYSTEMONLY | DSCAPS_VIDEOONLY|DSCAPS_VIDEOHIGH));
               break;
     }
}

static inline DFBResult
dfb_surface_resize( CoreSurface *surface,
                    int          width,
                    int          height )
{
     CoreSurfaceConfig config;

     D_MAGIC_ASSERT( surface, CoreSurface );
     D_ASSERT( width > 0 );
     D_ASSERT( height > 0 );

     config.flags  = CSCONF_SIZE;
     config.size.w = width;
     config.size.h = height;

     return dfb_surface_reconfig( surface, &config );
}

static inline DFBResult
dfb_surface_reformat( CoreSurface           *surface,
                      int                    width,
                      int                    height,
                      DFBSurfacePixelFormat  format )
{
     CoreSurfaceConfig config;

     D_MAGIC_ASSERT( surface, CoreSurface );
     D_ASSERT( width > 0 );
     D_ASSERT( height > 0 );

     config.flags  = (CoreSurfaceConfigFlags)(CSCONF_SIZE | CSCONF_FORMAT);
     config.size.w = width;
     config.size.h = height;
     config.format = format;

     return dfb_surface_reconfig( surface, &config );
}

/* global reactions */
ReactionResult _dfb_surface_palette_listener( const void *msg_data,
                                              void       *ctx );

typedef enum {
     DFB_LAYER_REGION_SURFACE_LISTENER,
     DFB_WINDOWSTACK_BACKGROUND_IMAGE_LISTENER
} DFB_SURFACE_GLOBALS;

#endif

