//
// GlarePlugin.cpp
//
// Glare Plugin
//
// Copyright (c) 2000 memon/moppi productions
//

//#define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
#include <windows.h>
#include <gl\gl.h>
#include <gl\glu.h>
#include <stdio.h>
#include "glext.h"

#include "extgl.h"

// Demopaja headers
#include "DemopajaVersion.h"
#include "PajaTypes.h"
#include "ClassDescC.h"
#include "ImportableI.h"
#include "OpenGLDeviceC.h"
#include "OpenGLViewportC.h"
#include "GlarePlugin.h"
#include "FileIO.h"
#include "AutoGizmoC.h"
#include "ImportableImageI.h"
#include "PBufferC.h"

using namespace PajaTypes;
using namespace PluginClass;
using namespace PajaSystem;
using namespace Edit;
using namespace Composition;
using namespace Import;
using namespace FileIO;

using namespace DepthBlurPlugin;
using namespace GBlurPlugin;


static
void
TRACE( const char* szFormat, ...  )
{
	char	szMsg[256];
	va_list	rList;
	va_start( rList, szFormat );
	_vsnprintf( szMsg, 255, szFormat, rList );
	va_end( rList );
	OutputDebugString( szMsg );
}

inline
uint32
lowest_bit_mask( uint32 v )
{
	return (v & -v);
}

static
uint32
ceil_power2( uint32 ui32Num )
{
	uint32	i = lowest_bit_mask( ui32Num );
	while( i < ui32Num )
		i <<= 1;
	return i;
}


//
// Global sampling pbuffer handling
//

PBufferC*		EffectBlurI::m_pPBuffer = 0;
int32				EffectBlurI::m_i32PBufferRefCount = 0;
uint32			EffectBlurI::m_ui32BlurTexId[BLUR_TEX_COUNT] = { 0, 0, 0, 0, 0 };
uint32			EffectBlurI::m_ui32TexId = 0;
uint32			EffectBlurI::m_ui32TexWidth = 0;
uint32			EffectBlurI::m_ui32TexHeight = 0;
uint32			EffectBlurI::m_ui32WhiteTextureID = 0;
uint32			EffectBlurI::m_ui32BlackTextureID = 0;

EffectBlurI::EffectBlurI() :
	m_bTextureRef( false )
{
	// empty
}


EffectBlurI::EffectBlurI( Edit::EditableI* pOriginal ) :
	EffectI( pOriginal ),
	m_bTextureRef( false )
{
	// empty
}

EffectBlurI::~EffectBlurI()
{
	if( m_bTextureRef )
		release_resources();
}

void
EffectBlurI::init_resources( int32 i32Width, int32 i32Height )
{
	m_bTextureRef = true;

	if( !m_i32PBufferRefCount )
	{
		if( !m_pPBuffer ) {
			m_pPBuffer = new PBufferC;
		}

		if( !m_ui32WhiteTextureID )
		{
			uint8	ui8WhiteTextureData[(4 * 4) * 3] = {
				255, 255, 255,	255, 255, 255,	255, 255, 255,	255, 255, 255,
				255, 255, 255,	255, 255, 255,	255, 255, 255,	255, 255, 255,
				255, 255, 255,	255, 255, 255,	255, 255, 255,	255, 255, 255,
				255, 255, 255,	255, 255, 255,	255, 255, 255,	255, 255, 255,
			};

			// Create textures
			glGenTextures( 1, &m_ui32WhiteTextureID );
			glBindTexture( GL_TEXTURE_2D, m_ui32WhiteTextureID );
			glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
			glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, 4, 4, 0, GL_RGB, GL_UNSIGNED_BYTE, ui8WhiteTextureData );
		}

		if( !m_ui32BlackTextureID )
		{
			uint8	ui8BlackTextureData[(4 * 4) * 3] = {
				0, 0, 0,	0, 0, 0,	0, 0, 0,	0, 0, 0,
				0, 0, 0,	0, 0, 0,	0, 0, 0,	0, 0, 0,
				0, 0, 0,	0, 0, 0,	0, 0, 0,	0, 0, 0,
				0, 0, 0,	0, 0, 0,	0, 0, 0,	0, 0, 0,
			};

			glGenTextures( 1, &m_ui32BlackTextureID );
			glBindTexture( GL_TEXTURE_2D, m_ui32BlackTextureID );
			glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
			glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, 4, 4, 0, GL_RGB, GL_UNSIGNED_BYTE, ui8BlackTextureData );
		}
	}

	check_blur_buffer_size( i32Width, i32Height );

	m_i32PBufferRefCount++;
}

void
EffectBlurI::check_blur_buffer_size( int32 i32Width, int32 i32Height )
{
	int32	i;

	// get image from frame buffer
	int32	i32WidthPow2 = ceil_power2( i32Width );
	int32	i32HeightPow2 = ceil_power2( i32Height );

	// Resize pbfueer on demand
	if( m_pPBuffer ) {
		int32	i32PBufWidth = i32WidthPow2 / 2;
		int32	i32PBufHeight = i32HeightPow2 / 2;
		if( !m_pPBuffer->is_initialised() || i32PBufWidth > m_pPBuffer->get_width() || i32PBufHeight > m_pPBuffer->get_height() ) {
			TRACE( "GBlur: pbuffer->init( %d, %d ) [old %dx%d]\n", i32PBufWidth, i32PBufHeight, m_pPBuffer->get_width(), m_pPBuffer->get_height() );
			m_pPBuffer->init( i32PBufWidth, i32PBufHeight, PBUFFER_TEXTURE_NONE );
		}
	}

	// Resize texture

	// If the maximum image size has changed, invalidate texture object if necessary
	if( i32WidthPow2 > (int32)m_ui32TexWidth || i32HeightPow2 > (int32)m_ui32TexHeight ) {

		TRACE( "GLARE tex size changed:\n" );
		TRACE( " - old: %d x %d\n", m_ui32TexWidth, m_ui32TexHeight );
		TRACE( " - new: %d x %d\n", i32WidthPow2, i32HeightPow2 );

		if( m_ui32TexId )
			glDeleteTextures( 1, &m_ui32TexId );
		m_ui32TexId = 0;

		for( i = 0; i < BLUR_TEX_COUNT; i++ ) {
			if( m_ui32BlurTexId[i] )
				glDeleteTextures( 1, &m_ui32BlurTexId[i] );
			m_ui32BlurTexId[i] = 0;
		}
	}

	if( !m_ui32TexId ) {
		glGenTextures( 1, &m_ui32TexId );
		glBindTexture( GL_TEXTURE_2D, m_ui32TexId );
		m_ui32TexWidth = i32WidthPow2;
		m_ui32TexHeight = i32HeightPow2;

		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
		glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );

		glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, m_ui32TexWidth, m_ui32TexHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0 );

		glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
	}


	for( int32 i = 0; i < BLUR_TEX_COUNT; i++ ) {
		if( !m_ui32BlurTexId[i] ) {
			glGenTextures( 1, &m_ui32BlurTexId[i] );
			glBindTexture( GL_TEXTURE_2D, m_ui32BlurTexId[i] );
			int32	i32BlurTexWidth = (int32)m_ui32TexWidth >> (i + 1);
			int32	i32BlurTexHeight = (int32)m_ui32TexHeight >> (i + 1);
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
			glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, i32BlurTexWidth, i32BlurTexHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0 );
			glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
		}
	}

}

void
EffectBlurI::release_resources()
{
	m_i32PBufferRefCount--;

	if( m_i32PBufferRefCount == 0 ) {

		delete m_pPBuffer;
		m_pPBuffer = 0;

		// Delete textures
		if( m_ui32WhiteTextureID )
		{
			glDeleteTextures( 1, &m_ui32WhiteTextureID );
			m_ui32WhiteTextureID = 0;
		}
		if( m_ui32BlackTextureID )
		{
			glDeleteTextures( 1, &m_ui32BlackTextureID );
			m_ui32BlackTextureID = 0;
		}

		if( m_ui32TexId ) {
			glDeleteTextures( 1, &m_ui32TexId );
			m_ui32TexId = 0;
		}

		for( int32 i = 0; i < BLUR_TEX_COUNT; i++ ) {
			if( m_ui32BlurTexId[i] ) {
				glDeleteTextures( 1, &m_ui32BlurTexId[i] );
				m_ui32BlurTexId[i] = 0;
			}
		}

	}

}



#ifndef PAJAPLAYER

//////////////////////////////////////////////////////////////////////////
//
// The DLL
//

BOOL APIENTRY
DllMain( HANDLE hModule, DWORD ulReasonForCall, LPVOID lpReserved )
{
    switch( ulReasonForCall )
	{
		case DLL_PROCESS_ATTACH:
		case DLL_THREAD_ATTACH:
		case DLL_THREAD_DETACH:
		case DLL_PROCESS_DETACH:
			break;
    }
    return TRUE;
}


//
// Returns number of classes inside this plugin DLL.
//

__declspec( dllexport )
int32
get_classdesc_count()
{
	return 2; //3;
}


//
// Returns class descriptors of the plugin classes.
//

__declspec( dllexport )
ClassDescC*
get_classdesc( int32 i )
{
	if( i == 0 )
		return &g_rGBlurDesc;
	else if( i == 1 )
		return &g_rDepthBlurDesc;
/*	else if( i == 2 )
		return &g_rEnchangeDesc;*/
	return 0;
}


//
// Returns the API version this DLL was made with.
//

__declspec( dllexport )
int32
get_api_version()
{
	return DEMOPAJA_VERSION;
}

//
// Returns the DLL name.
//

__declspec( dllexport )
char*
get_dll_name()
{
	return "GlarePlugin.dll - Glare Effect plugin (c)2000 memon/moppi productions";
}

#endif
