#include <windows.h>
#include <stdio.h>

// will probably only compile with MinGW GCC

typedef struct {
  PWNDCLASSEX class;
  HWND hWnd;
  char *title;
  int style;
  int x, y;
} window_instance;

// call this to initialize a WNDCLASSEX and a window_instance.
// doesn't register the window class.  do that yourself.
void init_window_classex(PWNDCLASSEX class,    // pointer to uninitialized class
                         window_instance *win, // pointer to uninitialized inst
                         HINSTANCE hInstance,  // of the application
                         WNDPROC WndProc,
                         LPCSTR name);         // of the window class

// call this to actually instantiate a window handle
// returns 0 on failure
int create_window(window_instance *win, int width, int height);

// for things that you want to MessageBox about and exit if they fail
#define check(foo) pcheck(foo, int)
#define pcheck(foo, type) (type)_check(#foo, (int)foo)
int _check(char *contents, int result);


void init_window_classex(PWNDCLASSEX class,
                         window_instance *win,
                         HINSTANCE hInstance,
                         WNDPROC WndProc,
                         LPCSTR name)
{
  WNDCLASSEX mine = {    // What a fucking pain in the ass.
    // never changing:
    .cbSize = sizeof(WNDCLASSEX),
    .style = 0,
    .cbClsExtra = 0,
    .cbWndExtra = 0,

    // supplied by user:
    .lpfnWndProc = WndProc,
    .hInstance = hInstance,
    .lpszClassName = name,

    // sensible defaults:
    .hIcon = LoadIcon(NULL, IDI_APPLICATION),
    .hCursor = LoadCursor(NULL, IDC_ARROW),
    .hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH),
    .lpszMenuName = NULL,
    .hIconSm = LoadIcon(NULL, IDI_WINLOGO),
  };

  *class = mine;
  win->class = class;
  win->hWnd = NULL;
  win->title = "Anonymous coward window";  // default value
  win->style = WS_OVERLAPPEDWINDOW;
  win->x = win->y = CW_USEDEFAULT;
}

int create_window(window_instance *win, int width, int height)
{
  win->hWnd = CreateWindow(win->class->lpszClassName,
                           win->title,
                           win->style,
                           win->x, win->y,
                           width, height,
                           NULL,    // parent handle
                           NULL,    // menu handle
                           win->class->hInstance,
                           NULL);
  return !!win->hWnd;
}

int _check(char *contents, int result)
{
  char errcodespace[32];
  if (result) return result;
  MessageBox(NULL, contents, "fatal _check error: this code failed", MB_OK);
  sprintf(errcodespace, "error code %d", (int)GetLastError());
  MessageBox(NULL, errcodespace, "here's why", MB_OK);
  ExitProcess(1);
}


int counter = 0;
enum { maxsize = 505 };
int y = 0, mousex = 0, mousey = 0;

void draw_in(HWND hWnd) {
  HDC hDC = pcheck(GetDC(hWnd), HDC);

  // double buffer one scan line
  HDC hDCBuffer = pcheck(CreateCompatibleDC(hDC), HDC);
  HBITMAP hbmBuffer = pcheck(CreateCompatibleBitmap(hDC, maxsize, 1), HBITMAP);
  HBITMAP hbmOldBuffer = pcheck(SelectObject(hDCBuffer, hbmBuffer), HBITMAP);

  int x;
  for (x = 0; x < maxsize; x++) {
    int dx = x - mousex;
    int dy = y - mousey;
    // YES this is a slow way of doing it; less than a megapixel per
    // second on my laptop.  Double buffering doesn't help much.
    // Sometimes, apparently at random, this call returns false (in
    // WINE).  I don't know why.  So I don't check the result.
    SetPixel(hDCBuffer, x, 0, (COLORREF)(dx*dx + dy*dy + counter));
    counter = (counter + 1) & 0xffffff;
  }

  check(BitBlt(hDC,  0, y, maxsize, 1,  hDCBuffer, 0, 0,  SRCCOPY));

  y = (y + 1) % maxsize;

  check(SelectObject(hDCBuffer, hbmOldBuffer));
  check(DeleteObject(hbmBuffer));
  check(DeleteDC(hDCBuffer));
  check(ReleaseDC(hWnd, hDC));
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 
{
  switch(msg) {
  case WM_MOUSEMOVE: 
    mousex = LOWORD(lParam);
    mousey = HIWORD(lParam);
    break;
  case WM_DESTROY:
    PostQuitMessage(0);
    break;
  default:
    return DefWindowProc(hWnd, msg, wParam, lParam);
  }
  return 0;
}

int my_message_loop(window_instance *win)
{
  MSG msg;
  for (;;) {
    // PM_NOYIELD means don't block.
    int rv = PeekMessage(&msg, NULL, 0, 0, PM_NOYIELD);
    if (!rv) draw_in(win->hWnd);  // no messages pending
    else if (msg.message == WM_QUIT) return msg.wParam;
    else {
      // I hope this second PeekMessage is guaranteed to work, and get
      // the same message.
      check(PeekMessage(&msg, NULL, 0, 0, PM_NOYIELD | PM_REMOVE));
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
  }
}

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpCmdLine,
                     int nCmdShow)
{
  WNDCLASSEX windowClass;
  window_instance win;

  init_window_classex(&windowClass, &win, hInstance, WndProc, "AClass");
  windowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
  check(RegisterClassEx(&windowClass));

  win.title = "hello, world";
  check(create_window(&win, maxsize, maxsize));
  ShowWindow(win.hWnd, nCmdShow);

  return my_message_loop(&win);
}
