#include <windows.h>
#include <windows.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
#include "..\gamelib\src\intrface\cddraw.h"
#include "..\gamelib\src\intrface\cdinput.h"
#include "loadpcx.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

#define CHECK(cl, ret) if (ret != 0) { OutputDebugString(cl.ErrorToString()); OutputDebugString("\n"); }

// 1 = circle with 256 pixel diameter
#define LIGHTRADIUS 2

CDDraw Cdd;
CDInput Cdi;
CDIDevice mouse;

WORD *BMP, *Light;
byte *Intense;
int Wid, Hei;
int Mx=0, My=0;
bool Dying, Safe;

void initlight(WORD *li)
{
   double nx,ny,nz;
   int x,y;

   for(y=0;y<256;y++)
   {
      for(x=0;x<256;x++)
      {
         nx=(x-128)/128.0;
         ny=(y-128)/128.0;
         nz=1-sqrt(nx*nx+ny*ny);
         if (nz<0) nz=0;
         if (nz>1) nz=1;
         li[y*256+x]=(WORD)(nz*256);
      }
   }
}

WORD _cdecl Alter(int c, int light)
{
   int r, g, b;

   r = (c & 0xF800) >> 11;
   g = (c & 0x07E0) >> 5;
   b = (c & 0x001F);

   r = (r*light) >> 8;
   g = (g*light) >> 8;
   b = (b*light) >> 8;

   return (WORD)((r<<11) | (g<<5) | b);
}


int Intensity(WORD c)
{
   int r, g, b;

   r = (c & 0xF800) >> 11;
   g = (c & 0x07E0) >> 5;
   b = (c & 0x001F);

   return (r*8 + g*4 + b*8)/3;
}


void DrawFrame()
{
   WORD *screen;
   int pitch, x, y, i, j;
   
   CHECK(Cdd,Cdd.Lock(&Cdd.Back));

   screen = (WORD*)Cdd.Back.Mem;
   pitch = Cdd.Back.Pitch;

   ZeroMemory(screen, 640*2);
   screen = (WORD*)((char *)screen+pitch*479);
   ZeroMemory(screen, 640*2);
   screen = (WORD*)Cdd.Back.Mem;
   for(y=1;y<479;y++)
   {
      screen = (WORD*)((char *)screen+pitch);
      screen[0] = 0;
      screen[639] = 0;
   }
   screen = (WORD*)Cdd.Back.Mem;
   
   for(y=1,j=0;y<Hei-1;y++)
   {
      j+=Wid;
      i=j+1;
      screen = (WORD *)((char *)screen + pitch);
      for(x=1;x<Wid-1;x++,i++)
      {
         __asm {
         // nx = Intense[i+1] - Intense[i-1];
            mov esi, Intense
            add esi, i
            mov eax, 0
            mov ecx, 0
            mov edx, 0
            mov al, [esi + 1]
            mov dl, [esi - 1]
            sub eax, edx
         // ny = Intense[i+Wid] - Intense[i-Wid];
            mov ebx, Wid
            mov cl, [esi+ebx]
            sub esi, ebx
            mov dl, [esi]
            sub ecx, edx
         // nx = Mx-x + (nx*LIGHTRADIUS)+(128*LIGHTRADIUS);
            mov edx, Mx
            sub edx, x
            shl eax, 1
            add eax, edx
            add eax, 256
         // ny = My-y + (ny*LIGHTRADIUS)+(128*LIGHTRADIUS);
            mov edx, My
            sub edx, y
            shl ecx, 1
            add ecx, edx
            add ecx, 256
         // nx/=LIGHTRADIUS;
            shr eax, 1
         // ny/=LIGHTRADIUS;
            shr ecx, 1
         // if (nx>255) nx = 255;
            cmp eax, 255
            jle short clipx1
            mov eax, 255
            clipx1:
         // if (nx<0) nx = 0;
            cmp eax, 0
            jge short clipx2
            mov eax, 0
            clipx2:
         // if (ny>255) ny = 255;
            cmp ecx, 255
            jle short clipy1
            mov ecx, 255
            clipy1:
         // if (ny<0) ny = 0;
            cmp ecx, 0
            jge short clipy2
            mov ecx, 0
            clipy2:
         // screen[x] = Alter(BMP[i]ecx,Light[ny*256+nx]eax);
            shl ecx, 8
            add ecx, eax
            mov ebx, Light
            mov ax, WORD PTR [ebx+ecx*2]
            mov ebx, BMP
            mov ecx, i
            mov dx, WORD PTR [ebx+ecx*2]
            push eax
            push edx
            call Alter
            add esp, 8
            mov edx, dword ptr [x]
            mov ecx, dword ptr [screen]
            mov word ptr [ecx+edx*2], ax
         }

/*
         nx = Intense[i+1] - Intense[i-1];
         ny = Intense[i+Wid] - Intense[i-Wid];
         nx = Mx-x + (nx*LIGHTRADIUS)+(128*LIGHTRADIUS);
         ny = My-y + (ny*LIGHTRADIUS)+(128*LIGHTRADIUS);
         nx/=LIGHTRADIUS;
         ny/=LIGHTRADIUS;
         if (nx>255) nx = 255;
         if (nx<0) nx = 0;
         if (ny>255) ny = 255;
         if (ny<0) ny = 0;
         screen[x] = Alter(BMP[i],Light[ny*256+nx]);
*/
      }
   }

   CHECK(Cdd, Cdd.Unlock(&Cdd.Back));
   CHECK(Cdd, Cdd.Flip());
}

void UpdateMouse(CDIDevice *dev)
{
   DIDEVICEOBJECTDATA dod[16];
   DWORD els = 16,i;

   if (Dying) return;
   Safe = FALSE;

   mouse.pDIDev->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), dod, &els, 0);
   for(i=0;i<els;i++)
   {
      switch(dod[i].dwOfs)
      {
         case DIMOFS_X:
            Mx += (int)dod[i].dwData;
            break;

         case DIMOFS_Y:
            My += (int)dod[i].dwData;
            break;
      }

      if (Mx < 0) Mx = 0;
      if (Mx > 639) Mx = 639;
      if (My < 0) My = 0;
      if (My > 479) My = 479;
   }

   DrawFrame();

   Safe = TRUE;
}

int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrev, PSTR lpCmdLine, int iCmdShow)
{
   HWND hWnd;
   MSG msg;
   static char AppName[]="TestProg";
   WNDCLASSEX WndClass;
   int i,len;
   char file[64];
   GUID guid;

   WndClass.cbSize      = sizeof(WndClass);
   WndClass.style       = CS_HREDRAW | CS_VREDRAW;
   WndClass.lpfnWndProc = WndProc;
   WndClass.cbClsExtra  = 0;
   WndClass.cbWndExtra  = 0;
   WndClass.hInstance   = hInst;
   WndClass.hIcon       = LoadIcon(NULL, IDI_APPLICATION);
   WndClass.hCursor     = LoadCursor(NULL, IDC_ARROW);
   WndClass.hbrBackground=(HBRUSH) GetStockObject(WHITE_BRUSH);
   WndClass.lpszMenuName= NULL;
   WndClass.lpszClassName=AppName;
   WndClass.hIconSm     = LoadIcon(NULL, IDI_APPLICATION);

   RegisterClassEx(&WndClass);
   hWnd = CreateWindowEx(WS_EX_TOPMOST, AppName,"Test",WS_POPUP,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInst,NULL);
   if (!hWnd) return 1;
   ShowWindow(hWnd, SW_HIDE);

   len = strlen(lpCmdLine);
   for(i=0;i<len;i++) if (isspace(lpCmdLine[i])) break;

   memcpy(file, lpCmdLine, i);
   file[i] = 0;

   BMP = loadpcx(file, &Wid, &Hei);
   if (!BMP) return 2;
   Mx = Wid/2;
   My = Hei/2;

   Intense = (byte *)malloc(Wid*Hei);
   if (!Intense) return 5;

   for(i=0,len=Wid*Hei;i<len;i++) Intense[i] = Intensity(BMP[i]);
   
   Light = (WORD *)malloc(65536*2);
   if (!Light) return 3;

   initlight(Light);

   mouse.Buffered = TRUE;
   mouse.Callback = UpdateMouse;

   CHECK(Cdd, Cdd.Init(hWnd));
   CHECK(Cdi, Cdi.Init(hWnd, hInst));
   CHECK(Cdi, Cdi.InitDevice(&mouse, CDI_MOUSE, DISCL_FOREGROUND | DISCL_EXCLUSIVE, guid));
   CHECK(Cdd, Cdd.SetMode(640, 480, CDD_16BIT, CDD_FULLSCREEN, CDD_DOUBLE));
   CHECK(Cdi, Cdi.Acquire(&mouse));
   CHECK(Cdi, Cdi.InitThread());

   while(GetMessage(&msg, NULL, 0, 0))
   {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
   }

   return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
   switch(iMsg)
   {
      case WM_KEYDOWN:
         if (wParam != VK_ESCAPE) return 0;
         // FALL THROUGH

      case WM_DESTROY:
         Dying = TRUE;
         Sleep(100);
         Cdi.Unacquire(&mouse);
         Cdi.KillThread();
         Cdi.KillDevice(&mouse);
         PostQuitMessage(0);
         return 0;

      case WM_PAINT:
         DrawFrame();
         ValidateRect(hWnd, NULL);
         return 0;

      case WM_MOUSEMOVE:
         ShowCursor(NULL);
         return 0;

   }

   return DefWindowProc(hWnd, iMsg, wParam, lParam);
}

