/*********************************************************************
 *                                                                   *
 *  rolex.c: Windows clock application for WINE (by Jim Peterson)    *
 *                                                                   *
 *  This is a translation of a Turbo Pascal OWL application I made   *
 *  once, so it's a little flaky (tons of globals, functions that    *
 *  could have been in-lined, etc.).  The source code should easily  *
 *  compile with a standard Win32 C compiler.                        *
 *                                                                   *
 *  To try it out, type 'make rolex'.                                *
 *                                                                   *
 *********************************************************************/

#include <math.h>
#include <string.h>
#include "windows.h"

char AppName[] = "Rolex";
char WindowName[] = "Rolex";
int WindowWidth = 100;
int WindowHeight = 121;
COLORREF FaceColor = RGB(192,192,192);
COLORREF HandColor = RGB(0,0,0);
COLORREF EtchColor = RGB(0,0,0);

float Pi=3.1415926;

typedef struct
{
  int StartX,StartY,EndX,EndY;
} HandData;

int MaxX,MaxY;
HandData OldSecond,OldHour,OldMinute;

HWND HWindow;

void DrawFace(HDC dc)
{
  int MidX, MidY, t;

  MidX=MaxX/2;
  MidY=MaxY/2;
  SelectObject(dc,CreateSolidBrush(FaceColor));
  SelectObject(dc,CreatePen(PS_SOLID,1,EtchColor));
  Ellipse(dc,0,0,MaxX,MaxY);

  for(t=0; t<12; t++)
  {
    MoveToEx(dc,MidX+sin(t*Pi/6)*0.9*MidX,MidY-cos(t*Pi/6)*0.9*MidY,NULL);
    LineTo(dc,MidX+sin(t*Pi/6)*0.8*MidX,MidY-cos(t*Pi/6)*0.8*MidY);
  }
  if(MaxX>64 && MaxY>64)
    for(t=0; t<60; t++)
      SetPixel(dc,MidX+sin(t*Pi/30)*0.9*MidX,MidY-cos(t*Pi/30)*0.9*MidY
	       ,EtchColor);
  DeleteObject(SelectObject(dc,GetStockObject(NULL_BRUSH)));
  DeleteObject(SelectObject(dc,GetStockObject(NULL_PEN)));
  memset(&OldSecond,0,sizeof(OldSecond));
  memset(&OldMinute,0,sizeof(OldMinute));
  memset(&OldHour,0,sizeof(OldHour));
}

void DrawHourHand(HDC dc)
{
  MoveToEx(dc, OldHour.StartX, OldHour.StartY, NULL);
  LineTo(dc, OldHour.EndX, OldHour.EndY);
}

void DrawMinuteHand(HDC dc)
{
  MoveToEx(dc, OldMinute.StartX, OldMinute.StartY, NULL);
  LineTo(dc, OldMinute.EndX, OldMinute.EndY);
}

void DrawSecondHand(HDC dc)
{
  MoveToEx(dc, OldSecond.StartX, OldSecond.StartY, NULL);
  LineTo(dc, OldSecond.EndX, OldSecond.EndY);
}

BOOL UpdateHourHand(HDC dc,int MidX,int MidY,int XExt,int YExt,WORD Pos)
{
  int Sx, Sy, Ex, Ey;
  BOOL rv;

  rv = FALSE;
  Sx = MidX; Sy = MidY;
  Ex = MidX+sin(Pos*Pi/6000)*XExt;
  Ey = MidY-cos(Pos*Pi/6000)*YExt;
  rv = ( Sx!=OldHour.StartX || Ex!=OldHour.EndX ||
	 Sy!=OldHour.StartY || Ey!=OldHour.EndY );
  if(rv)DrawHourHand(dc);
  OldHour.StartX = Sx; OldHour.EndX = Ex;
  OldHour.StartY = Sy; OldHour.EndY = Ey;
  return rv;
}

BOOL UpdateMinuteHand(HDC dc,int MidX,int MidY,int XExt,int YExt,WORD Pos)
{
  int Sx, Sy, Ex, Ey;
  BOOL rv;

  rv = FALSE;
  Sx = MidX; Sy = MidY;
  Ex = MidX+sin(Pos*Pi/30000)*XExt;
  Ey = MidY-cos(Pos*Pi/30000)*YExt;
  rv = ( Sx!=OldMinute.StartX || Ex!=OldMinute.EndX ||
	 Sy!=OldMinute.StartY || Ey!=OldMinute.EndY );
  if(rv)DrawMinuteHand(dc);
  OldMinute.StartX = Sx; OldMinute.EndX = Ex;
  OldMinute.StartY = Sy; OldMinute.EndY = Ey;
  return rv;
}

BOOL UpdateSecondHand(HDC dc,int MidX,int MidY,int XExt,int YExt,WORD Pos)
{
  int Sx, Sy, Ex, Ey;
  BOOL rv;

  rv = FALSE;
  Sx = MidX; Sy = MidY;
  Ex = MidX+sin(Pos*Pi/3000)*XExt;
  Ey = MidY-cos(Pos*Pi/3000)*YExt;
  rv = ( Sx!=OldSecond.StartX || Ex!=OldSecond.EndX ||
	 Sy!=OldSecond.StartY || Ey!=OldSecond.EndY );
  if(rv)DrawSecondHand(dc);
  OldSecond.StartX = Sx; OldSecond.EndX = Ex;
  OldSecond.StartY = Sy; OldSecond.EndY = Ey;
  return rv;
}

void Idle(HDC idc)
{
  SYSTEMTIME st;
  WORD H, M, S, F;
  int MidX, MidY;
  HDC dc;
  BOOL Redraw;

  if(idc)
    dc=idc;
  else
    dc=GetDC(HWindow);
  if(!dc)return;

  GetLocalTime(&st);
  H = st.wHour;
  M = st.wMinute;
  S = st.wSecond;
  F = st.wMilliseconds / 10;
  F = F + S*100;
  M = M*1000+F/6;
  H = H*1000+M/60;
  MidX = MaxX/2;
  MidY = MaxY/2;
  SelectObject(dc,CreatePen(PS_SOLID,1,FaceColor));
  Redraw = FALSE;
  if(UpdateHourHand(dc,MidX,MidY,MidX*0.5,MidY*0.5,H)) Redraw = TRUE;
  if(UpdateMinuteHand(dc,MidX,MidY,MidX*0.65,MidY*0.65,M)) Redraw = TRUE;
  if(UpdateSecondHand(dc,MidX,MidY,MidX*0.79,MidY*0.79,F)) Redraw = TRUE;
  DeleteObject(SelectObject(dc,CreatePen(PS_SOLID,1,HandColor)));
  if(Redraw)
  {
    DrawSecondHand(dc);
    DrawMinuteHand(dc);
    DrawHourHand(dc);
  }
  DeleteObject(SelectObject(dc,GetStockObject(NULL_PEN)));
  if(!idc) ReleaseDC(HWindow,dc);
}

LRESULT CALLBACK ProcessAppMsg(HWND wnd,UINT msg,WPARAM w,LPARAM l)
{
  PAINTSTRUCT PaintInfo;
  HDC dc;

  switch(msg)
  {
  case WM_PAINT:
    if(GetUpdateRect(wnd,NULL,FALSE))
    {
      dc=BeginPaint(wnd,&PaintInfo);
      DrawFace(dc);
      Idle(dc);
      EndPaint(wnd,&PaintInfo);
    }
    break;

  case WM_SIZE:
    MaxX = LOWORD(l);
    MaxY = HIWORD(l);
    break;

  case WM_DESTROY:
    PostQuitMessage (0);
    break;

  default:
    return DefWindowProc (wnd, msg, w, l);
  }
  return 0l;
}

WPARAM MessageLoop()
{
  MSG msg;

  while(1)
  {
    Sleep(1); /* sleep 1 millisecond */
    if(PeekMessage(&msg,0,0,0,PM_REMOVE))
    {
      if(msg.message == WM_QUIT) return msg.wParam;
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
    else
      Idle(NULL);
  }
}

int PASCAL WinMain (HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
{
  WNDCLASS class;
  if(!prev)
  {
    class.style = CS_HREDRAW | CS_VREDRAW;
    class.lpfnWndProc = ProcessAppMsg;
    class.cbClsExtra = 0;
    class.cbWndExtra = 0;
    class.hInstance  = inst;
    class.hIcon      = 0; /* Draw my own icon */
    class.hCursor    = LoadCursor (0, IDC_ARROW);
    class.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
    class.lpszMenuName = 0;
    class.lpszClassName = AppName;
  }
  if (!RegisterClass (&class)) return -1;

  HWindow=CreateWindowEx(WS_EX_TOPMOST,AppName,WindowName,WS_OVERLAPPEDWINDOW,
			 CW_USEDEFAULT,CW_USEDEFAULT,WindowWidth,WindowHeight,
			 0,0,inst,0);
  memset(&OldSecond,0,sizeof(OldSecond));
  memset(&OldMinute,0,sizeof(OldMinute));
  memset(&OldHour,0,sizeof(OldHour));
  MaxX = WindowWidth;
  MaxY = WindowHeight;

  ShowWindow (HWindow, show);
  UpdateWindow (HWindow);

  return MessageLoop();
}
