/*******************************************************************************
*  Copyright 1991-1996 by ORCA Software, Inc.                                  *
*                                                                              *
*  All rights reserved.  May not be reproduced or distributed, in printed or   *
*  electronic form, without permission of ORCA Software, Inc.  May not be      *
*  distributed as object code, separately or linked with other object modules, *
*  without permission.                                                         *
*******************************************************************************/

#define XI_INTERNAL
#define XI_R3_COMPAT
#include "xi.h"
#include "xiutils.h"

#if XIWS != XIWS_MAC && XIWS != XIWS_XM && XIWS != XIWS_WXGTK
#include <io.h>
#endif

#if XIWS == XIWS_XM || XIWS == XIWS_WXGTK
#include <unistd.h>
#endif

XI_BITMAP *
xi_bitmap_create( char *filename, XI_BITMAP_MODE mode )
{
  XI_BITMAP *bitmap;

  bitmap = XinMemoryZeroAlloc( sizeof( XI_BITMAP ) );
  if ( filename != NULL )
  {
#if XIWS != XIWS_MAC
    if ( access( filename, 0 ) != -1 )
#endif
      bitmap->xin_bitmap = XinBitmapRead( filename );
    if ( bitmap->xin_bitmap == NULL )
    {
      XinMemoryFree( bitmap );
      return NULL;
    }
  }
  bitmap->mode = mode;
  bitmap->ref_count = 1;
  return bitmap;
}

XI_BITMAP *
xi_bitmap_create_res( short id, XI_BITMAP_MODE mode )
{
  XI_BITMAP *bitmap;

  bitmap = XinMemoryZeroAlloc( sizeof( XI_BITMAP ) );
  if ( id != 0 )
  {
    bitmap->xin_bitmap = XinBitmapReadRes( id );
    if ( bitmap->xin_bitmap == NULL )
    {
      XinMemoryFree( bitmap );
      return NULL;
    }
  }
  bitmap->mode = mode;
  bitmap->ref_count = 1;
  return bitmap;
}

XI_BITMAP *
xi_bitmap_copy( XI_BITMAP * bitmap )
{
  if ( bitmap != NULL )
    bitmap->ref_count++;
  return bitmap;
}

void
xi_bitmap_destroy( XI_BITMAP * bitmap )
{
  if ( bitmap != NULL && --bitmap->ref_count == 0 )
  {
    if ( bitmap->xin_bitmap != NULL )
      XinBitmapDestroy( bitmap->xin_bitmap );
    XinMemoryFree( bitmap );
  }
}

static void
normal_bitmap( XI_BITMAP * bitmap, XinWindow win, XinRect * rct )
{
  short width,
  height;
  int x,
  y;
  XinRect src;
  XinRect dest;
  int full_width,
  full_height;
  XinBrush brush;

  if ( bitmap->xin_bitmap == NULL )
    width = height = 0;
  else
    XinBitmapSizeGet( bitmap->xin_bitmap, &width, &height );
  full_width = rct->right - rct->left;
  full_height = rct->bottom - rct->top;
  if ( bitmap->hcenter )
  {
    if ( width > full_width )
      x = 0;
    else
      x = ( full_width - width ) / 2;
  }
  else
    x = bitmap->x_offset;
  if ( bitmap->vcenter )
  {
    if ( height > full_height )
      y = 0;
    else
      y = ( full_height - height ) / 2;
  }
  else
    y = bitmap->y_offset;
  if ( x + width > full_width )
    width = full_width - x;
  if ( y + height > full_height )
    height = full_height - y;
  if ( bitmap->xin_bitmap != 0 )
  {
    dest.top = rct->top + y;
    dest.left = rct->left + x;
    dest.bottom = dest.top + height;
    dest.right = dest.left + width;
    src.top = src.left = 0;
    src.right = width;
    src.bottom = height;
    XinWindowBitmapDraw( win, bitmap->xin_bitmap, &dest, &src );
  }

  /* Draw background color around bitmap */
  XinWindowPenSet( win, &hollow_cpen );
  brush.pattern = XinBrushSolid;
  brush.fore_color = bitmap->background;
  XinWindowBrushSet( win, &brush );
  if ( y > 0 )
  {                             /* Draw top rectangle */
    dest.top = rct->top;
    dest.left = rct->left;
    dest.bottom = rct->top + y;
    dest.right = rct->right;
    xi_draw_rect( win, &dest );
  }
  if ( x > 0 )
  {                             /* Draw left rectangle */
    dest.top = rct->top + y;
    dest.left = rct->left;
    dest.bottom = dest.top + height;
    dest.right = rct->left + x;
    xi_draw_rect( win, &dest );
  }
  if ( x + width < full_width )
  {                             /* Draw right rectangle */
    dest.top = rct->top + y;
    dest.left = rct->left + x + width;
    dest.bottom = dest.top + height;
    dest.right = rct->right;
    xi_draw_rect( win, &dest );
  }
  if ( y + height < full_height )
  {                             /* Draw bottom rectangle */
    dest.top = rct->top + y + height;
    dest.left = rct->left;
    dest.bottom = rct->bottom;
    dest.right = rct->right;
    xi_draw_rect( win, &dest );
  }
}

static void
resize_bitmap( XI_BITMAP * bitmap, XinWindow win, XinRect * rct )
{
  short width,
  height;
  XinRect src;

  XinBitmapSizeGet( bitmap->xin_bitmap, &width, &height );
  src.top = src.left = 0;
  src.right = width;
  src.bottom = height;
  XinWindowBitmapDraw( win, bitmap->xin_bitmap, rct, &src );
}

static void
tile_bitmap( XI_BITMAP * bitmap, XinWindow win, XinRect * rct, XinRect * clip_rct, BOOLEAN on_update )
{
  short width,
  height;
  XinRect src;
  XinRect dest;
  int full_width,
  full_height;
  int x,
  y;

  XinBitmapSizeGet( bitmap->xin_bitmap, &width, &height );
  full_width = rct->right - rct->left;
  full_height = rct->bottom - rct->top;
  src.top = 0;
  src.left = 0;
  src.bottom = height;
  for ( y = 0; y < full_height; y += height )
  {
    dest.top = rct->top + y;
    if ( clip_rct != NULL )
    {
      if ( dest.top > clip_rct->bottom )
        break;
      if ( dest.top + height < clip_rct->top )
        continue;
    }
    if ( y + height > full_height )
      src.bottom = full_height - y;
    src.right = width;
    for ( x = 0; x < full_width; x += width )
    {
      if ( x + width > full_width )
        src.right = full_width - x;
      dest.left = rct->left + x;
      dest.right = dest.left + src.right;
      dest.bottom = dest.top + src.bottom;
      if ( clip_rct != NULL )
      {
        if ( dest.left > clip_rct->right )
          break;
        if ( dest.right < clip_rct->left )
          continue;
      }
      if ( !on_update || XinWindowPaintNeeds( win, &dest ) )
        XinWindowBitmapDraw( win, bitmap->xin_bitmap, &dest, &src );
    }
  }
}

void
xi_bitmap_draw( XI_BITMAP * bitmap, XinWindow win, XinRect * rct,
                XinRect * clip_rct, BOOLEAN in_paint_event )
{
  XI_BITMAP_MODE mode;

  if ( bitmap == NULL )
    return;

  if ( bitmap->xin_bitmap == NULL )
    mode = XI_BITMAP_NORMAL;
  else
    mode = bitmap->mode;
  XinWindowClipSet( win, clip_rct );
  switch ( mode )
  {
    case XI_BITMAP_NORMAL:
      normal_bitmap( bitmap, win, rct );
      break;
    case XI_BITMAP_RESIZE:
      resize_bitmap( bitmap, win, rct );
      break;
    case XI_BITMAP_TILE:
      tile_bitmap( bitmap, win, rct, clip_rct, in_paint_event );
      break;
  }
}

void
xi_bitmap_size_get( XI_BITMAP * bitmap, short *width, short *height )
{
  if ( bitmap != NULL && bitmap->xin_bitmap != NULL )
    XinBitmapSizeGet( bitmap->xin_bitmap, width, height );
}

BOOLEAN
xi_bitmap_draw_all_on_resize( XI_BITMAP * bitmap )
{
  if ( bitmap == NULL || bitmap->xin_bitmap == NULL )
    return FALSE;
  return ( ( bitmap->mode == XI_BITMAP_NORMAL && ( bitmap->vcenter || bitmap->hcenter ) )
          || bitmap->mode == XI_BITMAP_RESIZE );
}