// Copyright (C) 2018-2025, Tellusim Technologies Inc. All rights reserved
// https://tellusim.com/

#include <common/common.h>
#include <format/TellusimBrep.h>
#include <platform/TellusimDevice.h>
#include <platform/TellusimCommand.h>
#include <interface/TellusimCanvas.h>

/*
 */
using namespace Tellusim;

/*
 */
int32_t main(int32_t argc, char **argv) {
	
	using Tellusim::sin;
	using Tellusim::cos;
	
	DECLARE_WINDOW
	if(!window) return 1;
	
	// create window
	String title = String::format("%s Tellusim::Shape", window.getPlatformName());
	if(!window.create(title) || !window.setHidden(false)) return 1;
	
	// create device
	Device device(window);
	if(!device) return 1;
	
	// load brep
	Brep brep;
	if(!brep.load("sansb.ttf")) return 1;
	
	// create canvas
	Canvas canvas;
	
	// create shapes
	float32_t size = 84.0f;
	Array<CanvasShape> shapes;
	for(uint32_t i = '!'; i <= '~'; i++) {
		uint32_t index = brep.findGeometry(String::format("%c", i).get());
		if(index == Maxu32) continue;
		BrepGeometry geometry = brep.getGeometry(index);
		Matrix4x3d transform = geometry.getTransform();
		Vector2d offset = Vector2d(transform.m03, -0.25);
		for(BrepFace face : geometry.getFaces()) {
			if(!face.isPlane()) continue;
			
			// create shape
			CanvasShape shape(canvas);
			shapes.append(shape);
			
			// create curves
			for(BrepCurve curve : face.getCurves()) {
				uint32_t index = 0;
				uint32_t line_index = 0;
				uint32_t quadratic_index = 0;
				if(shape.getNumPositions()) {
					shape.addPosition(Vector2f::zero);
					shape.addPosition(Vector2f::zero);
					shape.addPosition(Vector2f::zero);
				}
				while(true) {
					if(line_index < curve.getNumLineIndices()) {
						uint32_t v0 = curve.getLineIndex(line_index + 0);
						if(index == v0) {
							uint32_t v1 = curve.getLineIndex(line_index + 1);
							shape.addPosition(Vector2f(curve.getVertex(v0) + offset) * size);
							shape.addPosition(Vector2f(curve.getVertex(v1) + offset) * size);
							shape.addPosition(Vector2f(curve.getVertex(v1) + offset) * size);
							line_index += 2;
							index = v1;
							continue;
						}
					}
					if(quadratic_index < curve.getNumQuadraticIndices()) {
						uint32_t v0 = curve.getQuadraticIndex(quadratic_index + 0);
						if(index == v0) {
							uint32_t v1 = curve.getQuadraticIndex(quadratic_index + 1);
							uint32_t v2 = curve.getQuadraticIndex(quadratic_index + 2);
							shape.addPosition(Vector2f(curve.getVertex(v0) + offset) * size);
							shape.addPosition(Vector2f(curve.getVertex(v1) + offset) * size);
							shape.addPosition(Vector2f(curve.getVertex(v2) + offset) * size);
							quadratic_index += 3;
							index = v2;
							continue;
						}
					}
					break;
				}
			}
		}
	}
	
	// create target
	Target target = device.createTarget(window);
	
	// main loop
	DECLARE_GLOBAL
	window.run([&]() -> bool {
		DECLARE_COMMON
		
		Window::update();
		
		if(!window.render()) return false;
		
		// canvas size
		float32_t height = 720.0f;
		float32_t width = window.getWidth() * height / window.getHeight();
		canvas.setViewport(width, height);
		
		// perspective transformation
		Matrix4x4f transform = Matrix4x4f::identity;
		transform *= Matrix4x4f::translate(canvas.getWidth() * 0.5f, canvas.getHeight() * 0.5f, 0.0f);
		transform *= Matrix4x4f::scale(canvas.getHeight() * 0.6f);
		transform *= Matrix4x4f::perspective(60.0f, 1.0f, 1.0f, 10000.0f);
		transform *= Matrix4x4f::lookAt(Vector3f(0.0f, 0.0f, 1000.0f), Vector3f::zero, Vector3f(0.0f, 1.0f, 0.0f));
		transform *= Matrix4x4f::rotateY(Tellusim::sin(time * 0.5f) * 85.0f);
		transform *= Matrix4x4f::rotateZ(Tellusim::cos(time) * 16.0f);
		transform *= Matrix4x4f::translate(-width * 0.5f, -height * 0.5f, 0.0f);
		
		// align shapes
		float32_t dx = width / (14.0f + 1.0f);
		float32_t dy = height / (7.0f + 1.0f);
		for(uint32_t i = 0; i < shapes.size(); i++) {
			float32_t x = (i % 14) * dx + dx;
			float32_t y = height - (i / 14) * dy - dy;
			shapes[i].setTransform(transform * Matrix4x4f::translate(x, y, 0.0f));
		}
		
		// window title
		if(fps > 0.0f) window.setTitle(String::format("%s %.1f FPS", title.get(), fps));
		
		// create canvas
		if(!canvas.create(device, target)) return false;
		
		// window target
		target.setClearColor(0.2f, 0.2f, 0.2f, 1.0f);
		target.begin();
		{
			// create command list
			Command command = device.createCommand(target);
			
			// draw canvas
			canvas.draw(command, target);
		}
		target.end();
		
		if(!window.present()) return false;
		
		if(!device.check()) return false;
		
		return true;
	});
	
	// finish context
	window.finish();
	
	return 0;
}
