/*
   Copyright (c) 2003 Andreas Robinson, All rights reserved.

   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.
*/

#include <config.h>

#include <gfx/convert.h>
#include "unichrome.h"
#include "uc_state.h"
#include "uc_accel.h"
#include "uc_hw.h"

enum uc_state_type {
     UC_TYPE_UNSUPPORTED,
     UC_TYPE_2D,
     UC_TYPE_3D
};

/// GPU selecting functions --------------------------------------------------

static inline bool
uc_has_dst_format( DFBSurfacePixelFormat format )
{
     switch (format) {
          case DSPF_ARGB1555:
          case DSPF_RGB16:
          case DSPF_RGB32:
          case DSPF_ARGB:
               return true;

          default:
               break;
     }

     return false;
}

static inline bool
uc_has_src_format_3d( DFBSurfacePixelFormat format )
{
     switch (format) {
          case DSPF_ARGB1555:
          case DSPF_RGB16:
          case DSPF_RGB32:
          case DSPF_ARGB:
          case DSPF_A8:
          case DSPF_LUT8:
               return true;

          default:
               break;
     }

     return false;
}

static inline enum uc_state_type
uc_select_drawtype( CardState* state,
                    DFBAccelerationMask accel )
{
     if (!(state->drawingflags & ~UC_DRAWING_FLAGS_2D) &&
         !(accel & DFXL_FILLTRIANGLE))
          return UC_TYPE_2D;

     if (!(state->drawingflags & ~UC_DRAWING_FLAGS_3D))
          return UC_TYPE_3D;

     return UC_TYPE_UNSUPPORTED;
}

static inline enum uc_state_type
uc_select_blittype( CardState* state,
                    DFBAccelerationMask accel )
{
     if (!(state->blittingflags & ~UC_BLITTING_FLAGS_2D)) {
          if ((state->source->config.format == state->destination->config.format) &&
              !((state->blittingflags & DSBLIT_SRC_COLORKEY) &&
                (state->blittingflags & DSBLIT_DST_COLORKEY)) &&
              !(accel & (DFXL_STRETCHBLIT | DFXL_TEXTRIANGLES)))
               return UC_TYPE_2D;
     }

     if (!(state->blittingflags & ~UC_BLITTING_FLAGS_3D)) {
          if (uc_has_src_format_3d( state->source->config.format ))
               return UC_TYPE_3D;
     }

     return UC_TYPE_UNSUPPORTED;
}

// DirectFB interfacing functions --------------------------------------------

void uc_check_state(void *drv, void *dev,
                    CardState *state, DFBAccelerationMask accel)
{
     /* Check destination format. */
     if (!uc_has_dst_format( state->destination->config.format ))
          return;

     if (DFB_DRAWING_FUNCTION(accel)) {
          /* Check drawing parameters. */
          switch (uc_select_drawtype(state, accel)) {
               case UC_TYPE_2D:
                    state->accel |= UC_DRAWING_FUNCTIONS_2D;
                    break;
               case UC_TYPE_3D:
                    state->accel |= UC_DRAWING_FUNCTIONS_3D;
                    break;
               default:
                    return;
          }
     }
     else {
          /* Check blitting parameters. */
          switch (uc_select_blittype(state, accel)) {
               case UC_TYPE_2D:
                    state->accel |= UC_BLITTING_FUNCTIONS_2D;
                    break;
               case UC_TYPE_3D:
                    state->accel |= UC_BLITTING_FUNCTIONS_3D;
                    break;
               default:
                    return;
          }
     }
}

void uc_set_state(void *drv, void *dev, GraphicsDeviceFuncs *funcs,
                  CardState *state, DFBAccelerationMask accel)
{
     UcDriverData   *ucdrv = (UcDriverData*) drv;
     UcDeviceData   *ucdev = (UcDeviceData*) dev;
     struct uc_fifo *fifo  = ucdrv->fifo;

     u32 rop3d     = HC_HROP_P;
     u32 regEnable = HC_HenCW_MASK | HC_HenAW_MASK;

     StateModificationFlags modified = state->mod_hw;

     // Check modified states and update hw

     if (modified & SMF_SOURCE)
          UC_INVALIDATE( uc_source2d );

     if (modified & (SMF_BLITTING_FLAGS | SMF_SOURCE))
          UC_INVALIDATE( uc_source3d | uc_texenv );

     if (modified & (SMF_BLITTING_FLAGS | SMF_SRC_COLORKEY | SMF_DST_COLORKEY))
          UC_INVALIDATE( uc_colorkey2d );

     if (modified & (SMF_COLOR | SMF_DESTINATION | SMF_DRAWING_FLAGS))
          UC_INVALIDATE( uc_color2d );

     if (modified & (SMF_SRC_BLEND | SMF_DST_BLEND))
          UC_INVALIDATE( uc_blending_fn );


     if (modified & SMF_COLOR)
          ucdev->color3d = PIXEL_ARGB( state->color.a, state->color.r,
                                       state->color.g, state->color.b );

     if (modified & SMF_DRAWING_FLAGS) {
          if (state->drawingflags & DSDRAW_XOR) {
               ucdev->draw_rop3d = HC_HROP_DPx;
               ucdev->draw_rop2d = VIA_ROP_DPx;
          }
          else {
               ucdev->draw_rop3d = HC_HROP_P;
               ucdev->draw_rop2d = VIA_ROP_P;
          }
     }

     ucdev->bflags = state->blittingflags;

     if (modified & SMF_DESTINATION)
          uc_set_destination(ucdrv, ucdev, state);

     if (modified & SMF_CLIP)
          uc_set_clip(ucdrv, ucdev, state);


     // Select GPU and check remaining states

     if (DFB_DRAWING_FUNCTION(accel)) {

          switch (uc_select_drawtype(state, accel)) {
               case UC_TYPE_2D:
                    funcs->FillRectangle = uc_fill_rectangle;
                    funcs->DrawRectangle = uc_draw_rectangle;
                    funcs->DrawLine = uc_draw_line;

                    uc_set_color_2d(ucdrv, ucdev, state);

                    state->set = UC_DRAWING_FUNCTIONS_2D;
                    break;

               case UC_TYPE_3D:
                    funcs->FillRectangle = uc_fill_rectangle_3d;
                    funcs->DrawRectangle = uc_draw_rectangle_3d;
                    funcs->DrawLine = uc_draw_line_3d;

                    if (state->drawingflags & DSDRAW_BLEND) {
                         uc_set_blending_fn(ucdrv, ucdev, state);
                         regEnable |= HC_HenABL_MASK;
                    }

                    rop3d = ucdev->draw_rop3d;

                    state->set = UC_DRAWING_FUNCTIONS_3D;
                    break;

               case UC_TYPE_UNSUPPORTED:
                    D_BUG("Unsupported drawing function!");
                    break;
          }
     }
     else { // DFB_BLITTING_FUNCTION(accel)
          switch (uc_select_blittype(state, accel)) {
               case UC_TYPE_2D:
                    uc_set_source_2d(ucdrv, ucdev, state);
                    funcs->Blit = uc_blit;

                    uc_set_colorkey_2d(ucdrv, ucdev, state);
                    state->set = UC_BLITTING_FUNCTIONS_2D;
                    break;

               case UC_TYPE_3D:
                    funcs->Blit = uc_blit_3d;
                    uc_set_source_3d(ucdrv, ucdev, state);
                    uc_set_texenv(ucdrv, ucdev, state);
                    uc_set_blending_fn(ucdrv, ucdev, state);

                    regEnable |= HC_HenTXMP_MASK | HC_HenTXCH_MASK | HC_HenTXPP_MASK | HC_HenDT_MASK;

                    if (state->blittingflags & (DSBLIT_BLEND_ALPHACHANNEL |
                                                DSBLIT_BLEND_COLORALPHA))
                         regEnable |= HC_HenABL_MASK;

                    state->set = UC_BLITTING_FUNCTIONS_3D;
                    break;

               case UC_TYPE_UNSUPPORTED:
                    D_BUG("Unsupported drawing function!");
                    break;
          }
     }

#ifdef UC_ENABLE_3D
     UC_FIFO_PREPARE( fifo, 6 );
     UC_FIFO_ADD_HDR( fifo, HC_ParaType_NotTex << 16 );

     /* Don't know what this does. DRI code always clears it. */
     UC_FIFO_ADD_3D ( fifo, HC_SubA_HPixGC,   0 );

     UC_FIFO_ADD_3D ( fifo, HC_SubA_HEnable,  regEnable );
     UC_FIFO_ADD_3D ( fifo, HC_SubA_HFBBMSKL, 0xffffff );
     UC_FIFO_ADD_3D ( fifo, HC_SubA_HROP,     rop3d | 0xff );
#endif

     UC_FIFO_CHECK(fifo);

     state->mod_hw = 0;
}

