#include "vncviewer.h"

int clientNum_ex;

/* reduce window */
int reducedWindowWidth;
int reducedWindowHeight;
IMAGEINFO *iinfo[MAXCLIENT];
int expandedWindowWidth;
int expandedWindowHeight;
IMAGEINFO *iinfo_ex;
void CreateImageInfo(gboolean flag);
void reduceImage(GdkImage *src_image, GdkImage *dst_image, int x, int y, int w, int h);
void reduceXYWH( int originalX, int originalY, int originalWidth, int originalHeight,
                 int *reducedX, int *reducedY, int *reducedWidth, int *reducedHeight, Bool flag);
void FillRectangle( int color, int x, int y, int width, int height, int bpp, GdkImage *image );

void draw_rectangle( GdkDrawable *drawable, GdkGC *gc, gint filled,
		     gint x, gint y, gint width, gint height, int color )
{
  GdkImage *image = gdkimage[clientNum];
  int bpp;

  if (image == NULL) return;
  if( appData.useBGR233 )
    bpp = gdkvisbpp;
  else
    bpp = myFormat.bitsPerPixel;
  
  FillRectangle( color, x, y, width, height, bpp, image );
  if (GETVIEW(clientNum))
    draw_image( pixmap_ex, gc, image, x, y, x, y, width, height );
  if (gdkpixmap[page][da_num] != NULL)
    draw_image( drawable, gc, image, x, y, x, y, width, height );
  return;
}

#define FILLRECT(INTXX)              \
  INTXX *scrXX;                      \
  INTXX fg = (INTXX)color;           \
  for( h = 0; h < height; h++ ){     \
    if(y+h >= image->height) break;  \
    scrXX = (INTXX*)scr;             \
    for( w = 0; w < width; w++ ){    \
      if(x+w >= image->width) continue;\
      if (&scrXX[w] == NULL) break;  \
      scrXX[w] = fg;                 \
    }                                \
    scr += scrWidthInBytes;          \
  }

void FillRectangle( int color, int x, int y, int width, int height, int bpp, GdkImage *image )
{
  int scrWidthInBytes = image->bpl;
  char *scr = image->mem + y * scrWidthInBytes + x * bpp / 8;
  int w, h;

  if ((scr == NULL) || (image == NULL) || (width == 0) || (height == 0)) {
    return;
  }
  switch( bpp ){
  case 8 :{
    FILLRECT( char );
    break;
  }
  case 15 :
  case 16 :{
    FILLRECT( short );
    break;
  }
  case 32 :{
    FILLRECT( int );
    break;
  }
  default :
    fprintf( stderr, "bad bitsPerPixel : %d\n", bpp );
    exit( 1 );
  }
  return;
}

void draw_image( GdkDrawable *drawable, GdkGC *gc, GdkImage *image,
		 gint xsrc, gint ysrc, gint xdest, gint ydest,
		 gint width, gint height )
{
  int reduceX, reduceY, reduceW, reduceH;
  int win_width, win_height;
  GdkImage *s_image;

  if (drawable == NULL) return;
  if (pixmap_ex != drawable) {
    if (gdkpixmap[page][da_num] == NULL) return;
    win_width = reducedWindowWidth;
    win_height = reducedWindowHeight;
    s_image = smallimage[clientNum];
    reduceXYWH(xsrc, ysrc, width, height, &reduceX, &reduceY, &reduceW, &reduceH, False);
  } else {
    win_width = expandedWindowWidth;
    win_height = expandedWindowHeight;
    s_image = smallimage_ex;
    reduceXYWH(xsrc, ysrc, width, height, &reduceX, &reduceY, &reduceW, &reduceH, True);
  }

  if (reduceX < 2) reduceX = 0;
  else reduceX -= 2;
  if (reduceY < 2) reduceY = 0;
  else reduceY -= 2;
  if (reduceX + reduceW + 4 > win_width) reduceW = win_width - reduceX;
  else reduceW += 4;
  if (reduceY + reduceH + 4 > win_height) reduceH = win_height - reduceY;
  else reduceH += 4;
  reduceImage(gdkimage[clientNum], s_image, reduceX, reduceY, reduceW, reduceH);
  gdk_draw_image(drawable, gdkGC, s_image, reduceX, reduceY,
		 reduceX, reduceY, reduceW, reduceH);

  return;
}

void window_copy_area( GdkWindow *window, GdkGC *gc, gint x, gint y,
		       GdkWindow *source_window, gint source_x, gint source_y,
		       gint width, gint height )
{
  char *buf;
  buf = malloc( myFormat.bitsPerPixel * width * height );
  CopyDataFromScreen( buf, source_x, source_y, width, height );
  CopyDataToScreen( buf, x, y, width, height );
  free( buf );
  return;
}

void CreateImageInfo(gboolean flag)
{
  int srcW, srcH, dstW, dstH;
  int c_num;
  IMAGEINFO *iinfo_tmp;

  if (da_num >= draw_info[page].row * draw_info[page].column) return;
  if (!flag) {
    c_num = clientNum;
    if (drawingarea[page][da_num]->allocation.width < gdkimage[c_num]->width) {
      reducedWindowWidth = dstW = drawingarea[page][da_num]->allocation.width;
    } else {
      reducedWindowWidth = dstW = gdkimage[c_num]->width;
    }
    if (drawingarea[page][da_num]->allocation.height < gdkimage[c_num]->height) {
      reducedWindowHeight = dstH = drawingarea[page][da_num]->allocation.height;
    } else {
      reducedWindowHeight = dstH = gdkimage[c_num]->height;
    }
   iinfo_tmp = iinfo[c_num];
  } else {
    c_num = clientNum_ex;
    if ((drawingarea_ex->allocation.width < smallimage_ex->width) &&
	(drawingarea_ex->allocation.height < smallimage_ex->height)) {
      expandedWindowWidth = dstW = drawingarea_ex->allocation.width;
      expandedWindowHeight = dstH = drawingarea_ex->allocation.height;
    } else {
      expandedWindowWidth = dstW = smallimage_ex->width;
      expandedWindowHeight = dstH = smallimage_ex->height;
    }
    iinfo_tmp = iinfo_ex;
  }

  srcW = server_CB.client_grp[c_num]->serverInitMsg.framebufferWidth;
  srcH = server_CB.client_grp[c_num]->serverInitMsg.framebufferHeight;

  if (!iinfo_tmp) {
    if ((iinfo_tmp = malloc (sizeof(IMAGEINFO))) == NULL) {
      fprintf(stderr, "small image infomation malloc failed\n");
      exit(1);
    }
    if (!flag)
      iinfo[c_num] = iinfo_tmp;
    else
      iinfo_ex = iinfo_tmp;
  }

  if (!srcW | !srcH | !dstW | !dstH) exit (-1);
  iinfo_tmp->widthSrcToDst = ( dstW << SHIFTVAL ) / srcW;
  iinfo_tmp->widthDstToSrc = ( srcW << SHIFTVAL ) / dstW;
  iinfo_tmp->heightSrcToDst = ( dstH << SHIFTVAL ) / srcH;
  iinfo_tmp->heightDstToSrc = ( srcH << SHIFTVAL ) / dstH;

  return;
}


void DestroyImageInfo(int c_num)
{
  if (iinfo[c_num] == NULL) return;
  free(iinfo[c_num]);
  iinfo[c_num] = NULL;
}


#define CALCRGB( INTXX )                                                       \
  INTXX pixel[4];                                                              \
  INTXX *src_addr = (INTXX*)( src_image->mem );                                \
  INTXX *dst_addr = (INTXX*)dst_image->mem + yMin * dstPitch;                  \
  INTXX *tmp_addr;                                                             \
  for (yCnt = yMin; yCnt < yMax; yCnt++) {                                     \
    xTmp = xMin * xMagnif;                                                     \
    for (xCnt = xMin; xCnt < xMax; xCnt++) {                                   \
      /* Find pixel's position in src_image */                                 \
      srcX = xTmp >> SHIFTVAL;                                                 \
      srcY = yTmp >> SHIFTVAL;                                                 \
      /* The pixels are int the src_image?? if not, use the up-left pixel */   \
      if (srcX + 1 < srcXMax && srcY + 1 < srcYMax) {                          \
	/* calculate the weights */                                            \
	xWeight = xTmp & SHIFTMASK;                                            \
	yWeight = yTmp & SHIFTMASK;                                            \
	weight[3] = xWeight + yWeight;                                         \
	weight[2] = SHIFTMAX - xWeight + yWeight;                              \
	weight[1] = SHIFTMAX + xWeight - yWeight;                              \
	weight[0] = SHIFTMAX2 - xWeight - yWeight;                             \
	/* summary 4 pixels RGB color */                                       \
	tmp_addr = &( src_addr[srcPitch * srcY + srcX] );                      \
	pixel[0] = *tmp_addr;                                                  \
	pixel[1] = *( tmp_addr + 1 );                                          \
	pixel[2] = *( tmp_addr + srcPitch );                                   \
	pixel[3] = *( tmp_addr + srcPitch + 1 );                               \
	rVal = gVal = bVal = 0;                                                \
	for (cnt = 0; cnt < 4; cnt++) {                                        \
	  rVal += ( pixel[cnt] & rMask ) * weight[cnt];                        \
	  gVal += ( pixel[cnt] & gMask ) * weight[cnt];                        \
	  bVal += ( pixel[cnt] & bMask ) * weight[cnt];                        \
	}                                                                      \
	rVal >>= AVERAGESHIFT; rVal &= rMask;                                  \
	gVal >>= AVERAGESHIFT; gVal &= gMask;                                  \
	bVal >>= AVERAGESHIFT; bVal &= bMask;                                  \
        *( dst_addr + xCnt ) = rVal | gVal | bVal;                             \
      } else {                                                                 \
	*( dst_addr + xCnt ) = src_addr[srcPitch * srcY + srcX];               \
      }                                                                        \
      xTmp += xMagnif;                                                         \
    }                                                                          \
    dst_addr += dstPitch;                                                      \
    yTmp += yMagnif;                                                           \
  }

void reduceImage(GdkImage *src_image, GdkImage *dst_image, int x, int y, int w, int h )
{
  int xMagnif, yMagnif;

  if (dst_image != smallimage_ex) {
    xMagnif = iinfo[clientNum]->widthDstToSrc;
    yMagnif = iinfo[clientNum]->heightDstToSrc;
  } else {
    xMagnif = iinfo_ex->widthDstToSrc;
    yMagnif = iinfo_ex->heightDstToSrc;
  }
  int xCnt, yCnt;
  int xMin = x, xMax = x + w, yMin = y, yMax = y + h;
  int srcXMax = si.framebufferWidth;
  int srcYMax = si.framebufferHeight;
  //int srcXMax = server_CB.client_grp[clientNum]->serverInitMsg.framebufferWidth;
  //int srcYMax = server_CB.client_grp[clientNum]->serverInitMsg.framebufferHeight;
  int srcX, srcY;
  int xTmp = xMin * xMagnif;
  int yTmp = yMin * yMagnif;
  int xWeight, yWeight;
  int srcPitch = src_image->bpl / ( gdkvisbpp / 8 );
  int dstPitch = dst_image->bpl / ( gdkvisbpp / 8 );
  int weight[4];
  int rVal, gVal, bVal;
  int rMask = src_image->visual->red_mask;
  int gMask = src_image->visual->green_mask;
  int bMask = src_image->visual->blue_mask;
  int cnt;

  switch( gdkvisbpp ){
  case 8 :{
    CALCRGB( char );
    break;
  }
  case 16 :{
    CALCRGB( short );
    break;
  }
  case 24 :
  case 32 :{
    CALCRGB( int );
    break;
  }
  default :
    break;
  }
  return;
}

void reduceXYWH( int originalX, int originalY, int originalWidth, int originalHeight,
		 int *reducedX, int *reducedY, int *reducedWidth, int *reducedHeight, Bool flag)
{
  if (!flag) {
    *reducedX = originalX * iinfo[clientNum]->widthSrcToDst >> SHIFTVAL;
    *reducedY = originalY * iinfo[clientNum]->heightSrcToDst >> SHIFTVAL;
    *reducedWidth = originalWidth * iinfo[clientNum]->widthSrcToDst >> SHIFTVAL;
    *reducedHeight = originalHeight * iinfo[clientNum]->heightSrcToDst >> SHIFTVAL;
    if (*reducedX > reducedWindowWidth) *reducedX = reducedWindowWidth;
    if (*reducedY > reducedWindowHeight) *reducedY = reducedWindowHeight;
  } else {
    *reducedX = originalX * iinfo_ex->widthSrcToDst >> SHIFTVAL;
    *reducedY = originalY * iinfo_ex->heightSrcToDst >> SHIFTVAL;
    *reducedWidth = originalWidth * iinfo_ex->widthSrcToDst >> SHIFTVAL;
    *reducedHeight = originalHeight * iinfo_ex->heightSrcToDst >> SHIFTVAL;
    if (*reducedX > expandedWindowWidth) *reducedX = expandedWindowWidth;
    if (*reducedY > expandedWindowHeight) *reducedY = expandedWindowHeight;
  }
  return;
}

/* 縦横非固定用関数 */
void fixed_aspect(int srcW, int srcH, int *dstW, int *dstH) {
  /* image aspect ratio */
  float image_aspect = (float)srcH / (float)srcW;

  printf("image aspect: %f\n", image_aspect);
  printf("image data: %dx%d -> %dx%d\n", srcW, srcH, *dstW, *dstH);
}
