output.bmp is completely black: #include <cstdint>
#include <string>
#include <vector>

#include <cstdint>
#include <fstream>
#include <sstream>
#include <vector>
#include <iostream>
#include <cmath>
#include <unistd.h>
#include <thread>

// Structure representing the BMP file header
struct bmpFileHeader {
    uint32_t fileSize;       // Size of the BMP file in bytes
    uint16_t reserved1;      // Reserved; actual value depends on the application that creates the image
    uint16_t reserved2;      // Reserved; actual value depends on the application that creates the image
    uint32_t bmpOffset;      // Offset where the pixel array (bitmap data) can be found
};

// Structure containing DIB header information of a BMP file
struct bmpFileDibInfo {
    uint32_t headerSize;         // Size of this header
    int32_t width;               // Bitmap width in pixels
    int32_t height;              // Bitmap height in pixels
    uint16_t planes;             // Number of color planes being used
    uint16_t bitsPerPixel;       // Number of bits per pixel
    uint32_t compression;        // Compression method being used
    uint32_t imageSize;          // Size of the raw bitmap data
    int32_t xPixelsPerMeter;     // Horizontal resolution of the image (pixel per meter)
    int32_t yPixelsPerMeter;     // Vertical resolution of the image (pixel per meter)
    uint32_t colorsUsed;         // Number of colors in the color palette
    uint32_t importantColors;    // Number of important colors used
};

#include <iostream>
#include <bitset>

// Class representing a Bitmap image

class Bitmap {
    private:
        bool isGrayscale;
        uint8_t bitsPerPixel;
        std::vector<std::vector<uint8_t>> pixels;
        std::vector<uint8_t> importantData;

        std::vector<std::vector<uint8_t>> palette;

        bmpFileHeader header;
        bmpFileDibInfo dibInfo;

    public:
        Bitmap() { } // Default constructor

        // Copy constructor
        Bitmap(const Bitmap& other) : pixels(other.pixels), isGrayscale(other.isGrayscale), palette(other.palette) { }

        ~Bitmap() { pixels.clear(); } // Destructor clearing the pixel data

        // Method to load a bitmap from a file
        void load(std::string);

        // Method to write a bitmap to a file
        void write(std::string);

        // Method to rotate the bitmap image
        void rotate(bool clockwise);

        // Method to apply a Gaussian filter to the bitmap image
        void applyGaussianFilter(const std::vector<std::vector<float>>& kernel, int numThreads);
        void applyGaussianFilterThread(const std::vector<std::vector<float>>& kernel, int startY, int endY);
};

// Function to generate a Gaussian kernel
std::vector<std::vector<float> > generateGaussianKernel(int size, float sigma) {
    std::vector<std::vector<float> > kernel(size, std::vector<float>(size));
    float sum = 0.0;
    int halfSize = size / 2;

    // Calculate the Gaussian values for each position in the kernel
    for (int x = -halfSize; x <= halfSize; x++) {
        for (int y = -halfSize; y <= halfSize; y++) {
            float exponent = -(x * x + y * y) / (2 * sigma * sigma);
            float value = exp(exponent) / (2 * M_PI * sigma * sigma);

            kernel[x + halfSize][y + halfSize] = value;
            sum += value;
        }
    }

    // Normalize the kernel to ensure the sum of all values is 1
    for (int i = 0; i < size; i++) {
        for (int j = 0; j < size; j++) {
            kernel[i][j] /= sum;
        }
    }

    return kernel;
}

// Extract the image name from a full path
std::string getImageName(std::string &imagePath) {
    std::string token;
    std::istringstream tokenStream(imagePath);
    std::string imageName;

    while (std::getline(tokenStream, token, '/')) {
        imageName = token;
    }
    
    return imageName;
}

void Bitmap::load(std::string fileName) {
    std::ifstream file(fileName, std::ios::binary);

    if (!file.is_open()) {
        std::cerr << "Error: Could not open file " << fileName << std::endl;
        exit(1);
    }

    unsigned char fileType[2];
    file.read(reinterpret_cast<char*>(fileType), 2);

    if ((fileType[0] != 'B') || (fileType[1] != 'M')) {
        std::cerr << "Error: File is not a BMP file" << std::endl;
        exit(1);
    }

    file.read(reinterpret_cast<char*>(&header), sizeof(bmpFileHeader));
    file.read(reinterpret_cast<char*>(&dibInfo), sizeof(bmpFileDibInfo));

    // Read palette if 8-bit
    if (dibInfo.bitsPerPixel == 8) {
        palette.resize(256, std::vector<uint8_t>(4));
        for (int i = 0; i < 256; ++i) {
            file.read(reinterpret_cast<char*>(palette[i].data()), 4);
        }
    }

    // Read any additional important data
    int extraDataSize = header.bmpOffset - sizeof(bmpFileHeader) - sizeof(bmpFileDibInfo) - (dibInfo.bitsPerPixel == 8 ? 256 * 4 : 0);
    importantData.resize(extraDataSize);
    if (extraDataSize > 0) {
        file.read(reinterpret_cast<char*>(importantData.data()), extraDataSize);
    }

    // Seek to pixel data
    file.seekg(header.bmpOffset, std::ios::beg);

    // Initialize pixels vector
    pixels.resize(dibInfo.width, std::vector<uint8_t>(dibInfo.height));

    // Calculate padding
    int bytesPerPixel = dibInfo.bitsPerPixel / 8;
    int padding = (4 - (dibInfo.width * bytesPerPixel) % 4) % 4;

    // Read pixel data row by row
    for (int x = 0; x < dibInfo.width; x++) { // BMP stores pixels bottom to top
        for (int y = 0; y < dibInfo.height; y++) {
            uint8_t pixel;
            file.read(reinterpret_cast<char*>(&pixel), sizeof(uint8_t));
            //std::cout << y << " " << x << " " << pixel << "\n";
            pixels[x][y] = pixel;
            
        }
        // Skip padding
        file.ignore(padding);
    }

    // Close the file
    file.close();
    std::cout << "File loaded\n";
    // Optional: Validate read operation
    if (!file) {
        std::cerr << "Error: Failed to read pixel data from file " << fileName << std::endl;
        exit(1);
    }
}

// Write the BMP image to a file
void Bitmap::write(std::string fileName) {
    std::ofstream file(fileName, std::ios::binary);
    
    if (!file) {
        std::cerr << "Error: Could not open file for writing." << std::endl;
        return;
    }

    file.write("BM", 2);
    
    file.write(reinterpret_cast<char*>(&header), sizeof(bmpFileHeader));
    file.write(reinterpret_cast<char*>(&dibInfo), sizeof(bmpFileDibInfo));
    
    //dibInfo.width = pixels[0].size();
    //dibInfo.height = pixels.size();
    // If 8-bit, write the color palette
    if (dibInfo.bitsPerPixel == 8) {
        for (int i = 0; i < 256; i++) {
            file.put(palette[i][0]); // Blue
            file.put(palette[i][1]); // Green
            file.put(palette[i][2]); // Red
            file.put(palette[i][3]); // Reserved
        }
    }
    for (auto &data : importantData) {
        file.write(reinterpret_cast<char*>(&data), sizeof(uint8_t));
    }
    file.seekp(header.bmpOffset);
    // Write pixel data
    int bytesPerPixel = dibInfo.bitsPerPixel / 8;
    int padding = (4 - (dibInfo.width * bytesPerPixel) % 4) % 4;
    
    for (int x = 0; x < dibInfo.width; x++) {
        for (int y = 0; y < dibInfo.height; y++) {
            //std::cout << pixels[x][y] << "\n";
            file.put(pixels[x][y]);
        }

        for (int i = 0; i < padding; i++) {
            file.put(0);
        }
        //file.put(row); 
    }

    file.close();
}

void Bitmap::rotate(bool clockwise) {
    int32_t width = pixels.size();
    int32_t heigth = pixels[0].size();

    std::vector <std::vector <uint8_t> > rotatedPixels(heigth, std::vector <uint8_t>(width));

    for (int32_t x = 0; x < width; x++) {
        for (int32_t y = 0; y < heigth; y++) {
            if (clockwise) {
                rotatedPixels[y][width - x - 1] = pixels[x][y];
            } else {
                rotatedPixels[heigth - y - 1][x] = pixels[x][y];
            }
        }
    }
    dibInfo.width = heigth;
    dibInfo.height = width;

    pixels = rotatedPixels;
}


int main() {
    // Load image
    Bitmap image;
    image.load("image.bmp");
    image.rotate(false);
    image.write("output.bmp");
    // Print image info
}