#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "vncserv.h"
#include "proto.h"


static CARD32 find_best_rgb(int r, int g, int b, int maxrgb[3], int shiftrgb[3]);


void pixtrans_build(vncserv *serv, int maxrgb[3], int shiftrgb[3], int clientbpp) {

  // remove previous table
  if (serv->pixtrans)   free(serv->pixtrans);
  serv->pixtrans = NULL;

  if (serv->sourcebpp == 4 && clientbpp == 8) {
    int i;
    int pal16[16] = { 0xffffff00, 0xdddddd00, 0xbbbbbb00, 0x99999900,
                      0x77777700, 0x55555500, 0x33333300, 0x00000000,
                      0x99440000, 0x00eeee00, 0x00cc0000, 0x0000dd00,
                      0xbbeeee00, 0x00885500, 0x00bbff00, 0xffbb0000 };

    // build default-16-colour-palette to RGB??? table
    serv->pixtrans = malloc(16);
    for (i = 0; i < 16; i++) {
      int r, g, b;
      r = (pal16[i]>> 8) & 255;
      g = (pal16[i]>>16) & 255;
      b = (pal16[i]>>24) & 255;
      ((CARD8 *)serv->pixtrans)[i] = find_best_rgb(r, g, b, maxrgb, shiftrgb);
    }

  } else if (serv->sourcebpp == 4 && clientbpp == 16) {
    int i;
    int pal16[16] = { 0xffffff00, 0xdddddd00, 0xbbbbbb00, 0x99999900,
                      0x77777700, 0x55555500, 0x33333300, 0x00000000,
                      0x99440000, 0x00eeee00, 0x00cc0000, 0x0000dd00,
                      0xbbeeee00, 0x00885500, 0x00bbff00, 0xffbb0000 };

    // build default-16-colour-palette to RGB??? table
    serv->pixtrans = malloc(16*2);
    for (i = 0; i < 16; i++) {
      int r, g, b;
      r = (pal16[i]>> 8) & 255;
      g = (pal16[i]>>16) & 255;
      b = (pal16[i]>>24) & 255;
      ((CARD16 *)serv->pixtrans)[i] = find_best_rgb(r, g, b, maxrgb, shiftrgb);
    }

  } else if (serv->sourcebpp == 8 && clientbpp == 8) {
    int i;

//    if (maxrgb[0] == 7   && maxrgb[1] == 7   && maxrgb[2] == 3 &&
//        shiftrgb[0] == 0 && shiftrgb[1] == 3 && shiftrgb[2] == 6)   return;
    // build RGB332 to RGB??? table
    serv->pixtrans = malloc(256);
    for (i = 0; i < 256; i++) {
      int r, g, b;
      r = serv->pal8bpp[i]       & 255;
      g = (serv->pal8bpp[i]>>8)  & 255;
      b = (serv->pal8bpp[i]>>16) & 255;
      ((CARD8 *)serv->pixtrans)[i] = find_best_rgb(r, g, b, maxrgb, shiftrgb);
    }

  } else if (serv->sourcebpp == 8 && clientbpp == 16) {
    int i;

    // build RGB332 to RGB??? table
    serv->pixtrans = malloc(512);
    for (i = 0; i < 256; i++) {
      int r, g, b;
      r = serv->pal8bpp[i]       & 255;
      g = (serv->pal8bpp[i]>>8)  & 255;
      b = (serv->pal8bpp[i]>>16) & 255;
      ((CARD16 *)serv->pixtrans)[i] = find_best_rgb(r, g, b, maxrgb, shiftrgb);
    }

  } else if (serv->sourcebpp == 16 && clientbpp == 8) {
    int rgb;

    // build RGB555 to RGB??? table
    serv->pixtrans = malloc(32768);
    for (rgb = 0; rgb < 32767; rgb++) {
      int r, g, b;
      r = rgb       & 31;
      g = (rgb>>5)  & 31;
      b = (rgb>>10) & 31;
      r = (r<<3) | (r>>2);
      g = (g<<3) | (g>>2);
      b = (b<<3) | (b>>2);
      ((CARD8 *)serv->pixtrans)[rgb] = find_best_rgb(r, g, b, maxrgb, shiftrgb);
    }

  } else if (serv->sourcebpp == 16 && clientbpp == 16) {
    int rgb;

    if (maxrgb[0] == 31  && maxrgb[1] == 31  && maxrgb[2] == 31 &&
        shiftrgb[0] == 0 && shiftrgb[1] == 5 && shiftrgb[2] == 10)   return;

    // build RGB555 to RGB??? table
    serv->pixtrans = malloc(65536);
    for (rgb = 0; rgb < 32767; rgb++) {
      int r, g, b;
      r = rgb       & 31;
      g = (rgb>>5)  & 31;
      b = (rgb>>10) & 31;
      r = (r<<3) | (r>>2);
      g = (g<<3) | (g>>2);
      b = (b<<3) | (b>>2);
      ((CARD16 *)serv->pixtrans)[rgb] = find_best_rgb(r, g, b, maxrgb, shiftrgb);
    }
  }
}


void pixtrans_do_it(vncserv *serv, int x, int y, int w, int h, void *p) {

  if (serv->pixtrans) {
    if (serv->sourcebpp == 4  && serv->clientbpp == 8) {
      CARD8 *rd, *wr, *lookup;
      int yi;

      lookup = serv->pixtrans;

      rd = (CARD8 *)serv->framebuffer;
      rd += y * serv->bpl;
      wr = p;
      for (yi = 0; yi < h; yi++) {
        int xi;
        for (xi = x; xi < w+x; xi++)
          if (xi & 1)
            wr[xi-x] = lookup[rd[xi>>1] >> 4];
          else
            wr[xi-x] = lookup[rd[xi>>1] & 15];
        rd += serv->bpl;
        wr += w;
      }

    } else if (serv->sourcebpp == 4  && serv->clientbpp == 16) {
      CARD8 *rd;
      CARD16 *wr, *lookup;
      int yi;

      lookup = serv->pixtrans;

      rd = (CARD8 *)serv->framebuffer;
      rd += y * serv->bpl;
      wr = p;
      for (yi = 0; yi < h; yi++) {
        int xi;
        for (xi = x; xi < w+x; xi++)
          if (xi & 1)
            wr[xi-x] = lookup[rd[xi>>1] >> 4];
          else
            wr[xi-x] = lookup[rd[xi>>1] & 15];
        rd += serv->bpl;
        wr += w;
      }

    } else if (serv->sourcebpp == 8  && serv->clientbpp == 8) {
      CARD8 *rd, *wr, *lookup;
      int yi;

      lookup = serv->pixtrans;

      rd = (CARD8 *)serv->framebuffer;
      rd += x + y * serv->sizex;
      wr = p;
      for (yi = 0; yi < h; yi++) {
        int xi;
        for (xi = 0; xi < w; xi++)  wr[xi] = lookup[rd[xi]];
        rd += serv->bpl;
        wr += w;
      }

    } else if (serv->sourcebpp == 8  && serv->clientbpp == 16) {
      CARD8 *rd;
      CARD16 *wr, *lookup;
      int yi;

      lookup = serv->pixtrans;

      rd = (CARD8 *)serv->framebuffer;
      rd += x + y * serv->sizex;
      wr = p;
      for (yi = 0; yi < h; yi++) {
        int xi;
        for (xi = 0; xi < w; xi++)  wr[xi] = lookup[rd[xi]];
        rd += serv->bpl;
        wr += w;
      }

    } else if (serv->sourcebpp == 16 && serv->clientbpp == 8) {
      CARD16 *rd;
      CARD8 *wr, *lookup;
      int yi;

      lookup = serv->pixtrans;

      rd = (CARD16 *)serv->framebuffer;
      rd += x + y * serv->sizex;
      wr = p;
      for (yi = 0; yi < h; yi++) {
        int xi;
        for (xi = 0; xi < w; xi++)  wr[xi] = lookup[rd[xi]];
        rd += serv->bpl/2;
        wr += w;
      }

    } else if (serv->sourcebpp == 16 && serv->clientbpp == 16) {
      CARD16 *rd, *wr, *lookup;
      int yi;

      lookup = serv->pixtrans;

      rd = (CARD16 *)serv->framebuffer;
      rd += x + y * serv->sizex;
      wr = p;
      for (yi = 0; yi < h; yi++) {
        int xi;
        for (xi = 0; xi < w; xi++)  wr[xi] = lookup[rd[xi]];
        rd += serv->bpl/2;
        wr += w;
      }
    }

  } else if (serv->sourcebpp == 8) {
    // 8 bpp direct copy
    CARD8 *rd, *wr;
    int yi;
    rd = (CARD8 *)serv->framebuffer;
    rd += x + y * serv->sizex;
    wr = p;
    for (yi = 0; yi < h; yi++) {
      memcpy(wr, rd, w);
      rd += serv->bpl;
      wr += w;
    }

  } else if (serv->sourcebpp == 16) {
    // 16 bpp direct copy
    CARD16 *rd, *wr;
    int yi;
    rd = (CARD16 *)serv->framebuffer;
    rd += x + y * serv->sizex;
    wr = p;
    for (yi = 0; yi < h; yi++) {
      memcpy(wr, rd, 2*w);
      rd += serv->bpl/2;
      wr += w;
    }
  }
}

// ----------------------------------------------------------------------

CARD32 find_best_rgb(int r, int g, int b, int maxrgb[3], int shiftrgb[3]) {
  r = (r * maxrgb[0] + 128)/255;
  g = (g * maxrgb[1] + 128)/255;
  b = (b * maxrgb[2] + 128)/255;
  if (r >= maxrgb[0])   r = maxrgb[0];
  if (g >= maxrgb[1])   g = maxrgb[1];
  if (b >= maxrgb[2])   b = maxrgb[2];
  return (r<<shiftrgb[0]) | (g<<shiftrgb[1]) | (b<<shiftrgb[2]);
}
