#include "externals.hh"
#include "bloom.hh"
#include "profiling.hh"
#include "globals.hh"

namespace {
	static const std::string Name_( "BLOOOOM!" );
}

#define PSTART() T_Profiler::Profiler.start( Name_ )
#define PEND() do { glFinish( ); T_Profiler::Profiler.end( Name_ ); } while ( 0 )


T_BloomPass::T_BloomPass(
		__rw__ T_Texture& input )
	: input_( input ) ,
		spHighpass_( GL_FRAGMENT_SHADER ) ,
		spDownsample_( GL_FRAGMENT_SHADER ) ,
		spBlur_( GL_FRAGMENT_SHADER ) ,
		//
		txBlur0_( input.width( ) , input.height( ) , E_TexType::RGB16F , BloomLevels ) ,
		txBlur1_( input.width( ) , input.height( ) , E_TexType::RGB16F , BloomLevels ) ,
		//
		filterParams_{ 1.6 , 1.2 , .95 } ,
		blurWeights_{ 0.324 , 0.232 , 0.0855 , 0.0205 } ,
		blurSize_{ 1.3 }
{
	txBlur0_.wrap( E_TexWrap::CLAMP_EDGE ).samplingMode( E_TexSampling::LINEAR );
	txBlur1_.wrap( E_TexWrap::CLAMP_EDGE ).samplingMode( E_TexSampling::LINEAR );

	for ( auto i = 0u ; i < BloomLevels ; i ++ ) {
		rtBlur0_.push_back( T_RendertargetSetup( ).add( txBlur0_ , i ).create( ) );
		rtBlur1_.push_back( T_RendertargetSetup( ).add( txBlur1_ , i ).create( ) );
	}

	spHighpass_.addFile( "bloom-highpass.glsl" );
	spDownsample_.addFile( "downsample.glsl" );
	spBlur_.addFile( "blur-pass.glsl" );

	spHighpass_.load( );
	spDownsample_.load( );
	spBlur_.load( );
}

void T_BloomPass::render( )
{
	PSTART( );

	auto& tm( Globals::Textures( ) );
	if ( spHighpass_.activate( ) && rtBlur0_[ 0 ].activate( ) ) {
		enum {
			U_TEXTURE		= 0 ,
			U_LOD			= 1 ,
			U_INPUT_SIZE		= 2 ,
			U_PARAMS		= 3 ,
		};

		tm.bind( 0 , input_ );

		glUniform1i( U_TEXTURE , 0 );
		glUniform1i( U_LOD , 0 );
		glUniform2f( U_INPUT_SIZE ,
				input_.width( ) ,
				input_.height( ) );
		glUniform3fv( U_PARAMS , 1 , filterParams_ );
		glRectf( -1, -1 , 1 , 1 );
	}
	GL_ASSERT( );

	enum {
		U_TEXTURE		= 0 ,
		U_OUTPUT_SIZE		= 1 ,
		U_SOURCE_LOD		= 2 ,
		U_DIRECTION		= 3 ,
		U_WEIGHTS		= 4 ,
	};
	for ( auto i = 0u ; i < BloomLevels ; i ++ ) {
		if ( i > 0 ) {
			spDownsample_.activate( );
			rtBlur0_[ i ].activate( );

			tm.bind( 0 , txBlur0_ );

			glUniform1i( 0 , 0 );
			glUniform2f( 1 , txBlur0_.width( ) >> i ,
					txBlur0_.height( ) >> i );
			glUniform1i( 2 , i - 1 );

			glRectf( -1 , -1 , 1 , 1 );
		}

		if ( !spBlur_.activate( ) ) {
			break;
		}
		glUniform4fv( U_WEIGHTS , 1 , blurWeights_ );
		glUniform2f( U_OUTPUT_SIZE , rtBlur0_[ i ].width( ) ,
				rtBlur0_[ i ].height( ) );
		glUniform1i( U_SOURCE_LOD , i );
		glUniform1i( U_TEXTURE , 0 );

		rtBlur1_[ i ].activate( );
		glUniform2f( U_DIRECTION , blurSize_ , 0 );
		tm.bind( 0 , txBlur0_ );
		glRectf( -1 , -1 , 1 , 1 );

		rtBlur0_[ i ].activate( );
		glUniform2f( U_DIRECTION , 0 , blurSize_ );
		tm.bind( 0 , txBlur1_ );
		glRectf( -1 , -1 , 1 , 1 );
	}

	PEND( );
}

void T_BloomPass::makeUI( )
{
	if ( !ImGui::CollapsingHeader( "Bloom" ) ) {
		return;
	}

	ImGui::DragFloat3( "Filter" , filterParams_ , .01f , 0.1 , 10 );
	ImGui::DragFloat4( "Weights" , blurWeights_ , .001f , 0. , 10 );
	ImGui::DragFloat( "Blur size" , &blurSize_ , .001f , 0. , 10 );
}