#include <windows.h>
#include <stdlib.h>
#include <time.h>

static char const * const class_name = "HellClass";
static int scrw, scrh;
static HDC desk_dc, off_dc;

#define NBALOON 25

#define MAXW 75
#define MINW 10
#define MINSWAY 150
#define MAXSWAY 300
#define DELTAX 1
#define DELTAY 3

struct baloon
{
	int x, y;
	int w, h;
	int dx, dy;
	int ax, sway;
	HBRUSH color;
};

static baloon baloons[NBALOON];

void sort(baloon* a)
{
	for (int i=0; i<NBALOON; i++)
	{
		for (int j=i; j<NBALOON; j++)
		{
			if (a[i].w > a[j].w)
			{
				int t = a[i].w;
				a[i].w = a[j].w;
				a[j].w = t;

				t = a[i].h;
				a[i].h = a[j].h;
				a[j].h = t;
			}
		}
	}
}

void draw(HDC hdc)
{
	BitBlt(hdc, 0, 0, scrw, scrh, desk_dc, 0, 0, SRCCOPY);
	sort(baloons);
	for (int i=0; i<NBALOON; i++)
	{
		SelectObject(hdc, baloons[i].color);
		Ellipse(hdc, baloons[i].x, baloons[i].y, baloons[i].x + baloons[i].w, baloons[i].y + baloons[i].h);
		MoveToEx(hdc, baloons[i].x + baloons[i].w / 2, baloons[i].y + baloons[i].h, NULL);
		LineTo(hdc, baloons[i].x + baloons[i].w / 2, scrh);
	}
}

void reset(baloon& b)
{
	if (b.color)
	{
		DeleteObject(b.color);
	}
	b.w = rand() % (MAXW-MINW) + MINW;
	b.h = int(b.w * 1.5f);
	b.x = rand() % (scrw-b.w) + b.w;
	b.y = scrh + b.h + rand()%200;
	b.ax = b.x + b.w/2;
	b.sway = rand() % (MAXSWAY-MINSWAY) + MINSWAY;
	b.dx = rand() & 1 ? -DELTAX : DELTAX;
	b.dy = -DELTAY;
	b.color = CreateSolidBrush(
		RGB(
			rand() % 200 + 100,
			rand() % 200 + 100,
			rand() % 200 + 100
		)
	);
}

void init()
{
	srand(time(NULL));
	for (int i=0; i<NBALOON; i++)
	{
		reset(baloons[i]);
	}
}

void update(baloon& b)
{
	b.x += b.dx;
	b.y += b.dy;

	if (abs(b.x - b.ax) >= b.sway)
	{
		b.dx = -b.dx;
	}
	if (b.y < -b.h)
	{
		reset(b);
	}
}

void timer_func(HWND hwnd)
{
	draw(off_dc);
	HDC hdc = GetDC(hwnd);
	BitBlt(hdc, 0, 0, scrw, scrh, off_dc, 0, 0, SRCCOPY);
	ReleaseDC(hwnd, hdc);

	for (int i=0; i<NBALOON; i++)
	{
		update(baloons[i]);
	}
}

LRESULT CALLBACK window_proc(HWND hwnd, UINT umsg, WPARAM wpar, LPARAM lpar)
{
	switch (umsg)
	{
	case WM_TIMER:
		timer_func(hwnd);
		break;

	case WM_KEYDOWN:
		SendMessage(hwnd, WM_CLOSE, 0, 0);
		break;

	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hwnd, umsg, wpar, lpar);
	}
	return 0L;
}

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmd, int show)
{
	WNDCLASSEX wcx;

	wcx.cbSize = sizeof(wcx);
	wcx.style = CS_HREDRAW | CS_VREDRAW;
	wcx.lpfnWndProc = window_proc;
	wcx.cbClsExtra = wcx.cbWndExtra = 0;
	wcx.hInstance = hinst;
	wcx.hIcon = wcx.hIconSm = LoadIcon(0, IDI_APPLICATION);
	wcx.hCursor = LoadCursor(0, IDC_ARROW);
	wcx.lpszMenuName = NULL;
	wcx.lpszClassName = class_name;
	wcx.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);

	RegisterClassEx(&wcx);

	HWND hwnd = CreateWindowEx(0, class_name, "Hell", WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, hinst, NULL);
	
	scrw = GetSystemMetrics(SM_CXSCREEN);
	scrh = GetSystemMetrics(SM_CYSCREEN);

	HDC hdc = GetDC(hwnd);

	HBITMAP hbmp = CreateCompatibleBitmap(hdc, scrw, scrh);
	desk_dc = CreateCompatibleDC(hdc);
	SelectObject(desk_dc, hbmp);

	hbmp = CreateCompatibleBitmap(hdc, scrw, scrh);
	off_dc = CreateCompatibleDC(hdc);
	SelectObject(off_dc, hbmp);

	ReleaseDC(hwnd, hdc);

	hdc = GetDC(HWND_DESKTOP);

	BitBlt(desk_dc, 0, 0, scrw, scrh, hdc, 0, 0, SRCCOPY);

	init();
	SetTimer(hwnd, 1, 50, NULL);
	ShowWindow(hwnd, SW_MAXIMIZE);

	MSG msg;

	while (GetMessage(&msg, 0, 0, 0) > 0)
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return int(msg.wParam);
}
