#include <plugin.h>

typedef unsigned long COLORREF;

#define RED(rgb) ((rgb) & 0xFF)
#define GREEN(rgb) ((rgb) >> 8 & 0xFF)
#define BLUE(rgb) ((rgb) >> 16 & 0xFF)
#ifndef RGB
#define RGB(r,g,b) ((r) | (g) << 8 | (b) << 16)
#endif

#define PLUGIN_NAME "Stereogram"

#define NUM_PARAM 10
VarStruct varstr[NUM_PARAM] = {
  {LABEL     , "In: 1 Strip", 0.0f, 0.0f, 0.0f, ""},
  {INT|NUM   , "Width", 64.0f, 0.0f, 1024.0f, "Random-dot pattern width"},
  {FLO|NUMSLI, "Low", 1.0f, 0.5f, 1.0f, "Distant divergence factor"},
  {FLO|NUMSLI, "High", 0.8f, 0.5f, 1.0f, "Close divergence factor"},
  {INT|TOG   , "Fast mode", 0.0f, 0.0f, 1.0f, 
	"Uses faster (but lower quality) algorithm"},
  {INT|TOG   , "Interpolate", 0.0f, 0.0f, 1.0f, 
	"Is supposed to increase quality of pic (but doesn't yet)"},
  {INT|TOG   , "Hint spots", 1.0f, 0.0f, 1.0f, 
	"Adds dots to the bottom of image to assist with focus"},
  {INT|NUM   , "Hint size", 8.0f, 1.0f, 1024.0f, 
	"Sets the size of the hint spots"},
  {INT|TOG   , "Same pattern", 0.0f, 0.0f, 1.0f, 
	"Always uses the same pattern"},
  {INT|TOG   , "Show ZBuf", 0.0f, 0.0f, 1.0f, "Display depth field of image"},
};

/* The cast struct is for input in the main doit function
   Varstr and Cast must have the same variables in the same order */

typedef struct {
  int   dummy;
  int   width;
  float low;
  float high;
  int  fast;
  int  interpol;
  int  hint;
  int   size;
  int  predictable;
  int  showzbuf;
} Cast;

float cfra; /* current frame */

void plugin_seq_doit(Cast *, float, float, int, int,
   ImBuf *, ImBuf *, ImBuf *, ImBuf *);

void DoStereogram (const int *zbuf, const COLORREF *pattern, COLORREF *out, 
	int sx, int sy, Cast *param) 
{
  int x, y, /* holds current position in zbuf array */
      w, /* the length of the row array = the width of the picture + 
		a small area off the left */
      a, /* temporary value for rounding double to int */
     *p_in, /* pointer into zbuf, for optim */
      r1, r2, g1, g2, b1, b2, rt, gt, bt, rgb1, rgb2, rgbt; /* used in 
		interpolation of colour */
  unsigned int zval; /* holds z-buf value at current pixel */
  COLORREF *p_out, *p_pat; /* pointer into images, for optim */
  double *row = NULL, /* holds the precise position of each pixel in the 
		pattern */
         *p_row, /* pointer into row, for optim */
         b, c, /* used in interpolation of position */
         d, /* temp double value */
         diff; /* optim, high - low*/

  diff = param->high - param->low;
  w = sx + param->width;
  row = (double *)malloc(w * sizeof(double));

  /* init area offscreen with unwarped pattern */
  for (x = 0; x < param->width; x++) row[x] = x - param->width;
  p_in = (int *)zbuf;
  p_out = out;
  p_pat = (COLORREF *)pattern;
  for (y = 0; y < sy; y++)
  {
    p_row = row + param->width;
    for (x = 0; x < sx; x++)
    {
      zval = *p_in++ + 0x80000000L;
      if (param->showzbuf)
      {
        zval >>= 24;
        zval = 255 - zval;
        *p_out++ = RGB(zval,zval,zval);
        continue;
      }
      d = (double)zval;
      d = (double)param->width * (param->high - diff * (d / 4294967295.0));
      a = (int)d;
      if (a > d) a--; /* Force truncate, (int) may round on some 
		implementations */
      if (param->fast)
        *p_row = p_row[-a] + param->width;
      else
      { /* interpolate position */
        d -= a; /* find fractional part of d */
        b = p_row[-a]; /* pixel before position */
        c = p_row[1-a]; /* pixel after position */
        b += (c - b) * d; /* plain linear approx */
        *p_row = b + param->width;
      }
      p_row++;
    }
    if (param->showzbuf)
      continue;
    /* row now has the pixel positions of each point on the stereogram
     * now render the row */
    p_row = row + param->width;
    for (x = 0; x < sx; x++)
    {
      d = *p_row++;
      a = (int)d;
      if (a > d) a--; /* force truncate */
      while (a < 0) a += param->width;
      while (a >= param->width) a -= param->width;
      if (!param->interpol)
        *p_out++ = p_pat[a];
      else
      { /* interpolate colour */
        d -= a; /* find fractional part of d */
        rgb1 = p_pat[a];
        rgb2 = p_pat[a+1];
        r1 = RED(rgb1);
        r2 = RED(rgb2);
        rt = (int)(r1 + (r2 - r1) * d);
        g1 = GREEN(rgb1);
        g2 = GREEN(rgb2);
        gt = (int)(g1 + (g2 - g1) * d);
        b1 = BLUE(rgb1);
        b2 = BLUE(rgb2);
        bt = (int)(b1 + (b2 - b1) * d);
        rgbt = RGB(rt, gt, bt);
        *p_out++ = rgbt;
      }
    }
    p_pat += param->width; /* advance to next row of pattern */
  }
  free(row);
}

/* ******************** Plugin functions ***************** */

int plugin_seq_getversion(void)
{
  return B_PLUGIN_VERSION;
}

void plugin_but_changed(int but)
{
}

void plugin_init(void)
{
}

void plugin_getinfo(PluginInfo *info)
{
  info->name = PLUGIN_NAME;

  info->nvars = NUM_PARAM;
  info->varstr = varstr;

  info->cfra = &cfra;

  info->init = plugin_init;
  info->seq_doit = (SeqDoit)plugin_seq_doit;
  info->callback = plugin_but_changed;
}

void plugin_seq_doit(Cast *param, float facf0, float facf1, int sx, 
	int sy, ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *out, ImBuf *use)
{
  int a;
  COLORREF *recto;
  int *recti;
  COLORREF *pattern, *p, *hintrow;

  if (param->predictable) srand(0);

  a = param->width * sy;
  p = pattern = (COLORREF *)malloc(a * sizeof(COLORREF));
  while (a--)
  {
    *p++ = RGB(rand() % 0xFFL, rand() % 0xFFL, rand() % 0xFFL);
  }

  if(ibuf1)
  {
    if(ibuf1->zbuf)
    {
      recto = (COLORREF *)out->rect;
      recti = ibuf1->zbuf;
      DoStereogram (recti, pattern, recto, sx, sy, param);
      if (param->hint)
      {
        hintrow = (COLORREF *)malloc(sx * sizeof(COLORREF));
        memset (hintrow, 0, sx * sizeof(COLORREF));
        a = sx/2 - param->width/2 - param->size/2;
        memset (hintrow + a, 255, param->size * sizeof(COLORREF));
        a += param->width;
        memset (hintrow + a, 255, param->size * sizeof(COLORREF));
        a = param->size;
        while (a--)
        {
          memcpy (recto, hintrow, sx * sizeof(COLORREF));
          recto += sx;
        }
        free(hintrow);
      }
      free (pattern);
    }
    else
      printf("No zBuffer!\nMust link to a scene or IRIZ picture\n");
  }
  else
    printf ("Not linked to a buffer!\n");
}
