#include "texture_class.h"

texture_class::texture_class()
{
	m_targaData = 0;
	m_texture = 0;
	m_textureView = 0;
}


texture_class::texture_class(const texture_class& other)
{
}


texture_class::~texture_class()
{
}

bool texture_class::Initialize(ID3D11Device * device, ID3D11DeviceContext * deviceContext, std::string filename)
{
    LOG_INIT("Initializing texture: " + filename);

	bool result;
	D3D11_TEXTURE2D_DESC textureDesc;
	HRESULT hResult;
	unsigned int rowPitch;
	D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
	// Load the targa image data into memory.
	result = LoadTarga(filename);
	if (!result)
	{
		Logger::Get().Log("Failed to load targa data", __FILE__, __LINE__, Logger::LogLevel::Error);
		R_FALSE
	}
	// Setup the description of the texture.
	textureDesc.Height = m_height;
	textureDesc.Width = m_width;
	textureDesc.MipLevels = 0;
	textureDesc.ArraySize = 1;
	textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	textureDesc.SampleDesc.Count = 1;
	textureDesc.SampleDesc.Quality = 0;
	textureDesc.Usage = D3D11_USAGE_DEFAULT;
	textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
	textureDesc.CPUAccessFlags = 0;
	textureDesc.MiscFlags = D3D11_RESOURCE_MISC_GENERATE_MIPS;

    // Create the empty texture.
    hResult = device->CreateTexture2D(&textureDesc, NULL, &m_texture);
    if (FAILED(hResult))
    {
        LOG_ERROR("Failed to create texture");
        R_FALSE
    }

	// Set the row pitch of the targa image data.
	rowPitch = (m_width * 4) * sizeof(unsigned char);
	// Copy the targa image data into the texture.
	deviceContext->UpdateSubresource(m_texture, 0, NULL, m_targaData, rowPitch, 0);
	// Setup the shader resource view description.
	srvDesc.Format = textureDesc.Format;
	srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
	srvDesc.Texture2D.MostDetailedMip = 0;
	srvDesc.Texture2D.MipLevels = -1;

    // Create the shader resource view for the texture.
    hResult = device->CreateShaderResourceView(m_texture, &srvDesc, &m_textureView);
    if (FAILED(hResult))
    {
        LOG_ERROR("Failed to create shader resource view");
        R_FALSE
    }

	// Generate mipmaps for this texture.
	deviceContext->GenerateMips(m_textureView);

	// Release the targa image data now that the image data has been loaded into the texture.
	delete[] m_targaData;
	m_targaData = 0;

    LOG_INIT("Texture initialized");

    R_TRUE
}

void texture_class::Shutdown()
{
    LOG_SHUTDOWN("Shutting down texture");
    // Release the texture view resource.
    if (m_textureView)
    {
        m_textureView->Release();
        m_textureView = 0;
    }

	// Release the texture.
	if (m_texture)
	{
		m_texture->Release();
		m_texture = 0;
	}

	// Release the targa data.
	if (m_targaData)
	{
		delete[] m_targaData;
		m_targaData = 0;
	}

    LOG_SHUTDOWN("Texture shut down");

	return;
}

ID3D11ShaderResourceView* texture_class::GetTexture()
{
	return m_textureView;
}

bool texture_class::LoadTarga(std::string filename)
{
    LOG_INFO("Loading targa file: " + filename);
    int error, bpp, imageSize, index, i, j, k;
    FILE* filePtr;
    unsigned int count;
    TargaHeader targaFileHeader;
    unsigned char* targaImage;


    // Open the targa file for reading in binary.
    error = fopen_s(&filePtr, filename.c_str(), "rb");
    if (error != 0)
    {
        LOG_ERROR("Failed to open targa file. Working directory: " + std::filesystem::current_path().string());
        R_FALSE
    }

    // Read in the file header.
    count = (unsigned int)fread(&targaFileHeader, sizeof(TargaHeader), 1, filePtr);
    if (count != 1)
    {
        LOG_ERROR("Failed to read targa file header");
        R_FALSE
    }

	// Get the important information from the header.
	m_height = (int)targaFileHeader.height;
	m_width = (int)targaFileHeader.width;
	bpp = (int)targaFileHeader.bpp;

    // Check that it is 32 bit and not 24 bit.
    if (bpp != 32 && bpp != 24)
    {
        LOG_ERROR("Targa file is not 32 or 24 bit");
        R_FALSE
    }

	// Calculate the size of the 32 bit image data.
	imageSize = m_width * m_height * (bpp / 8);

	// Allocate memory for the targa image data.
	targaImage = new unsigned char[imageSize];

    // Read in the targa image data.
    count = (unsigned int)fread(targaImage, 1, imageSize, filePtr);
    if (count != imageSize)
    {
        LOG_ERROR("Failed to read targa image data");
        R_FALSE
    }

    // Close the file.
    error = fclose(filePtr);
    if (error != 0)
    {
        LOG_ERROR("Failed to close targa file");
        R_FALSE
    }

	// Allocate memory for the targa destination data.
	m_targaData = new unsigned char[imageSize];

	// Initialize the index into the targa destination data array.
	index = 0;

	// Initialize the index into the targa image data.
	k = (m_width * m_height * (bpp / 8)) - (m_width * (bpp / 8));

    // Now copy the targa image data into the targa destination array in the correct order since the targa format is stored upside down and also is not in RGBA order.
    for (j = 0; j < m_height; j++)
    {
        for (i = 0; i < m_width; i++)
        {
            if (index + 3 < imageSize) // Ajout de la vérification ici
            {
                m_targaData[index + 0] = targaImage[k + 2];  // Red.
                m_targaData[index + 1] = targaImage[k + 1];  // Green.
                m_targaData[index + 2] = targaImage[k + 0];  // Blue
                if (bpp == 32)
                {
                    m_targaData[index + 3] = targaImage[k + 3];  // Alpha
                }
            }
            else
            {
                LOG_ERROR("Index out of bounds");
                R_FALSE
            }

            // Increment the indexes into the targa data.
            k += (bpp / 8);
            index += (bpp / 8);
        }

        // Set the targa image data index back to the preceding row at the beginning of the column since its reading it in upside down.
        k -= (m_width * (bpp / 8) * 2);
    }


    // Release the targa image data now that it was copied into the destination array.
    delete[] targaImage;
    targaImage = 0;

    LOG_INFO("targa file " + filename + " loaded");

    R_TRUE
}


int texture_class::GetWidth()
{
	return m_width;
}


int texture_class::GetHeight()
{
	return m_height;
}
