#define APPNAME "lines"

#include <windows.h>

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


static HINSTANCE hInst;
static char szAppName[100];
static char szTitle[100];

static int pen_width = 1;
static int pen_style = PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT;

BOOL InitApplication(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

static char *
error_message (int error)
{
  char *msg;
  char *retval;
  int nbytes;

  FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER
		 |FORMAT_MESSAGE_IGNORE_INSERTS
		 |FORMAT_MESSAGE_FROM_SYSTEM,
		 NULL, error, 0,
		 (LPTSTR) &msg, 0, NULL);
  nbytes = strlen (msg);

  if (nbytes > 2 && msg[nbytes-1] == '\n' && msg[nbytes-2] == '\r')
    msg[nbytes-2] = '\0';
  
  retval = strdup (msg);

  if (msg != NULL)
    LocalFree (msg);

  return retval != NULL ? retval : "";
}

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
   MSG msg;

   setvbuf (stdout, NULL, _IONBF, BUFSIZ);

   // Initialize global strings
   lstrcpy (szAppName, APPNAME);
   lstrcpy (szTitle, APPNAME);

   if (!hPrevInstance) {
      // Perform instance initialization:
      if (!InitApplication(hInstance)) {
         return (FALSE);
      }
   }

   // Perform application initialization:
   if (!InitInstance(hInstance, nCmdShow)) {
      return (FALSE);
   }

   // Main message loop:
   while (GetMessage(&msg, NULL, 0, 0)) {
     TranslateMessage(&msg);
     DispatchMessage(&msg);
   }

   return ((int) msg.wParam);

   lpCmdLine; // This will prevent 'unused formal parameter' warnings
}

BOOL InitApplication(HINSTANCE hInstance)
{
    WNDCLASS  wc;

    // Fill in window class structure with parameters that describe
    // the main window.
    wc.style         = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc   = (WNDPROC)WndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = LoadIcon (hInstance, szAppName);
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    
    wc.lpszMenuName  = szAppName;
    wc.lpszClassName = szAppName;
    
    // Register the window class and return success/failure code.
    return RegisterClass(&wc);
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;
   RECT r;

   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateWindow(szAppName, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, CW_USEDEFAULT, 800, 800,
      NULL, NULL, hInstance, NULL);

   if (!hWnd) {
      return (FALSE);
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return (TRUE);
}

static void
paint_lines (RECT *rect,
	     HDC hdc)
{
  const int w = rect->right - rect->left, h = rect->bottom - rect->top;
  const int d = (w < h) ? w : h;
  const int midx = rect->left + w/2, midy = rect->top + h/2;
  HPEN old_pen, black_pen, red_pen, blue_pen;
  LOGBRUSH logbrush;
  const COLORREF black = RGB (0, 0, 0);
  const COLORREF red = RGB (255, 0, 0);
  const COLORREF blue = RGB (0, 0, 255);
  int step;
  const int NSTEP = 16;
  const int sep = (pen_width >= 2) ? pen_width*2 : 4;
  
  logbrush.lbStyle = BS_SOLID;
  logbrush.lbColor = black;
  logbrush.lbHatch = 0;
  black_pen = ExtCreatePen (PS_COSMETIC|PS_SOLID, 1, &logbrush, 0, NULL);

  logbrush.lbColor = red;
  red_pen = ExtCreatePen (pen_style, pen_width, &logbrush, 0, NULL);

  logbrush.lbColor = blue;
  blue_pen = ExtCreatePen (pen_style, pen_width, &logbrush, 0, NULL);

  old_pen = SelectObject (hdc, black_pen);

  for (step = 0; step < NSTEP; step++)
    {
      const double angle = M_PI*2*step/NSTEP;
      const int x0 = midx + d/8. * cos (angle), y0 = midy - d/8 * sin (angle);
      const int x1 = midx + d*3./8 * cos (angle), y1 = midy - d*3./8 * sin (angle);
      const int xd = sep * cos (angle + M_PI/2), yd = -sep * sin (angle + M_PI/2);

      SelectObject (hdc, black_pen);
      MoveToEx (hdc, x0 - pen_width/2 - 1, y0, NULL);
      LineTo (hdc, x0 + pen_width/2 + 2, y0);
      MoveToEx (hdc, x0, y0 - pen_width/2 - 1, NULL);
      LineTo (hdc, x0, y0 + pen_width/2 + 2);
      MoveToEx (hdc, x1 - pen_width/2 - 1, y1, NULL);
      LineTo (hdc, x1 + pen_width/2 + 2, y1);
      MoveToEx (hdc, x1, y1 - pen_width/2 - 1, NULL);
      LineTo (hdc, x1, y1 + pen_width/2 + 2);
      MoveToEx (hdc, x0 + xd - pen_width/2 - 1, y0 + yd, NULL);
      LineTo (hdc, x0 + xd + pen_width/2 + 2, y0 + yd);
      MoveToEx (hdc, x0 + xd, y0 + yd - pen_width/2 - 1, NULL);
      LineTo (hdc, x0 + xd, y0 + yd + pen_width/2 + 2);
      MoveToEx (hdc, x1 + xd - pen_width/2 - 1, y1 + yd, NULL);
      LineTo (hdc, x1 + xd + pen_width/2 + 2, y1 + yd);
      MoveToEx (hdc, x1 + xd, y1 + yd - pen_width/2 - 1, NULL);
      LineTo (hdc, x1 + xd, y1 + yd + pen_width/2 + 2);

      SelectObject (hdc, red_pen);
      MoveToEx (hdc, x0, y0, NULL);
      LineTo (hdc, x1, y1);

      SelectObject (hdc, blue_pen);
      MoveToEx (hdc, x1 + xd, y1 + yd, NULL);
      LineTo (hdc, x0 + xd, y0 + yd);
    }

  SelectObject (hdc, old_pen);
  DeleteObject (black_pen);
  DeleteObject (red_pen);
  DeleteObject (blue_pen);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
   PAINTSTRUCT ps;
   HDC hdc;
   RECT rect;
   int i, j, k;

   switch (message) {

   case WM_PAINT:
     hdc = BeginPaint (hWnd, &ps);
     GetClientRect (hWnd, &rect);
     paint_lines (&rect, hdc);
     EndPaint (hWnd, &ps);
     break;

   case WM_DESTROY:
     PostQuitMessage(0);
     break;

   case WM_CHAR:
     if (isdigit (wParam))
       {
	 if ((pen_style & PS_GEOMETRIC) == PS_GEOMETRIC)
	   {
	     pen_width = (wParam - '0');
	     GetClientRect (hWnd, &rect);
	     InvalidateRect (hWnd, &rect, TRUE);
	   }
       }
     else if (wParam == 'f')
       {
	 pen_style = PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT;
	 GetClientRect (hWnd, &rect);
	 InvalidateRect (hWnd, &rect, TRUE);
       }
     else if (wParam == 'r')
       {
	 pen_style = PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_ROUND;
	 GetClientRect (hWnd, &rect);
	 InvalidateRect (hWnd, &rect, TRUE);
       }
     else if (wParam == 's')
       {
	 pen_style = PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_SQUARE;
	 GetClientRect (hWnd, &rect);
	 InvalidateRect (hWnd, &rect, TRUE);
       }
     else if (wParam == 'c')
       {
	 pen_style = PS_COSMETIC|PS_SOLID;
	 pen_width = 1;
	 GetClientRect (hWnd, &rect);
	 InvalidateRect (hWnd, &rect, TRUE);
       }
     goto deflt;

   default:
   deflt:
     return (DefWindowProc(hWnd, message, wParam, lParam));
   }
   return (0);
}
