#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif

#include <windows.h>
#include <vector>
#include <complex>
#include <thread>
#include <mutex>
#include <atomic>
#include <chrono>

// Window size
const int WIDTH = 800;
const int HEIGHT = 600;

// Mandelbrot view
double xmin = -2.0, xmax = 1.0;
double ymin = -1.5, ymax = 1.5;
int max_iter = 500;

// Pixel buffer
std::vector<unsigned char> pixels(WIDTH* HEIGHT * 3);
HBITMAP hBitmap = nullptr;
std::mutex renderMutex;

// Track ongoing rendering
std::atomic<bool> rendering(false);

// Low-res rendering factor
int previewScale = 4;

// Forward declarations
void generateMandelbrot(int width, int height, std::vector<unsigned char>& buffer);

// Create or update the bitmap
void updateBitmap(HDC hdc)
{
    std::lock_guard<std::mutex> lock(renderMutex);

    BITMAPINFO bmi = {};
    bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth = WIDTH;
    bmi.bmiHeader.biHeight = -HEIGHT; // top-down
    bmi.bmiHeader.biPlanes = 1;
    bmi.bmiHeader.biBitCount = 24;
    bmi.bmiHeader.biCompression = BI_RGB;

    if (!hBitmap)
    {
        void* pBits;
        hBitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &pBits, nullptr, 0);
    }

    SetDIBits(hdc, hBitmap, 0, HEIGHT, pixels.data(), &bmi, DIB_RGB_COLORS);
}

// Multi-threaded block render
void renderBlock(int yStart, int yEnd, int width, int height, std::vector<unsigned char>& buffer)
{
    for (int py = yStart; py < yEnd; ++py)
    {
        double y0 = ymin + (ymax - ymin) * py / height;
        for (int px = 0; px < width; ++px)
        {
            double x0 = xmin + (xmax - xmin) * px / width;
            std::complex<double> c(x0, y0), z(0, 0);
            int iter = 0;
            while (std::abs(z) <= 2.0 && iter < max_iter) z = z * z + c, iter++;

            int idx = (py * width + px) * 3;
            buffer[idx + 0] = iter % 256;
            buffer[idx + 1] = (iter * 2) % 256;
            buffer[idx + 2] = (iter * 3) % 256;
        }
    }
}

// Multi-threaded Mandelbrot
void generateMandelbrot(int width, int height, std::vector<unsigned char>& buffer)
{
    int numThreads = std::thread::hardware_concurrency();
    std::vector<std::thread> threads;
    int blockHeight = height / numThreads;

    for (int i = 0; i < numThreads; ++i)
    {
        int yStart = i * blockHeight;
        int yEnd = (i == numThreads - 1) ? height : yStart + blockHeight;
        threads.emplace_back(renderBlock, yStart, yEnd, width, height, std::ref(buffer));
    }

    for (auto& t : threads) t.join();
}

// Start rendering in a background thread
void startRender(HWND hwnd)
{
    if (rendering) return; // avoid multiple renders
    rendering = true;

    std::thread([hwnd]()
        {
            // 1) Low-res preview
            int lowW = WIDTH / previewScale, lowH = HEIGHT / previewScale;
            std::vector<unsigned char> preview(lowW * lowH * 3);
            generateMandelbrot(lowW, lowH, preview);

            // Upscale preview to full size
            {
                std::lock_guard<std::mutex> lock(renderMutex);
                for (int y = 0;y < HEIGHT;y++)
                    for (int x = 0;x < WIDTH;x++)
                    {
                        int px = x / previewScale;
                        int py = y / previewScale;
                        int idx = (y * WIDTH + x) * 3;
                        int idxSmall = (py * lowW + px) * 3;
                        pixels[idx + 0] = preview[idxSmall + 0];
                        pixels[idx + 1] = preview[idxSmall + 1];
                        pixels[idx + 2] = preview[idxSmall + 2];
                    }
            }
            InvalidateRect(hwnd, nullptr, TRUE);

            // 2) Full-res render
            generateMandelbrot(WIDTH, HEIGHT, pixels);
            InvalidateRect(hwnd, nullptr, TRUE);

            rendering = false;
        }).detach();
}

// Dragging
int lastX = 0, lastY = 0;
bool dragging = false;

// Window procedure
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;

    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);
        updateBitmap(hdc);

        HDC memDC = CreateCompatibleDC(hdc);
        HBITMAP oldBmp = (HBITMAP)SelectObject(memDC, hBitmap);
        BitBlt(hdc, 0, 0, WIDTH, HEIGHT, memDC, 0, 0, SRCCOPY);
        SelectObject(memDC, oldBmp);
        DeleteDC(memDC);

        EndPaint(hwnd, &ps);
    }
    return 0;

    case WM_MOUSEWHEEL:
    {
        short delta = GET_WHEEL_DELTA_WPARAM(wParam);
        double zoomFactor = (delta > 0) ? 0.8 : 1.25;

        double centerX = (xmin + xmax) / 2;
        double centerY = (ymin + ymax) / 2;
        double width = (xmax - xmin) * zoomFactor;
        double height = (ymax - ymin) * zoomFactor;

        xmin = centerX - width / 2;
        xmax = centerX + width / 2;
        ymin = centerY - height / 2;
        ymax = centerY + height / 2;

        startRender(hwnd);
    }
    return 0;

    case WM_LBUTTONDOWN:
        dragging = true;
        lastX = LOWORD(lParam);
        lastY = HIWORD(lParam);
        return 0;

    case WM_LBUTTONUP:
        dragging = false;
        return 0;

    case WM_MOUSEMOVE:
        if (dragging)
        {
            int x = LOWORD(lParam);
            int y = HIWORD(lParam);
            double dx = (x - lastX) * (xmax - xmin) / WIDTH;
            double dy = (y - lastY) * (ymax - ymin) / HEIGHT;

            xmin -= dx; xmax -= dx;
            ymin += dy; ymax += dy;

            lastX = x;
            lastY = y;

            startRender(hwnd);
        }
        return 0;
    }

    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

// Entry point
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR, int nCmdShow)
{
    const wchar_t CLASS_NAME[] = L"MandelbrotWindow";

    WNDCLASS wc = {};
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;
    RegisterClass(&wc);

    HWND hwnd = CreateWindowEx(
        0, CLASS_NAME, L"Mandelbrot Explorer", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, WIDTH, HEIGHT,
        nullptr, nullptr, hInstance, nullptr
    );

    ShowWindow(hwnd, nCmdShow);

    // Initial render
    startRender(hwnd);

    MSG msg = {};
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}
