/*         ______   ___    ___ 
 *        /\  _  \ /\_ \  /\_ \ 
 *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___ 
 *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
 *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
 *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
 *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
 *                                           /\____/
 *                                           \_/__/
 *
 *      Example program showing how to write a Windows screensaver. This
 *      uses a fullscreen DirectX mode when activated normally, or the
 *      GDI interface functions when it is running in preview mode from
 *      inside the Windows screensaver selection dialog.
 *
 *      Compile this like a normal executable, but rename the output
 *      program to have a .scr extension, and then copy it into your
 *      windows directory.
 *
 *      By Shawn Hargreaves.
 *
 *      See readme.txt for copyright information.
 */


#include <time.h>
#include <math.h>

#include <allegro.h>
#include <winalleg.h>

BITMAP *buf;

class CBloob {
public:
	BITMAP *bmp;
	int upper, lower;
	double x, y, dx, dy;
	int ix, iy;
	int a_matt;

	CBloob(int diameter, int from_col, int to_col);
	~CBloob();
	void setupBlender();
	void draw(BITMAP *b);


};


char bloob_blender[256][256];


CBloob::CBloob(int diameter, int from_col, int to_col) {
	bmp = create_bitmap(diameter, diameter);
	upper = to_col;
	lower = from_col;
	int dist = upper-lower;
	dx = rand()%40/10.0+1;
	dy = rand()%50/10.0;
	a_matt = (int)(diameter * 0.07);
	int a,b;

	if (bmp != NULL) {
		//generate bloob
		for(int y=0;y<diameter;y++)
			for(int x=0;x<diameter;x++) {
				int dx = x - diameter / 2;
				int dy = y - diameter / 2;
				float k = 1 - sqrt(dx*dx+dy*dy) * 2 / diameter;
			    if (k < 0) k = 0;
				unsigned char c = (unsigned char)MAX(dist * k * k, 0);
				// unsigned char c = (unsigned char)MAX(dist * k, 0);
				bmp->line[x][y] = c;
			}

	}
}

CBloob::~CBloob() {
	if (bmp != NULL) destroy_bitmap(bmp);
}

void CBloob::setupBlender() {
	int a, b;

	// setup blender
	for(a=0;a<256;a++)
		for(b=0;b<256;b++)
			bloob_blender[a][b] = MIN(a+b, upper);
}

void CBloob::draw(BITMAP *dest_bmp) {
	int v,u;
	ix = (int)x;
	iy = (int)y;

	// add bloob to bitmap
    for(v=a_matt;v<bmp->h-a_matt;v++)
      for(u=a_matt;u<bmp->w-a_matt;u++)
		  dest_bmp->line[iy+v][ix+u] = bloob_blender[dest_bmp->line[iy+v][ix+u]][bmp->line[v][u]];
}

#define MAX_BLOOBS	64

// bitmaps
BITMAP *swap_screen = NULL, *background = NULL;

	int i;
	float tick = 0;
  bool alloked = false;
	CBloob *b[MAX_BLOOBS];
	RGB rgb = { 0, 0, 0 };

/* initialises our graphical effect */
void ss_init(void)
{

	swap_screen = create_bitmap(320, 240);
	background = create_bitmap(320, 240);



	//clear(buf);
	set_color(0, &rgb);

	// setup palette
	for(i=0;i<128;i++) {
		rgb.b = (i*63)/128;
		rgb.g = (i>=64 ? (i-64) : 0);
		rgb.r = (i>=96 ? (i-96)*2 : 0);
		set_color(i, &rgb);
	}

	// create bloobs
	for(i=0;i<MAX_BLOOBS;i++) {
		int size = MAX(MIN(MAX_BLOOBS - i / 2, 64), 5);
		b[i] = new CBloob(size, 0, 127);
	}
  alloked= true;

	// set up bloob blender
	b[0]->setupBlender();

	// create background
	clear(background);


}


	int w = 48, h = 32;
/* animates the graphical effect */
void ss_update(void)
{
		tick += .02;

		// move blobs
		for(i=0;i<MAX_BLOOBS;i++) {
			b[i]->x = 160 - b[i]->bmp->w / 2 + w * cos(tick + i * 3) + w * cos(tick/3 - i * 7);
			b[i]->y = 120 - b[i]->bmp->h / 2 + h * sin(tick/9 - i * 5) + h * sin(tick + i * 3);
		}
}

/* draws the graphical effect */
void ss_draw(void)
{
 		// draw
		// clear(swap_screen);
		blit(background, swap_screen, 0, 0, 0, 0, 320, 240);

		// draw blobs
		for(i=0;i<MAX_BLOOBS;i++) {
			b[i]->draw(swap_screen);
		}

		// show fps
		// textprintf(swap_screen, font, 1, 1, makecol(100, 100, 100), "%d", fps);

		// blit to screen
		stretch_blit(swap_screen, buf, 0, 0, 320, 240, 0, 0, 640, 480);
}



/* shuts down the graphical effect */
void ss_exit(void)
{
  if (alloked) {
  	for(i=0;i<MAX_BLOOBS;i++) {
		delete b[i];
	}
  
  }
  destroy_bitmap(swap_screen);
  destroy_bitmap(background);
}



/* dialog procedure for the settings dialog */
BOOL CALLBACK settings_dlg_proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
   switch (uMsg) {

      case WM_COMMAND: 
     switch (HIWORD(wParam)) { 

        case BN_CLICKED:
           EndDialog(hwndDlg, 0);
           return 1;
     }
     break;
   }

   return 0;
}



/* the settings dialog function */
int do_settings(HANDLE hInstance, HANDLE hPrevInstance, HWND hParentWnd)
{
//   DialogBox(hInstance, "ID_CONFIG_DLG", hParentWnd, settings_dlg_proc);

   return 0;
}



/* the password dialog function */
int do_password(HANDLE hInstance, HANDLE hPrevInstance, HWND hParentWnd)
{
   MessageBox(hParentWnd, "Sorry, this screensaver doesn't implement the password stuff", "Allegro Screensaver", MB_OK);

   return 0;
}



/* window procedure for the screensaver preview */
LRESULT CALLBACK preview_wnd_proc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
{
   PAINTSTRUCT ps;
   HDC hdc;

   switch (message) {

      case WM_CREATE:
     SetTimer(hwnd, 1, 15, NULL);
     return 0;

      case WM_TIMER:
     ss_update();
     InvalidateRect(hwnd, NULL, FALSE);
     return 0;

      case WM_PAINT:
     hdc = BeginPaint(hwnd, &ps);
     ss_draw();
     set_palette_to_hdc(hdc, _current_palette);
     draw_to_hdc(hdc, buf, 0, 0);
     EndPaint(hwnd, &ps);
     return 0;

      case WM_DESTROY:
     KillTimer(hwnd, 1);
     PostQuitMessage(0);
     return 0;
   }

   return DefWindowProc(hwnd, message, wParam, lParam);
}



/* the screensaver preview function */
int do_preview(HANDLE hInstance, HANDLE hPrevInstance, HWND hParentWnd)
{
/*   WNDCLASS wndclass;
   HWND hwnd;
   MSG msg;
   RECT rc;

   if (!hPrevInstance) {
      wndclass.style = CS_HREDRAW | CS_VREDRAW;
      wndclass.lpfnWndProc = preview_wnd_proc;
      wndclass.cbClsExtra = 0;
      wndclass.cbWndExtra = 0;
      wndclass.hInstance = hInstance;
      wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
      wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
      wndclass.hbrBackground = NULL;
      wndclass.lpszMenuName = NULL;
      wndclass.lpszClassName = "sspreview";

      RegisterClass(&wndclass);
   }

   if (hParentWnd)
      GetClientRect(hParentWnd, &rc);
   else
      rc.right = rc.bottom = 256;

   install_allegro(SYSTEM_NONE, &errno, atexit);
   set_palette(default_palette);
   set_gdi_color_format();

   buf = create_bitmap(rc.right, rc.bottom);

   ss_init();
   ss_update();

   hwnd = CreateWindow("sspreview", NULL, WS_CHILD, 
               0, 0, rc.right, rc.bottom, 
               hParentWnd, NULL, hInstance, NULL);

   ShowWindow(hwnd, SW_SHOW);
   UpdateWindow(hwnd);

   while (GetMessage(&msg, NULL, 0, 0)) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
   }

   ss_exit();
   destroy_bitmap(buf);
*/
   return 0;
}



/* display switch callback */
static int foreground = TRUE;

static void dispsw_callback(void)
{
   foreground = FALSE;
}


/* run the saver normally, in fullscreen mode */
int do_saver(HANDLE hInstance, HANDLE hPrevInstance, HWND hParentWnd)
{
   HANDLE scrsaver_mutex;
   int mx, my, t;

   /* prevent multiple instances from running */
   scrsaver_mutex = CreateMutex(NULL, TRUE, "Allegro screensaver");

   if (!scrsaver_mutex || (GetLastError() == ERROR_ALREADY_EXISTS))
      return -1;

   allegro_init();
   install_keyboard();
   install_mouse();
   install_timer();
   
   /* try to set a fullscreen mode */
   if (set_gfx_mode(GFX_DIRECTX_ACCEL, 640, 480, 0, 0) != 0)
      if (set_gfx_mode(GFX_DIRECTX_SOFT, 640, 480, 0, 0) != 0)
         if (set_gfx_mode(GFX_DIRECTX_SAFE, 640, 480, 0, 0) != 0) {
            ReleaseMutex(scrsaver_mutex);
            return -1;
         }

   set_display_switch_mode(SWITCH_BACKAMNESIA);  /* not SWITCH_AMNESIA */
   set_display_switch_callback(SWITCH_OUT, dispsw_callback);

   buf = create_bitmap(SCREEN_W, SCREEN_H);

   ss_init();
   ss_update();

   mx = mouse_x;
   my = mouse_y;

   t = retrace_count;

   while (foreground && (!keypressed()) && (!mouse_b) && (mouse_x == mx) && (mouse_y == my)) {
      while (t < retrace_count) {
     ss_update();
     t++;
      }

      ss_draw();
      blit(buf, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);

      poll_mouse();
      while (t == retrace_count)
        yield_timeslice();
   }

   ss_exit();
   destroy_bitmap(buf);

   ReleaseMutex(scrsaver_mutex);
   return 0;
}



/* the main program body */
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
{
   HWND hwnd;
   char *args;

   args = lpszCmdParam;

   if ((args[0] == '-') || (args[0] == '/'))
      args++;

   if ((args[0]) && ((args[1] == ' ') || (args[1] == ':')))
      hwnd = (HWND)atoi(args+2);
   else
      hwnd = GetActiveWindow();

   switch (utolower(args[0])) {

      case 'c':
     return do_settings(hInstance, hPrevInstance, hwnd);

      case 'a':
     return do_password(hInstance, hPrevInstance, hwnd);

      case 'p':
     return do_preview(hInstance, hPrevInstance, hwnd);

      case 's':
     return do_saver(hInstance, hPrevInstance, hwnd);
   }

   return 0;
}
