#include <windows.h>
#include <mmsystem.h>
#include "Pattern.h"



//--- DATA -------------------------------------------------------------------//

HINSTANCE hMainInstance;
HWND hMainWindow = NULL;

char szAppName[40] = "Lewpen's pattern generator";



//---- FUNCTION PROTOS -------------------------------------------------------//

PATTERN_A   *a1, *a2, *a3, *a4, *a5;
PATTERN_RGB *rgb1, *rgb2, *rgb3, *rgb4, *rgb5;



//---- Startup ---------------------------------------------------------------//

void Startup()
{
  Pattern_Startup();

  a1 = Pattern_Create_A();
  a2 = Pattern_Create_A();
  a3 = Pattern_Create_A();
  a4 = Pattern_Create_A();
  a5 = Pattern_Create_A();

  rgb1 = Pattern_Create_RGB();
  rgb2 = Pattern_Create_RGB();
  rgb3 = Pattern_Create_RGB();
  rgb4 = Pattern_Create_RGB();
  rgb5 = Pattern_Create_RGB();
}



//---- Display ---------------------------------------------------------------//

void Display(HDC dc, PATTERN_RGBA *rgb)
{
  BITMAPINFO bmi;

  bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  bmi.bmiHeader.biWidth = 256;
  bmi.bmiHeader.biHeight = -256;
  bmi.bmiHeader.biPlanes = 1;
  bmi.bmiHeader.biBitCount = 32;
  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 = 256, h = 256, 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
  );

}






/*
  from redraw

  Pattern_Fn_A_Plasma1(a1, time/17);
  Pattern_Fn_A_Plasma2(a2, time/36);
  Pattern_Fn_A_Plasma3(a3, time/41);

  Pattern_Fn_RGB_Checkers(rgb2);

  Pattern_Fn_RGB2_A2_Lookup(rgb1, rgb2, a1, a2);
*/

/*
  Pattern_Fn_A_Plasma1(a1, time/51);
  Pattern_Fn_A_Plasma2(a2, time/36);
  Pattern_Fn_A_Plasma3(a3, time/75 );
  
  Pattern_Fn_A2_A2_Lookup(a4, a3, a2, a1);
  Pattern_Fn_A2_A2_Lookup(a5, a4, a3, a2);

  Pattern_Fn_RGB_Checkers(rgb2);

  Pattern_Fn_RGB_A3(rgb1, a4, a4, a4);
*/



//---- RANDOM FUNCTIONS ------------------------------------------------------//

int random( int min, int max )
{
  if( min == max ) return min;
  if( min > max ) { int t=min; min = max; max = t; }

  int dif = max-min;

  static int r1 = 0x8939823;
  static int r2 = 0x9219812;
  static int r3 = 0x112891F;

  r1 += 19198;

  r2 += r1 * 0x982191 - 328923;
  r3 <<= 1;
  r3 ^= r2;

  return min + ( ( ( r3*7 + r2*33 + r1*4 ) / 3 ) % dif );
}



//---- COLOUR FUNCTIONS ------------------------------------------------------//

typedef unsigned int RGBA;


#define GET_A( c ) (((c)>>24)&255)
#define GET_R( c ) (((c)>>16)&255)
#define GET_G( c ) (((c)>>8)&255)
#define GET_B( c ) (((c))&255)

#define SET_A( c, a ) ( ( (a)     <<24)+((c)&0x00FFFFFF) )
#define SET_R( c, r ) ( (((r)&255)<<24)+((c)&0xFF00FFFF) )
#define SET_G( c, g ) ( (((g)&255)<<24)+((c)&0xFFFF00FF) )
#define SET_B( c, b ) ( (((b)&255)<<24)+((c)&0xFFFFFF00) )



#define MAKE_RGBA( r, g, b, a ) ((RGBA)( (((a)&255)<<24) + (((r)&255)<<16) + (((g)&255)<<8) + (((b)&255)) ))

#define CAP_VALUE( a ) ( ((a)>255) ? 255 : a )


#define MUL_RGBA_RGBA( c1, c2 )      \
  MAKE_RGBA                          \
  (                                  \
    (GET_R(c1) * GET_R(c2)) / 256,   \
    (GET_G(c1) * GET_G(c2)) / 256,   \
    (GET_B(c1) * GET_B(c2)) / 256,   \
    (GET_A(c1) * GET_A(c2)) / 256    \
  );

#define ADD_RGBA_RGBA( c1, c2 )           \
  MAKE_RGBA                               \
  (                                       \
    CAP_VALUE( GET_R(c1) + GET_R(c2) ),   \
    CAP_VALUE( GET_G(c1) + GET_G(c2) ),   \
    CAP_VALUE( GET_B(c1) + GET_B(c2) ),   \
    CAP_VALUE( GET_A(c1) + GET_A(c2) )    \
  )

#define MUL_RGBA_BYTE( c, f )        \
  MAKE_RGBA                          \
  (                                  \
    (GET_R(c) * (f))/256,             \
    (GET_G(c) * (f))/256,             \
    (GET_B(c) * (f))/256,             \
    GET_A(c)                        \
  )

#define OVERLAY( under, over )                   \
  ADD_RGBA_RGBA                                  \
  (                                              \
    MUL_RGBA_BYTE( under, 255 - GET_A(over) ),   \
    MUL_RGBA_BYTE( over, GET_A(over) )           \
  )

#define MASK_RGBA_RGBA( mask, col )          \
  SET_A                                      \
  (                                          \
    col,                                     \
    (GET_A(mask)*GET_A(col)) / 256           \
  )


//---- IMAGE ROUTINES --------------------------------------------------------//

#define MAX_CIRCLES 200

#define MIN_RADIUS 1000
#define MAX_RADIUS 3000

struct
{
  double x, y;
  double dx, dy;

  double r;

  RGBA col;
}
circle[ MAX_CIRCLES ];

bool circles_init = false;


void draw_circle( PATTERN_RGBA *dest, double x, double y, double r, RGBA col )
{
  int x1, y1, x2, y2;

  x1 = x - r - 1;
  y1 = y - r - 1;
  x2 = x + r + 1;
  y2 = y + r + 1;

  if( x1 < 0 ) x1 = 0;
  if( y1 < 0 ) y1 = 0;
  if( x2 > 255 ) x2 = 255;
  if( y2 < 255 ) y2 = 255;

  int i, j;

  for( j=y1; j<=y2; j++ )
  {
    for( i=x1; i<=x2; i++ )
    {
      if( (x-i)*(x-i) + (y-j)*(y-j) < r*r ) dest->buf[j][i] = col;
    }
  }
}

void render_circle_image( PATTERN_RGBA *dest )
{
  //- Fill destination with transparent

  int i, j;

  for( j=0; j<256; j++ )
  {
    for( i=0; i<256; i++ )
    {
      dest->buf[j][i] = 0;
    }
  }

  //- Draw & move circles

  if( ! circles_init )
  {
    for( i=0; i<MAX_CIRCLES; i++ )
    {
      circle[i].x = (random( 0, 65536 )+0.0) / 256.0;
      circle[i].y = (random( 0, 65536 )+0.0) / 256.0;
      circle[i].r = 0.01 * random( MIN_RADIUS, MAX_RADIUS );
      circle[i].dy = -0.01 * random( 0, 100 );
      circle[i].col = 0xFF000000 | 0x010102 * random( 60, 120 );
    }

    circles_init = true;
  }

  for( i=0; i<MAX_CIRCLES; i++ )
  {
    draw_circle( dest, circle[i].x, circle[i].y, circle[i].r, circle[i].col );

    circle[i].y += circle[i].dy;

    if( circle[i].y < 0 ) circle[i].y += 256.0;
    if( circle[i].y >= 256 ) circle[i].y -= 256.0;
  }

}

void get_alpha_channel( PATTERN_A *dest, PATTERN_RGBA *src )
{
  int i, j;

  for( j=0; j<256; j++ )
  {
    for( i=0; i<256; i++ )
    {
      dest->buf[j][i] = GET_A( src->buf[j][i] );
    }
  }
}

void blur_channel( PATTERN_A *dest, PATTERN_A *src )
{
  int i, j;
  int a, b;

  for( j=0; j<256; j++ )
  {
    for( i=0; i<256; i++ )
    {
      unsigned int c=0;

      for( b=-5; b<=5; b++ )
      {
        for( a=-5; a<=5; a++ )
        {
          c += src->buf[ (j+b)&255 ][ (i+a)&255 ];
        }
      }

      dest->buf[j][i] = c * (1.0/(11.0*11.0));
    }
  }
}




//---- Redraw ----------------------------------------------------------------//

PATTERN_RGBA a;
PATTERN_A    b;
PATTERN_A    c;
PATTERN_RGBA cc;

PATTERN_RGBA dest;


void Redraw(HDC dc)
{
  int time = timeGetTime();



  //- Prepare circles into A

  render_circle_image( &a );

  //- Prepare blurred circles into C

  get_alpha_channel( &b, &a );
  blur_channel( &c, &b );


  //- Prepare scaled one into CC

//  cc = scale( inv( g ), 1.25 );

//  scale_and_invert(

//  blur_image( &cc, &c );



  int i, j;

  for( j=0; j<256; j++ )
  {
    for( i=0; i<256; i++ )
    {
      RGBA d = MUL_RGBA_BYTE( OVERLAY( 0xFFFF00, a.buf[j][i] ), c.buf[j][i] );

      RGBA g = MUL_RGBA_BYTE( c.buf[(j-4)&255][(i-4)&255], 64 ) * 0xFF010101;

      RGBA dd = MASK_RGBA_RGBA( a.buf[j][i], c.buf[(j+17)&255][(i+17)&255] ^ 0xFFFFFF );

      RGBA f = MUL_RGBA_RGBA( d, dd );

      RGBA e = ADD_RGBA_RGBA( f, g );

      dest.buf[j][i] = d;
    }
  }


  

  Display( dc, &dest );

}



//---- WinProc ---------------------------------------------------------------//

LRESULT CALLBACK WinProc(HWND hwnd, UINT uiMsg, UINT wParam, LONG lParam)
{
  HDC dc;
  PAINTSTRUCT ps;

  switch(uiMsg) {

  case WM_CREATE:
    Startup();
    SetTimer(hwnd, 1, 10, NULL);
    break;

  case WM_TIMER:
    dc = GetDC(hwnd);
    Redraw(dc);
    ReleaseDC(hwnd, dc);
    return 0L;

  case WM_PAINT:
    dc = BeginPaint(hwnd, &ps);
    Redraw(dc);
    EndPaint(hwnd, &ps);
    return 0L;

  case WM_NCHITTEST:
    return HTCAPTION;

  case WM_KEYDOWN:
    if( wParam == VK_ESCAPE ) PostMessage( hwnd, WM_DESTROY, 0, 0 );
    return 0L;

  case WM_DESTROY:
    KillTimer(hwnd, 1);
    PostQuitMessage(0);
    return 0L;

  }

  return DefWindowProc(hwnd, uiMsg, wParam, lParam);
}




//---- WinMain ---------------------------------------------------------------//

int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
  WNDCLASS wndclass;
  char cSwitch;
  MSG msg;
  RECT rc;

  hMainInstance = hInstance;

  if(hPrevInstance) return 1;

  //- Create window class

  wndclass.style         = CS_HREDRAW | CS_VREDRAW;
  wndclass.lpfnWndProc   = WinProc;
  wndclass.cbClsExtra    = 0;
  wndclass.cbWndExtra    = 0;
  wndclass.hInstance     = hInstance;
  wndclass.hIcon         = NULL;
  wndclass.hCursor       = LoadCursor(NULL, IDC_ARROW);
  wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
  wndclass.lpszMenuName  = NULL;
  wndclass.lpszClassName = szAppName;

  if(!RegisterClass(&wndclass)) return 1;

  //- Create main window

  hMainWindow = CreateWindowEx(
    0,
    szAppName,
    szAppName,
    WS_POPUP,
    128,
    128,
    256,
    256,
    NULL,
    NULL,
    hInstance,
    NULL
  );

  if(!hMainWindow) return 1;

  ShowWindow(hMainWindow, SW_SHOW);

  //- Message loop

  while(TRUE) {
    while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
      if (msg.message == WM_QUIT) return TRUE;
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
    HDC dc = GetDC(hMainWindow);
    Redraw(dc);
    ReleaseDC(hMainWindow, dc);
  }

  return 0;
}



//---- END OF FILE -----------------------------------------------------------//




