/*
	Copyright (C) 2021-2025 Mark Tyler

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program in the file COPYING.
*/

#include "lanter_be_terrascene.h"



TerraScene::TerraScene ()
	:
	m_light ( LANDSCAPE_VIEW_RANGE * 0.5f,
		LANDSCAPE_VIEW_RANGE * 0.5f,
		LANDSCAPE_VIEW_RANGE * 0.7f
		)
{
}

void TerraScene::init_buffers ( TerraMap const & map )
{
	if ( m_buffer_ready )
	{
		return;
	}
	else
	{
		m_buffer_ready = 1;
	}

	std::string const version ( mtGin::GL::get_shader_language_version() );

	m_gl_lines.init ( version );

	CubeLines cube ( GLfloat(LANDSCAPE_VIEW_RANGE)/2.0f );
	m_gl_lines.populate ( cube.get_data(), cube.get_data_size() );

	m_gl_land_triangles.init ( version );
	m_gl_land_wireframe.init ( version );
	m_gl_land_points.init ( version );

	LandTriangles land_tri;
	if ( land_tri.init ( map ) )
	{
		return;
	}
	m_gl_land_triangles.populate ( land_tri.get_data(),
		land_tri.get_vertex_count() );

	LandLines land_lines;
	if ( land_lines.init ( map ) )
	{
		return;
	}
	m_gl_land_wireframe.populate ( land_lines.get_data(),
		land_lines.get_vertex_count() );

	LandPoints land_points;
	if ( land_points.init ( map ) )
	{
		return;
	}
	m_gl_land_points.populate ( land_points.get_data(),
		land_points.get_vertex_count() );

	std::vector<mtGin::Vertex> bezpo = {
		{-1000,	-500,	600 },
		{ 0,	-500,	700 },
		{ 0,	-100,	1000 },
		{ 500,	-500,	700 },
		{ 700,	-500,	900 }
		};

	mtGin::BezierPath bez;
	bez.set_smooth_curve ( bezpo );

	size_t const ntot = bez.get_size();

	if ( ntot > 1 )
	{
		std::vector< mtGin::GL::VertexRGB > bezls;
		mtGin::Vertex p1, p2, cp1, cp2;

		for ( size_t i = 1; i < ntot; i++ )
		{
			mtGin::BezierNode const * const n1 = bez.get_node ( i );
			n1->get ( p1, cp1, p2, cp2 );

			// Create line from point to point
			bezls.push_back ( { (GLfloat)p1.x, (GLfloat)p1.y,
				(GLfloat)p1.z, 0.7f, 0.7f, 0.5f } );
			bezls.push_back ( { (GLfloat)p2.x, (GLfloat)p2.y,
				(GLfloat)p2.z, 0.7f, 0.7f, 0.5f } );

			// Create lines depicting vertex control points
			bezls.push_back ( { (GLfloat)p1.x, (GLfloat)p1.y,
				(GLfloat)p1.z, 0.3f, 0.3f, 1.0f } );
			bezls.push_back ( { (GLfloat)cp1.x, (GLfloat)cp1.y,
				(GLfloat)cp1.z, 0.3f, 0.3f, 1.0f } );

			bezls.push_back ( { (GLfloat)p2.x, (GLfloat)p2.y,
				(GLfloat)p2.z, 0.3f, 0.3f, 1.0f } );
			bezls.push_back ( { (GLfloat)cp2.x, (GLfloat)cp2.y,
				(GLfloat)cp2.z, 0.3f, 0.3f, 1.0f } );

			// Create smooth bezier with 100 subdivisions
			for ( int tm = 0; tm < 100; tm++ )
			{
				if (	bez.get_position ( i, tm/100.0, p1 )
					|| bez.get_position(i, (tm+1)/100.0, p2)
					)
				{
					continue;
				}

				bezls.push_back ( {(GLfloat)p1.x, (GLfloat)p1.y,
					(GLfloat)p1.z, 1.0f, 0.5f, 1.0f } );
				bezls.push_back ( {(GLfloat)p2.x, (GLfloat)p2.y,
					(GLfloat)p2.z, 1.0f, 0.5f, 1.0f } );
			}
		}

		m_gl_lines_bez.init ( version );
		m_gl_lines_bez.populate ( bezls.data(), bezls.size() );
	}
}

void TerraScene::render ( TerraCamera const & camera ) const
{
	glEnable ( GL_VERTEX_PROGRAM_POINT_SIZE );
	glEnable ( GL_LINE_WIDTH );
	glEnable ( GL_LINE_SMOOTH );
	glHint ( GL_LINE_SMOOTH_HINT, GL_NICEST );
	glLineWidth ( 1.0f );
	glDisable ( GL_CULL_FACE );
	glEnable ( GL_MULTISAMPLE );	// Antialiasing

	mtGin::GL::Matrix4x4 const & camera_matrix = camera.get_matrix();
	mtGin::GL::Array4x4_float const & camera_data = camera_matrix.data();

	camera.set_view_perspective_3d ();

	glLoadIdentity ();
	glLoadMatrixf ( &camera_data[0][0] );

	glClearColor ( 0.2f, 0.2f, 0.3f, 1.0f );
	glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	glEnable ( GL_DEPTH_TEST );

	// Disable lighting for these lines & shapes
	glDisable ( GL_LIGHTING );
	glDisable ( GL_LIGHT0 );

	render_light ();
	render_axis ();

///	Render the map landscape & lines in the 3D scape

//	glDisable ( GL_MULTISAMPLE );	// Antialiasing


	// Allow lighting for the landscape
	glEnable ( GL_LIGHTING );
	glEnable ( GL_LIGHT0 );
	glLightModeli ( GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE );
	glLightModelf ( GL_LIGHT_MODEL_TWO_SIDE, GLfloat(1.0) );

	mtGin::GL::Matrix4x4 const & proj = camera.get_view_projection ();

	switch ( m_mode )
	{
		case MODE_TRIANGLES:
			m_gl_land_triangles.render ( camera_matrix, proj,
				m_light );
			break;

		case MODE_WIREFRAME:
			m_gl_land_wireframe.render ( camera_matrix, proj );
			break;

		case MODE_POINTS:
			m_gl_land_points.render ( camera_matrix, proj );
			break;

		case MODE_TRIANGLES_WITH_WIREFRAME:
			glEnable ( GL_POLYGON_OFFSET_FILL );
			glPolygonOffset ( 1.0f, 1.0f );
			m_gl_land_triangles.render(camera_matrix, proj, m_light);
			glDisable ( GL_POLYGON_OFFSET_FILL );

			glPolygonMode ( GL_FRONT_AND_BACK, GL_LINE );
			m_gl_land_wireframe.render ( camera_matrix, proj );
			glPolygonMode ( GL_FRONT_AND_BACK, GL_FILL );
			break;
	}

	m_gl_lines.render ( camera_matrix, proj );
	m_gl_lines_bez.render ( camera_matrix, proj );

///	Render the top level HUD
	camera.set_view_perspective_2d ();
	glLoadIdentity ();

	glDisable ( GL_DEPTH_TEST );
	glDisable ( GL_MULTISAMPLE );	// Antialiasing
	glDisable ( GL_LIGHTING );
	glDisable ( GL_LIGHT0 );

	glColor3f ( 1.0f, 1.0f, 1.0f );
	glBegin ( GL_LINES );
	glVertex3f (-1.0f, 0.0f, 0.0f );
	glVertex3f ( 1.0f, 0.0f, 0.0f );
	glEnd ();
	glBegin ( GL_LINES );
	glVertex3f ( 0.0f,-1.0f, 0.0f );
	glVertex3f ( 0.0f, 1.0f, 0.0f );
	glEnd ();
}

void TerraScene::render_pyramid (
	GLfloat	const	x,
	GLfloat	const	y,
	GLfloat	const	z,
	GLfloat	const	size
	)
{
	GLfloat const s = size * GLfloat(1.5);

	glBegin ( GL_LINES );
	glVertex3f( x + size, y + size, z ); glVertex3f( x + size, y - size, z);
	glVertex3f( x + size, y - size, z ); glVertex3f( x - size, y - size, z);
	glVertex3f( x - size, y - size, z ); glVertex3f( x - size, y + size, z);
	glVertex3f( x - size, y + size, z ); glVertex3f( x + size, y + size, z);
	glEnd ();

	glBegin ( GL_LINES );
	glVertex3f ( x + size, y + size, z ); glVertex3f ( x, y, z + s );
	glVertex3f ( x, y, z + s ); glVertex3f ( x - size, y - size, z );
	glEnd ();

	glBegin ( GL_LINES );
	glVertex3f ( x + size, y - size, z ); glVertex3f ( x, y, z + s );
	glVertex3f ( x, y, z + s ); glVertex3f ( x - size, y + size, z );
	glEnd ();
}

void TerraScene::render_light () const
{
	glColor3f ( 1.0f, 1.0f, 0.0f );
	render_pyramid ( GLfloat(m_light.x), GLfloat(m_light.y),
		GLfloat(m_light.z), 100.0f );
}

void TerraScene::render_axis ()
{
	float const s = LANDSCAPE_VIEW_RANGE * 2;

	glColor3f ( 1.0f, 0.0f, 0.0f );
	glBegin ( GL_LINES );
	glVertex3f ( -s, 0, 0 ); glVertex3f ( s, 0, 0);
	glEnd ();

	glColor3f ( 0.0f, 1.0f, 0.0f );
	glBegin ( GL_LINES );
	glVertex3f ( 0, -s, 0 ); glVertex3f ( 0, s, 0);
	glEnd ();

	glColor3f ( 0.0f, 0.0f, 1.0f );
	glBegin ( GL_LINES );
	glVertex3f ( 0, 0, -s ); glVertex3f ( 0, 0, s);
	glEnd ();
}

