#include <windows.h>
#include <math.h>

#include "Pattern.h"



//---- GLOBAL DATA -----------------------------------------------------------//

int pattern_sin[256];



//---- Pattern_Startup -------------------------------------------------------//

void Pattern_Startup()
{
  int i;

  for(i=0; i<256; i++) pattern_sin[i] = (int)(128+127*sin(i*3.1415926/128));
}



//---- Pattern_Display -------------------------------------------------------//

void Pattern_Display(HDC dc, PATTERN_RGB *rgb)
{
  BITMAPINFO bmi;

  bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  bmi.bmiHeader.biWidth = 256;
  bmi.bmiHeader.biHeight = -256;
  bmi.bmiHeader.biPlanes = 1;
  bmi.bmiHeader.biBitCount = 24;
  bmi.bmiHeader.biCompression = BI_RGB;
  bmi.bmiHeader.biSizeImage = 0;
  bmi.bmiHeader.biXPelsPerMeter = 1000;
  bmi.bmiHeader.biYPelsPerMeter = 1000;
  bmi.bmiHeader.biClrUsed = 0;
  bmi.bmiHeader.biClrImportant = 0;

  int w = 1280, h = 1024, x, y;

  for(y = 0; y < h; y += 256)
    for(x = 0; x < w; x += 256)

  StretchDIBits(
    dc,
    x, y, 256, 256,
    0, 0, 256, 256,
    (LPVOID)rgb->buf,
    (LPBITMAPINFO)&bmi,
    DIB_RGB_COLORS, SRCCOPY
  );

}



//---- Pattern_Create_A ------------------------------------------------------//

PATTERN_A *Pattern_Create_A()
{
  return (PATTERN_A *)malloc(sizeof(PATTERN_A));
}



//---- Pattern_Create_RGB ----------------------------------------------------//

PATTERN_RGB *Pattern_Create_RGB()
{
  return (PATTERN_RGB *)malloc(sizeof(PATTERN_RGB));
}



//---- Pattern_Fn_A_Plasma1 --------------------------------------------------//

void Pattern_Fn_A_Plasma1(PATTERN_A *a, int time)
{
  int i, j;

  for(j=0; j<256; j++) {
    for(i=0; i<256; i++) {
      a->buf[j][i] = (
        pattern_sin[(time*3 + i*5 + j*4)&255] +
        pattern_sin[(time*6 + i*2 + j*5)&255] +
        pattern_sin[(time*5 + i*1 + j*3)&255] +
        pattern_sin[(time*2 + i*3 + j*1)&255]
      ) >> 2;
    }
  }
}



//---- Pattern_Fn_A_Plasma2 --------------------------------------------------//

void Pattern_Fn_A_Plasma2(PATTERN_A *a, int time)
{
  int i, j;

  for(j=0; j<256; j++) {
    for(i=0; i<256; i++) {
      a->buf[j][i] = (
        pattern_sin[(time*4 + i*1 + j*1)&255] +
        pattern_sin[(time*3 - i*1 - j*3)&255] +
        pattern_sin[(time*1 - i*3 + j*2)&255] +
        pattern_sin[(time*2 - i*2 - j*1)&255]
      ) >> 2;
    }
  }

}



//---- Pattern_Fn_A_Plasma3 --------------------------------------------------//

void Pattern_Fn_A_Plasma3(PATTERN_A *a, int time)
{
  int i, j;

  for(j=0; j<256; j++) {
    for(i=0; i<256; i++) {
      a->buf[j][i] = (
        pattern_sin[(time*1 - i*7 - j*3)&255] +
        pattern_sin[(time*1 - i*6 + j*6)&255] +
        pattern_sin[(time*1 + i*4 + j*3)&255] +
        pattern_sin[(time*1 + i*5 - j*2)&255]
      ) >> 2;
    }
  }

}



//---- Pattern_Fn_A_Copy -----------------------------------------------------//

void Pattern_Fn_A_Copy(PATTERN_A *dest, PATTERN_A *src)
{
  memcpy(dest, src, sizeof(PATTERN_A));
}



//---- Pattern_Fn_RGB_Copy ---------------------------------------------------//

void Pattern_Fn_RGB_Copy(PATTERN_RGB *dest, PATTERN_RGB *src)
{
  memcpy(dest, src, sizeof(PATTERN_RGB));
}



//---- Pattern_Fn_A_RGB ------------------------------------------------------//

void Pattern_Fn_A_RGB(PATTERN_A *dest, PATTERN_RGB *src)
{

  int i, j;

  for(j=0; j<256; j++) {
    for(i=0; i<256; i++) {
      dest->buf[j][i] = (src->buf[j][i][0] * 2 + src->buf[j][i][1] * 3 + src->buf[j][i][2] * 2)>>3;
    }
  }

}



//---- Pattern_Fn_A_Emboss ---------------------------------------------------//

void Pattern_Fn_A_Emboss(PATTERN_A *a)
{
  pattern_var(PATTERN_A, Pattern_Create_A, temp);

  int i, j;

  for(j=0; j<256; j++) {
    for(i=0; i<256; i++) {
      temp->buf[j][i] = 128 + (((int)(a->buf[(j+1)&255][(i)&255]) - (int)(a->buf[(j-1)&255][(i)&255]) + (int)(a->buf[(j)&255][(i+1)&255]) - (int)(a->buf[(j)&255][(i-1)&255])));
    }
  }

  Pattern_Fn_A_Copy(a, temp);
}



//---- Pattern_Fn_RGB_A ------------------------------------------------------//

void Pattern_Fn_RGB_A(PATTERN_RGB *rgb, PATTERN_A *a)
{
  int i, j;

  for(j=0; j<256; j++) {
    for(i=0; i<256; i++) {
      rgb->buf[j][i][0] = a->buf[j][i];
      rgb->buf[j][i][1] = a->buf[j][i];
      rgb->buf[j][i][2] = a->buf[j][i];
    }
  }

}



//---- Pattern_Fn_RGB_A3 -----------------------------------------------------//

void Pattern_Fn_RGB_A3(PATTERN_RGB *rgb, PATTERN_A *a1, PATTERN_A *a2, PATTERN_A *a3)
{
  int i, j;

  for(j=0; j<256; j++) {
    for(i=0; i<256; i++) {
      rgb->buf[j][i][0] = a1->buf[j][i];
      rgb->buf[j][i][1] = a2->buf[j][i];
      rgb->buf[j][i][2] = a3->buf[j][i];
    }
  }

}



//---- Pattern_Fn_A3_RGB -----------------------------------------------------//

void Pattern_Fn_A3_RGB(PATTERN_A *r, PATTERN_A *g, PATTERN_A *b, PATTERN_RGB *rgb)
{
  int i, j;

  for(j=0; j<256; j++) {
    for(i=0; i<256; i++) {
      r->buf[j][i] = rgb->buf[j][i][0];
      g->buf[j][i] = rgb->buf[j][i][1];
      b->buf[j][i] = rgb->buf[j][i][2];
    }
  }

}



//---- Pattern_Fn_RGB2_A2_Lookup --------------------------------------------//

void Pattern_Fn_RGB2_A2_Lookup(PATTERN_RGB *rgb1, PATTERN_RGB *rgb2, PATTERN_A *a1, PATTERN_A *a2)
{
  int i, j;
  int x, y;

  for(j=0; j<256; j++) {
    for(i=0; i<256; i++) {
      x = a1->buf[j][i];
      y = a2->buf[j][i];
      rgb1->buf[j][i][0] = rgb2->buf[y][x][0];
      rgb1->buf[j][i][1] = rgb2->buf[y][x][1];
      rgb1->buf[j][i][2] = rgb2->buf[y][x][2];
    }
  }

}



//---- Pattern_Fn_A2_A2_Lookup ----------------------------------------------//

void Pattern_Fn_A2_A2_Lookup(PATTERN_A *aa1, PATTERN_A *aa2, PATTERN_A *a1, PATTERN_A *a2)
{
  int i, j;
  int x, y;

  for(j=0; j<256; j++) {
    for(i=0; i<256; i++) {
      x = a1->buf[j][i];
      y = a2->buf[j][i];
      aa1->buf[j][i] = aa2->buf[y][x];
    }
  }

}



//---- Pattern_Fn_A_Checkers ------------------------------------------------//

void Pattern_Fn_A_Checkers(PATTERN_A *a)
{
  int i, j;
  int x, y;
  int c;

  for(j=0; j<256; j++) {
    for(i=0; i<256; i++) {
      //if((i<128 && j<128) || (i>=128 && j>=128)) c = max(i, j); else c = min(i, j);
      c = i^j;
      a->buf[j][i] = c;
    }
  }

}



//---- Pattern_Fn_RGB_Checkers ----------------------------------------------//

void Pattern_Fn_RGB_Checkers(PATTERN_RGB *rgb)
{
  int i, j;
  int x, y;
  int c;

  for(j=0; j<256; j++) {
    for(i=0; i<256; i++) {
//      c=i^j;
//      if((i<128 && j<128) || (i>=128 && j>=128)) c = 255-max(i, j); else c = min(i, j);
      if(i<128) c = j; else c = j-128;
      if(j<128) c -= i;

      rgb->buf[j][i][0] = c;
      rgb->buf[j][i][1] = c;
      rgb->buf[j][i][2] = c;
    }
  }

}



//---- Pattern_Fn_RGB_XOR ---------------------------------------------------//

void Pattern_Fn_RGB_XOR(PATTERN_RGB *rgb)
{
  int i, j;
  int x, y;
  int c;

  for(j=0; j<256; j++) {
    for(i=0; i<256; i++) {
      rgb->buf[j][i][0] = i^j;
      rgb->buf[j][i][1] = i^j;
      rgb->buf[j][i][2] = i^j;
    }
  }

}



//---- Pattern_Fn_RGB3_Difference -------------------------------------------//

void Pattern_Fn_RGB3_Difference(PATTERN_RGB *result, PATTERN_RGB *fade1, PATTERN_RGB *fade2)
{
  int i, j;
  int x, y;
  int c;

  for(j=0; j<256; j++) {
    for(i=0; i<256; i++) {
      int d1 = (int)fade1->buf[j][i][0] - (int)fade2->buf[j][i][0]; if(d1 < 0) d1 =- d1;
      int d2 = (int)fade1->buf[j][i][1] - (int)fade2->buf[j][i][1]; if(d2 < 0) d2 =- d2;
      int d3 = (int)fade1->buf[j][i][2] - (int)fade2->buf[j][i][2]; if(d3 < 0) d3 =- d3;
      result->buf[j][i][0] = d1;
      result->buf[j][i][1] = d2;
      result->buf[j][i][2] = d3;
    }
  }
}



//---- Pattern_Fn_RGB_Expander ----------------------------------------------//

void Pattern_Fn_RGB_Expander(PATTERN_RGB *dest, PATTERN_RGB *src)
{
  int i, j;
  int x, y;
  int c;

  for(j=0; j<256; j++) {
    for(i=0; i<256; i++) {
      int r1 = dest->buf[(j-1)&255][(i)&255][0];
      int r2 = dest->buf[(j+1)&255][(i)&255][0];
      int r3 = dest->buf[(j)&255][(i-1)&255][0];
      int r4 = dest->buf[(j)&255][(i+1)&255][0];

      int a1 = abs(r1 - src->buf[j][i][0]);
      int a2 = abs(r2 - src->buf[j][i][0]);
      int a3 = abs(r3 - src->buf[j][i][0]);
      int a4 = abs(r4 - src->buf[j][i][0]);

      if(a1 > a2) { a1 = a2; r1 = r2; }
      if(a1 > a3) { a1 = a3; r1 = r3; }
      if(a1 > a4) { a1 = a4; r1 = r4; }

      dest->buf[j][i][0] = r1;
      dest->buf[j][i][1] = r1;
      dest->buf[j][i][2] = r1;
    }
  }

}



//---- Pattern_Fn_RGB_Fade --------------------------------------------------//

void Pattern_Fn_RGB_Fade(PATTERN_RGB *rgb, PATTERN_RGB *fade1, PATTERN_RGB *fade2, unsigned char alpha)
{
  int i, j;
  int x, y;
  int c;

  for(j=0; j<256; j++) {
    for(i=0; i<256; i++) {
      rgb->buf[j][i][0] = (fade1->buf[j][i][0] * (255-alpha) + fade2->buf[j][i][0] * alpha) >> 8;
      rgb->buf[j][i][1] = (fade1->buf[j][i][1] * (255-alpha) + fade2->buf[j][i][1] * alpha) >> 8;
      rgb->buf[j][i][2] = (fade1->buf[j][i][2] * (255-alpha) + fade2->buf[j][i][2] * alpha) >> 8;
    }
  }

}



//---- Pattern_Fn_RGB_BoxBlur -----------------------------------------------//

void Pattern_Fn_RGB_BoxBlur(PATTERN_RGB *dest, PATTERN_RGB *src, int radius)
{
  int b, g, r;
  int i, j;

  int diameter = radius*2+1;

  pattern_var(PATTERN_RGB, Pattern_Create_RGB, temp);

  //  Blur horizontally into buffer

  for(j=0; j<256; j++)
  {
    b = g = r = 0;

    for(i=-radius; i<=radius; i++)
    {
      b += src->buf[j][i&255][0];
      g += src->buf[j][i&255][1];
      r += src->buf[j][i&255][2];
    }

    for(i=0; i<255; i++)
    {
      temp->buf[j][i][0] = b/diameter;
      temp->buf[j][i][1] = g/diameter;
      temp->buf[j][i][2] = r/diameter;

      b -= src->buf[j][(i-radius)&255][0];
      g -= src->buf[j][(i-radius)&255][1];
      r -= src->buf[j][(i-radius)&255][2];

      b += src->buf[j][(i+1+radius)&255][0];
      g += src->buf[j][(i+1+radius)&255][1];
      r += src->buf[j][(i+1+radius)&255][2];
    }
  }

  //  Blur vertically

  for(i=0; i<256; i++)
  {
    b = g = r = 0;

    for(j=-radius; j<=radius; j++)
    {
      b += temp->buf[j&255][i][0];
      g += temp->buf[j&255][i][1];
      r += temp->buf[j&255][i][2];
    }

    for(j=0; j<255; j++)
    {
      dest->buf[j][i][0] = b/diameter;
      dest->buf[j][i][1] = g/diameter;
      dest->buf[j][i][2] = r/diameter;

      b -= temp->buf[(j-radius)&255][i][0];
      g -= temp->buf[(j-radius)&255][i][1];
      r -= temp->buf[(j-radius)&255][i][2];

      b += temp->buf[(j+1+radius)&255][i][0];
      g += temp->buf[(j+1+radius)&255][i][1];
      r += temp->buf[(j+1+radius)&255][i][2];
    }
  }

}



//---- Pattern_Fn_RGB_RippleDistort -----------------------------------------//

void Pattern_Fn_RGB_RippleDistort(PATTERN_RGB *dest, PATTERN_RGB *src, int time)
{
  pattern_var(PATTERN_A, Pattern_Create_A, a1);
  pattern_var(PATTERN_A, Pattern_Create_A, a2);

  Pattern_Fn_A_Plasma1(a1, time/35);
  Pattern_Fn_A_Plasma2(a2, time/36);

  Pattern_Fn_RGB2_A2_Lookup(dest, src, a1, a2);
}



//---- Pattern_Fn_RGB_BWRipples ---------------------------------------------//

void Pattern_Fn_RGB_BWRipples(PATTERN_RGB *result, int time)
{
  pattern_const(PATTERN_RGB, Pattern_Create_RGB, checkers, Pattern_Fn_RGB_Checkers);

//  pattern_var(PATTERN_RGB, Pattern_Create_RGB, checkers);
//  Pattern_Fn_RGB_Checkers(checkers);
//  Pattern_Fn_RGB_BoxBlur(checkers, checkers, 3);//10+8*sin(time*3.14159/4000));

  Pattern_Fn_RGB_RippleDistort(result, checkers, time);
}



//---- Pattern_Main ---------------------------------------------------------//

void Pattern_Main(PATTERN_RGB *result, int time)
{
  static int lasttime = 0, timediff = 0;

  Pattern_Fn_RGB_BWRipples(result, time);



//  time = time/2;

/*
  if(time-lasttime < timediff) return;

  lasttime = time;
  timediff = (time%53);
  timediff = 60;
*/

  pattern_const(PATTERN_RGB, Pattern_Create_RGB, checkers, Pattern_Fn_RGB_Checkers);
  pattern_var(PATTERN_RGB, Pattern_Create_RGB, result1);
  pattern_var(PATTERN_RGB, Pattern_Create_RGB, result2);

  Pattern_Fn_RGB_BoxBlur(result2, checkers, 7);

  Pattern_Fn_RGB_RippleDistort(result1, result2, time);

  Pattern_Fn_RGB_BoxBlur(result, result1, 6);
  Pattern_Fn_RGB3_Difference(result, result, result1);



/*
  pattern_var(PATTERN_A, Pattern_Create_A, result1);
  pattern_var(PATTERN_A, Pattern_Create_A, result2);
  pattern_var(PATTERN_A, Pattern_Create_A, result3);
  pattern_var(PATTERN_A, Pattern_Create_A, result4);
  pattern_var(PATTERN_A, Pattern_Create_A, result5);
  pattern_var(PATTERN_A, Pattern_Create_A, result6);
  pattern_var(PATTERN_A, Pattern_Create_A, result7);
  pattern_var(PATTERN_A, Pattern_Create_A, result8);

  Pattern_Fn_A_Plasma1(result1, time/25 );
  Pattern_Fn_A_Plasma2(result2, time/28 );
  Pattern_Fn_A_Plasma2(result3, time/31 );
  Pattern_Fn_A_Plasma1(result4, time/34 );
  Pattern_Fn_A_Plasma2(result5, time/37 );
  Pattern_Fn_A_Plasma2(result6, time/40 );
  Pattern_Fn_A_Plasma1(result7, time/43 );
  Pattern_Fn_A_Plasma1(result8, time/46 );

  pattern_var(PATTERN_RGB, Pattern_Create_RGB, fade1);
  Pattern_Fn_RGB_A3(fade1, result1, result2, result3);

  pattern_var(PATTERN_RGB, Pattern_Create_RGB, fade2);
  Pattern_Fn_RGB_A3(fade2, result2, result5, result8);

  pattern_var(PATTERN_RGB, Pattern_Create_RGB, fade3);
  Pattern_Fn_RGB_A3(fade3, result3, result8, result5);

  pattern_var(PATTERN_RGB, Pattern_Create_RGB, fade4);
  Pattern_Fn_RGB_A3(fade4, result4, result3, result2);

  pattern_var(PATTERN_RGB, Pattern_Create_RGB, fade5);
  Pattern_Fn_RGB_A3(fade5, result5, result6, result7);

  pattern_var(PATTERN_RGB, Pattern_Create_RGB, fade6);
  Pattern_Fn_RGB_A3(fade6, result6, result1, result4);

//  pattern_var(PATTERN_RGB, Pattern_Create_RGB, fade7);
//  Pattern_Fn_RGB_A3(fade7, result7, result4, result1);

//  pattern_var(PATTERN_RGB, Pattern_Create_RGB, fade8);
//  Pattern_Fn_RGB_A3(fade8, result8, result7, result6);




//  Pattern_Fn_RGB_Copy(result, fade8);

  Pattern_Fn_RGB3_Difference(result, fade1, result);
  Pattern_Fn_RGB3_Difference(result, fade2, result);
  Pattern_Fn_RGB3_Difference(result, fade3, result);
  Pattern_Fn_RGB3_Difference(result, fade4, result);
  Pattern_Fn_RGB3_Difference(result, fade5, result);
//  Pattern_Fn_RGB3_Difference(result, fade6, result);
//  Pattern_Fn_RGB3_Difference(result, fade7, result);
//  Pattern_Fn_RGB3_Difference(result, fade8, result);

//  Pattern_Fn_RGB_Fade(result, result, fade8, 32 );

 
*/

//  Pattern_Fn_RGB_Fade(result, result, fade8, 128 + 96*sin(time*3.14159/2000) );
}




//---- END OF FILE -----------------------------------------------------------//




