
// zlib open source license
//
// Copyright (c) 2025 David Forsgren Piuva
// 
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// 
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 
//    1. The origin of this software must not be misrepresented; you must not
//    claim that you wrote the original software. If you use this software
//    in a product, an acknowledgment in the product documentation would be
//    appreciated but is not required.
// 
//    2. Altered source versions must be plainly marked as such, and must not be
//    misrepresented as being the original software.
// 
//    3. This notice may not be removed or altered from any source
//    distribution.

#ifndef DFPSR_TEXTURE_TYPES
#define DFPSR_TEXTURE_TYPES

#include "Image.h"
#include "../../base/noSimd.h" // Scalar versions of the SIMD functions for creating template functions both with and without SIMD.
#include "../../base/DsrTraits.h" // Scalar versions of the SIMD functions for creating template functions both with and without SIMD.
#include "../math/scalar.h"

namespace dsr {

// MIP is a latin acronym "multum in parvo" meaning much in little.
static const uint32_t DSR_MIP_LEVEL_COUNT = 16;

// Mip index 0 is full resolution.
// Mip index 1 is half resolution.
// Mip index 2 is quarter resolution.
// ...
struct Texture {
	Buffer impl_buffer;
	// Base-two logarithms of the highest resolution.
	uint32_t impl_log2width = 0;
	uint32_t impl_log2height = 0;
	// Mip level indices from 0 to impl_maxMipLevel.
	uint32_t impl_maxMipLevel = 0;
	// Number of pixels before the largest mip level.
	uint32_t impl_startOffset = 0;
	uint32_t impl_maxLevelMask = 0;
	// Tiling of unsigned pixel coordinates using bit masks.
	uint32_t impl_minWidthOrMask = 0;
	uint32_t impl_minHeightOrMask = 0;
	uint32_t impl_maxWidthAndMask = 0;
	uint32_t impl_maxHeightAndMask = 0;
	// Maximum dimensions for calculating mip level.
	float impl_floatMaxWidth = 0.0f;
	float impl_floatMaxHeight = 0.0f;
	// What each pixel contains.
	uint8_t impl_pixelFormat = 0;
	Texture() {}
	// TODO: Allow creating a single layer from an existing pixel buffer, which must be free from padding.
	//       If not using multi-threading to write to an image, one can use less than a cache line for alignment.
	//       Store a bit in image saying if the image is a thread-safe write target with cache aligned rows.
	Texture(uint32_t log2width, uint32_t log2height, uint32_t maxMipLevel, PixelFormat format, uint32_t pixelSize)
	: impl_log2width(log2width), impl_log2height(log2height), impl_maxMipLevel(maxMipLevel), impl_pixelFormat(uint8_t(format)) {
		if (maxMipLevel < 0) maxMipLevel = 0;
		if (maxMipLevel >= DSR_MIP_LEVEL_COUNT) maxMipLevel = DSR_MIP_LEVEL_COUNT - 1;
		if ((int32_t)log2width - maxMipLevel < 0 || (int32_t)log2height - maxMipLevel < 0) {
			// TODO: Indicate failure.
			this->impl_pixelFormat = 0;
		} else {
			uint32_t highestLayerPixelCount = uint32_t(1) << (log2width + log2height);
			uint64_t pixelCount = 0;
			uint32_t levelPixelCount = highestLayerPixelCount;
			for (int32_t level = maxMipLevel; level >= 0; level--) {
				pixelCount = pixelCount | levelPixelCount;
				levelPixelCount = levelPixelCount >> 2;
			}
			if (pixelCount > 4294967296) {
				// TODO: Indicate failure to index pixels using 32-bit gather.
			} else {
				this->impl_startOffset = (uint32_t)pixelCount & ~highestLayerPixelCount;
				this->impl_maxLevelMask = highestLayerPixelCount - 1;
				this->impl_minWidthOrMask = (uint32_t(1) << (log2width - maxMipLevel)) - 1;
				this->impl_minHeightOrMask = (uint32_t(1) << (log2height - maxMipLevel)) - 1;
				this->impl_maxWidthAndMask = (uint32_t(1) << log2width) - 1;
				this->impl_maxHeightAndMask = (uint32_t(1) << log2height) - 1;
				this->impl_floatMaxWidth = float((uint32_t(1) << log2width));
				this->impl_floatMaxHeight = float((uint32_t(1) << log2height));
				this->impl_buffer = buffer_create((uint32_t)pixelCount * pixelSize);
			}
		}
	}
};

struct TextureRgbaU8 : public Texture {
	TextureRgbaU8() {}
	TextureRgbaU8(uint32_t log2width, uint32_t log2height, uint32_t maxMipLevel = DSR_MIP_LEVEL_COUNT - 1)
	: Texture(log2width, log2height, min(log2width, log2height, maxMipLevel), PixelFormat::RgbaU8, sizeof(uint32_t)) {}
};

}

#endif
