// Revised 2019 with a bit better variable names.
// Experimental C++ version 2022. Almost no code changes.
// Simple Vulkan version 2026.

#include "svk.hpp"
#include "LittleOBJLoader.h"
#include <math.h>
#include "svk_images.hpp"

struct uniforms
{
// Model-to-world matrix
// Modify this matrix.
// See below for how it is applied to your model.
    mat4 modelToWorld = mat4( 1.0, 0.0, 0.0, 0.0,
                              0.0, 1.0, 0.0, 0.0,
                              0.0, 0.0, 1.0, 0.0,
                              0.0, 0.0, 0.0, 1.0);
// World-to-view matrix. Usually set by lookAt() or similar.
    mat4 worldToView;
// Projection matrix, set by a call to perspective() or frustum().
    mat4 projectionMatrix;
} uniforms;

// Globals
// * Model(s)
Model *bunny;
// * Reference(s) to shader program(s)
PipelineData program;
// * Texture(s)
AllocatedImage texture;
AllocatedBuffer uniformsBuffer;

// Should be moved to the SVK folder.
void dumpInfo()
{
        VkPhysicalDeviceProperties deviceProperties;
        vkGetPhysicalDeviceProperties(svk._chosenGPU, &deviceProperties);
        uint32_t maxSupported = deviceProperties.limits.maxPushConstantsSize;
        printf("Vulkan API version: %d.%d.%d\n",
             VK_API_VERSION_MAJOR(deviceProperties.apiVersion),
             VK_API_VERSION_MINOR(deviceProperties.apiVersion),
             VK_API_VERSION_PATCH(deviceProperties.apiVersion)
        );
        printf("Device: %s\n", deviceProperties.deviceName);
        printf("Max push constants: %d\n", maxSupported);
        printf("Max image dimensions: %d %d %d\n",
              deviceProperties.limits.maxImageDimension1D, 
              deviceProperties.limits.maxImageDimension2D, 
              deviceProperties.limits.maxImageDimension3D); // Note: Many more in VkPhysicalDeviceLimits.  See also svk.printDeviceLimits.
}


void init(void)
{
	dumpInfo();

	svk.setBackgroundColor(0.2, 0.2, 0.5);

	// Load and compile shader
	svkBuildPipeline(program, "lab0.vert", "lab0.frag");

	uniforms.projectionMatrix = frustum(-0.5, 0.5, -0.5, 0.5, 0.5, 100); // perspective(90, 1.0, 0.1, 1000);
	uniforms.worldToView = lookAt(0,0,1.5, 0,0,0, 0,1,0);
	svk.allocateUniformBuffer(uniformsBuffer, sizeof(uniforms));
        svk.uploadToUniformBuffer(uniformsBuffer, &uniforms); // 	
        svkInstallUniform(program, "uniforms", uniformsBuffer);
	
	// Upload geometry to the GPU:
	bunny = LoadModel("objects/stanford-bunny.obj");

	// Load textures
        svk.loadTGATexture(texture, "textures/maskros512.tga");
//        svkInstallTexture(pld, "texture", texture); Add this once you use a texture in the shader
}


void display(VkCommandBuffer cmd)
{
        svk.beginRendering(cmd, true);
        
	vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, program.pipeline);
        svkBindPipelineData(cmd, program);

// Change matrices for animation	
	svk.uploadToUniformBuffer(uniformsBuffer, &uniforms);

	//draw the model
	DrawModel(bunny, cmd);
	
        vkCmdEndRendering(cmd);
}

int main()
{
	svk.initWindowName("Lab 0 - Simpler Vulkan Introduction");
	svk.init();

	init();

	svk.bindDrawingFunction(display);

	svk.run();

	svk.cleanup();

	return 0;
}

