//
// WaveFieldEffect.cpp
//
// Wave Field Effect
//
// Copyright (c) 2003 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>

// Demopaja headers
#include "DemopajaVersion.h"
#include "PajaTypes.h"
#include "ClassDescC.h"
#include "ImportableI.h"
#include "OpenGLDeviceC.h"
#include "OpenGLViewportC.h"
#include "ValueFieldPlugin.h"
#include "FileIO.h"
#include "AutoGizmoC.h"
#include "ImportableImageI.h"
#include "glext.h"
#include "Composition.h"

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

using namespace WaveFieldPlugin;


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 );
}



//////////////////////////////////////////////////////////////////////////
//
//  Wave Field effect class descriptor.
//

WaveFieldDescC::WaveFieldDescC()
{
	// empty
}

WaveFieldDescC::~WaveFieldDescC()
{
	// empty
}

void*
WaveFieldDescC::create()
{
	return (void*)WaveFieldEffectC::create_new();
}

int32
WaveFieldDescC::get_classtype() const
{
	return CLASS_TYPE_EFFECT;
}

SuperClassIdC
WaveFieldDescC::get_super_class_id() const
{
	return SUPERCLASS_EFFECT_VALUEFIELD;
}

ClassIdC
WaveFieldDescC::get_class_id() const
{
	return CLASS_WAVEFIELD_EFFECT;
}

const char*
WaveFieldDescC::get_name() const
{
	return "Field Wave";
}

const char*
WaveFieldDescC::get_desc() const
{
	return "Walue Field Wave Effect";
}

const char*
WaveFieldDescC::get_author_name() const
{
	return "Mikko \"memon\" Mononen";
}

const char*
WaveFieldDescC::get_copyright_message() const
{
	return "Copyright (c) 2003 Moppi Productions";
}

const char*
WaveFieldDescC::get_url() const
{
	return "http://www.demopaja.org";
}

const char*
WaveFieldDescC::get_help_filename() const
{
	return "res://WaveFieldHelp.html";
}

uint32
WaveFieldDescC::get_required_device_driver_count() const
{
	return 1;
}

const ClassIdC&
WaveFieldDescC::get_required_device_driver( uint32 ui32Idx )
{
	return PajaSystem::CLASS_OPENGL_DEVICEDRIVER;
}


uint32
WaveFieldDescC::get_ext_count() const
{
	return 0;
}

const char*
WaveFieldDescC::get_ext( uint32 ui32Index ) const
{
	return 0;
}


//////////////////////////////////////////////////////////////////////////
//
// Global descriptors, which will be returned to the Demopaja.
//

WaveFieldDescC	g_rWaveFieldDesc;

//////////////////////////////////////////////////////////////////////////
//
// The GBlur effect
//

WaveFieldEffectC::WaveFieldEffectC()
{
	//
	// Create Transform gizmo.
	//
	m_pTraGizmo = AutoGizmoC::create_new( this, "Transform", ID_GIZMO_TRANS );

	m_pTraGizmo->add_parameter(	ParamVector2C::create_new( m_pTraGizmo, "Position", Vector2C(), ID_TRANSFORM_POS,
						PARAM_STYLE_EDITBOX | PARAM_STYLE_ABS_POSITION, PARAM_ANIMATABLE ) );
	
	m_pTraGizmo->add_parameter(	ParamVector2C::create_new( m_pTraGizmo, "Pivot", Vector2C(), ID_TRANSFORM_PIVOT,
						PARAM_STYLE_EDITBOX | PARAM_STYLE_REL_POSITION | PARAM_STYLE_WORLD_SPACE, PARAM_ANIMATABLE ) );
	
	m_pTraGizmo->add_parameter(	ParamFloatC::create_new( m_pTraGizmo, "Rotation", 0, ID_TRANSFORM_ROT,
						PARAM_STYLE_EDITBOX | PARAM_STYLE_ANGLE, PARAM_ANIMATABLE, 0, 0, 1.0f ) );

	m_pTraGizmo->add_parameter(	ParamVector2C::create_new( m_pTraGizmo, "Scale", Vector2C( 1, 1 ), ID_TRANSFORM_SCALE,
						PARAM_STYLE_EDITBOX | PARAM_STYLE_PERCENT, PARAM_ANIMATABLE, Vector2C(), Vector2C(), 0.01f ) );


	//
	// Create Attributes gizmo.
	//

	m_pAttGizmo = AutoGizmoC::create_new( this, "Attributes", ID_GIZMO_ATTRIB );

/*
	// Image
	m_pAttGizmo->add_parameter(	ParamFileC::create_new( m_pAttGizmo, "Image", SUPERCLASS_IMAGE, NULL_CLASSID, ID_ATTRIBUTE_IMAGE, PARAM_STYLE_FILE, PARAM_ANIMATABLE ) );

	// Scale
	m_pAttGizmo->add_parameter(	ParamVector2C::create_new( m_pAttGizmo, "Scale", Vector2C( 1, 1 ), ID_ATTRIBUTE_SCALE,
						PARAM_STYLE_EDITBOX | PARAM_STYLE_PERCENT, PARAM_ANIMATABLE, Vector2C(), Vector2C(), 0.01f ) );

	// Offset
	m_pAttGizmo->add_parameter(	ParamVector2C::create_new( m_pAttGizmo, "Offset", Vector2C(), ID_ATTRIBUTE_OFFSET,
						PARAM_STYLE_EDITBOX | PARAM_STYLE_REL_POSITION | PARAM_STYLE_OBJECT_SPACE, PARAM_ANIMATABLE ) );

	// Wrap
	ParamIntC*	pWrapMode = ParamIntC::create_new( m_pAttGizmo, "Wrap", 0, ID_ATTRIBUTE_WRAP, PARAM_STYLE_COMBOBOX, PARAM_ANIMATABLE, 0, 1 );
	pWrapMode->add_label( 0, "Clamp" );
	pWrapMode->add_label( 1, "Repeat" );
	m_pAttGizmo->add_parameter( pWrapMode );

	// Resample
	ParamIntC*	pResampleMode = ParamIntC::create_new( m_pAttGizmo, "Resample", 0, ID_ATTRIBUTE_RESAMPLE, PARAM_STYLE_COMBOBOX, PARAM_ANIMATABLE, 0, 1 );
	pResampleMode->add_label( 0, "Bilinear" );
	pResampleMode->add_label( 1, "Nearest" );
	m_pAttGizmo->add_parameter( pResampleMode );
*/
}

WaveFieldEffectC::WaveFieldEffectC( EditableI* pOriginal ) :
	EffectValueFieldI( pOriginal )
{
	// empty
}

WaveFieldEffectC::~WaveFieldEffectC()
{
	// Return if this is a clone.
	if( get_original() )
		return;

	// Release gizmos.
	m_pTraGizmo->release();
	m_pAttGizmo->release();
}

WaveFieldEffectC*
WaveFieldEffectC::create_new()
{
	return new WaveFieldEffectC;
}

DataBlockI*
WaveFieldEffectC::create()
{
	return new WaveFieldEffectC;
}

DataBlockI*
WaveFieldEffectC::create( EditableI* pOriginal )
{
	return new WaveFieldEffectC( pOriginal );
}

void
WaveFieldEffectC::copy( EditableI* pEditable )
{
	EffectI::copy( pEditable );

	// Make deep copy.
	WaveFieldEffectC*	pEffect = (WaveFieldEffectC*)pEditable;
	m_pTraGizmo->copy( pEffect->m_pTraGizmo );
	m_pAttGizmo->copy( pEffect->m_pAttGizmo );
}

void
WaveFieldEffectC::restore( EditableI* pEditable )
{
	EffectI::restore( pEditable );

	// Make shallow copy.
	WaveFieldEffectC*	pEffect = (WaveFieldEffectC*)pEditable;
	m_pTraGizmo = pEffect->m_pTraGizmo;
	m_pAttGizmo = pEffect->m_pAttGizmo;
}

int32
WaveFieldEffectC::get_gizmo_count()
{
	// Return number of gizmos inside this effect.
	return GIZMO_COUNT;
}

GizmoI*
WaveFieldEffectC::get_gizmo( PajaTypes::int32 i32Index )
{
	// Returns specified gizmo.
	// Since the ID's are zero based, we can use them as indices.
	switch( i32Index ) {
	case ID_GIZMO_TRANS:
		return m_pTraGizmo;
	case ID_GIZMO_ATTRIB:
		return m_pAttGizmo;
	}

	return 0;
}

ClassIdC
WaveFieldEffectC::get_class_id()
{
	// Return the class ID. Should be same as in the class descriptor.
	return CLASS_WAVEFIELD_EFFECT;
}

const char*
WaveFieldEffectC::get_class_name()
{
	// Return the class name. Should be same as in the class descriptor.
	return "Field Wave";
}

void
WaveFieldEffectC::set_default_file( int32 i32Time, FileHandleC* pHandle )
{
	// Empty
}

ParamI*
WaveFieldEffectC::get_default_param( int32 i32Param )
{
	// Return specified default parameter.
	if( i32Param == DEFAULT_PARAM_POSITION )
		return m_pTraGizmo->get_parameter( ID_TRANSFORM_POS );
	else if( i32Param == DEFAULT_PARAM_ROTATION )
		return m_pTraGizmo->get_parameter( ID_TRANSFORM_ROT );
	else if( i32Param == DEFAULT_PARAM_SCALE )
		return m_pTraGizmo->get_parameter( ID_TRANSFORM_SCALE );
	else if( i32Param == DEFAULT_PARAM_PIVOT )
		return m_pTraGizmo->get_parameter( ID_TRANSFORM_PIVOT );
	return 0;
}

void
WaveFieldEffectC::initialize( uint32 ui32Reason, DemoInterfaceC* pInterface )
{
	EffectI::initialize( ui32Reason, pInterface );
}


void
WaveFieldEffectC::eval_state( int32 i32Time )
{
	if( !m_pDemoInterface )
		return;
/*
	DeviceContextC* pContext = m_pDemoInterface->get_device_context();
	TimeContextC* pTimeContext = m_pDemoInterface->get_time_context();


	Matrix2C	rPosMat, rRotMat, rScaleMat, rPivotMat;

	Vector2C	rScale;
	Vector2C	rPos;
	Vector2C	rPivot;
	float32		f32Rot;

	// Get parameters which affect the transformation.
	((ParamVector2C*)m_pTraGizmo->get_parameter( ID_TRANSFORM_POS ))->get_val( i32Time, rPos );
	((ParamVector2C*)m_pTraGizmo->get_parameter( ID_TRANSFORM_PIVOT ))->get_val( i32Time, rPivot );
	((ParamFloatC*)m_pTraGizmo->get_parameter( ID_TRANSFORM_ROT ))->get_val( i32Time, f32Rot );
	((ParamVector2C*)m_pTraGizmo->get_parameter( ID_TRANSFORM_SCALE ))->get_val( i32Time, rScale );

	// Calculate transformation matrix.
	rPivotMat.set_trans( rPivot );
	rPosMat.set_trans( rPos );
	rRotMat.set_rot( f32Rot / 180.0f * (float32)M_PI );
	rScaleMat.set_scale( rScale ) ;
	m_rTM = rPivotMat * rRotMat * rScaleMat * rPosMat;

	float32		f32Width = 25;
	float32		f32Height = 25;
	Vector2C	rMin, rMax;
	Vector2C	rVec;


	// Get the size from the file or use the defaults if no file.
	FileHandleC*		pHandle = 0;
	int32				i32FileTime = 0;
	((ParamFileC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_IMAGE ))->get_file( i32Time, pHandle, i32FileTime );

	if( pHandle )
		m_pMaskImp = (ImportableImageI*)pHandle->get_importable();

	if( m_pMaskImp ) {
		m_pMaskImp->eval_state( i32FileTime );
		f32Width = (float32)m_pMaskImp->get_width() * 0.5f;
		f32Height = (float32)m_pMaskImp->get_height() * 0.5f;
	}

	m_i32FileTime = i32FileTime;

	// Calcualte vertices of the rectangle.
	m_rVertices[0][0] = -f32Width;		// top-left
	m_rVertices[0][1] = -f32Height;

	m_rVertices[1][0] =  f32Width;		// top-right
	m_rVertices[1][1] = -f32Height;

	m_rVertices[2][0] =  f32Width;		// bottom-right
	m_rVertices[2][1] =  f32Height;

	m_rVertices[3][0] = -f32Width;		// bottom-left
	m_rVertices[3][1] =  f32Height;

	// Calculate bounding box
	for( uint32 i = 0; i < 4; i++ ) {
		rVec = m_rTM * m_rVertices[i];
		m_rVertices[i] = rVec;

		if( !i )
			rMin = rMax = rVec;
		else {
			if( rVec[0] < rMin[0] ) rMin[0] = rVec[0];
			if( rVec[1] < rMin[1] ) rMin[1] = rVec[1];
			if( rVec[0] > rMax[0] ) rMax[0] = rVec[0];
			if( rVec[1] > rMax[1] ) rMax[1] = rVec[1];
		}
	}

	// Store bounding box.
	m_rBBox[0] = rMin;
	m_rBBox[1] = rMax;

	Vector2C	rImageScale( 1, 1 );
	Vector2C	rImageOffset( 0, 0 );
	int32			i32Wrap = WRAPMODE_CLAMP;
	int32			i32Resample = RESAMPLEMODE_BILINEAR;
	ColorC		rColor( 1, 1, 1 );
	float32		f32Opacity = 1.0f;


	// Get image scale.
	((ParamVector2C*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_SCALE ))->get_val( i32Time, rImageScale );
	// Get image offset.
	((ParamVector2C*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_OFFSET ))->get_val( i32Time, rImageOffset );
	// Get Wrap
	((ParamIntC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_WRAP ))->get_val( i32Time, i32Wrap );
	// Get Resample
	((ParamIntC*)m_pAttGizmo->get_parameter( ID_ATTRIBUTE_RESAMPLE ))->get_val( i32Time, i32Resample );

	rImageOffset[0] /= f32Width * 2.0f;
	rImageOffset[1] /= f32Height * 2.0f;

	// Calculate texture matrix
	Vector2C	rBase = m_rVertices[0];
	Vector2C	rAxisX = m_rVertices[1] - m_rVertices[0];
	Vector2C	rAxisY = m_rVertices[2] - m_rVertices[1];

	Matrix2C	rBaseMat;
	rBaseMat[0] = rAxisX;
	rBaseMat[1] = rAxisY;
	rBaseMat[2] = rBase;

	Matrix2C	rTexMat, rOffsetTexMat, rScaleTexMat, rOffsetBackTexMat;
	rOffsetTexMat.set_trans( Vector2C( -0.5f, -0.5f ) - rImageOffset );
	rScaleTexMat.set_scale( rImageScale );
	rOffsetBackTexMat.set_trans( Vector2C( 0.5f, 0.5f ) );

	m_rTextureTM = rBaseMat.invert() * rOffsetTexMat * rScaleTexMat * rOffsetBackTexMat;


	m_i32MaskImageFlags = 0;	
	
	if( i32Wrap == WRAPMODE_CLAMP )
		m_i32MaskImageFlags |= IMAGE_CLAMP;
	else
		m_i32MaskImageFlags |= IMAGE_WRAP;

	if( i32Resample == RESAMPLEMODE_BILINEAR )
		m_i32MaskImageFlags |= IMAGE_LINEAR;
	else
		m_i32MaskImageFlags |= IMAGE_NEAREST;*/
}

void
WaveFieldEffectC::draw_ui( uint32 ui32Flags )
{
	int	i;

/*	if( get_flags() & ITEM_SELECTED )
	{
		// Draw mask
		if( !m_pDemoInterface )
			return;

		if( !m_pMaskImp )
			return;

		DeviceContextC* pContext = m_pDemoInterface->get_device_context();
		TimeContextC* pTimeContext = m_pDemoInterface->get_time_context();
		OpenGLDeviceC*	pDevice = (OpenGLDeviceC*)pContext->query_interface( CLASS_OPENGL_DEVICEDRIVER );
		if( !pDevice )
			return;

		m_pMaskImp->eval_state( m_i32FileTime );

		glEnable( GL_BLEND );

		glEnable( GL_TEXTURE_2D );
		m_pMaskImp->bind_texture( pDevice, m_i32MaskImageFlags );

		// Pass color from previous texture stage
		glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT );
		glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_REPLACE_EXT );
		glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT );
		glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR );

		// Modulate alpha
		glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_MODULATE );
		glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_TEXTURE );
		glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_EXT, GL_ONE_MINUS_SRC_ALPHA );
		glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_EXT, GL_PREVIOUS_EXT );
		glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_EXT, GL_SRC_ALPHA );


		// Normal
		glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

		// Set color and opacity
		glColor4ub( 240, 6, 0, 64 );

		// Default texture coordinates
		Vector2C	rMaskTexCoords[4];

		// Transform texture coords
		for( i = 0; i < 4; i++ )
		{
			// Image Tex coords
			rMaskTexCoords[i] = m_rVertices[i] * m_rTextureTM;
		}

		// Draw rectangle.
		glBegin( GL_QUADS );

		glTexCoord2f( rMaskTexCoords[0][0], rMaskTexCoords[0][1] );
		glVertex2f( m_rVertices[0][0], m_rVertices[0][1] );
		
		glTexCoord2f( rMaskTexCoords[1][0], rMaskTexCoords[1][1] );
		glVertex2f( m_rVertices[1][0], m_rVertices[1][1] );
		
		glTexCoord2f( rMaskTexCoords[2][0], rMaskTexCoords[2][1] );
		glVertex2f( m_rVertices[2][0], m_rVertices[2][1] );

		glTexCoord2f( rMaskTexCoords[3][0], rMaskTexCoords[3][1] );
		glVertex2f( m_rVertices[3][0], m_rVertices[3][1] );

		glEnd();

		glDisable( GL_TEXTURE_2D );
		glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
		glDisable( GL_BLEND );

	}*/
}

BBox2C
WaveFieldEffectC::get_bbox()
{
	// Return the bounding box.
	return m_rBBox;
}

const Matrix2C&
WaveFieldEffectC::get_transform_matrix() const
{
	// Return the trnasformation matrix.
	return m_rTM;
}

bool
WaveFieldEffectC::hit_test( const Vector2C& rPoint )
{
	// Point in polygon test.
	// from c.g.a FAQ
	int		i, j;
	bool	bInside = false;

	for( i = 0, j = 4 - 1; i < 4; j = i++ ) {
		if( ( ((m_rVertices[i][1] <= rPoint[1]) && (rPoint[1] < m_rVertices[j][1])) ||
			((m_rVertices[j][1] <= rPoint[1]) && (rPoint[1] < m_rVertices[i][1])) ) &&
			(rPoint[0] < (m_rVertices[j][0] - m_rVertices[i][0]) * (rPoint[1] - m_rVertices[i][1]) / (m_rVertices[j][1] - m_rVertices[i][1]) + m_rVertices[i][0]) )

			bInside = !bInside;
	}

	return bInside;
}

float
WaveFieldEffectC::sample_value_float( const PajaTypes::Vector2C& rSamplePos, const PajaTypes::Matrix2C& rTM )
{
	return 0;
}

Vector2C
WaveFieldEffectC::sample_value_vector2( const PajaTypes::Vector2C& rSamplePos, const PajaTypes::Matrix2C& rTM )
{
	return Vector2C();
}

void
WaveFieldEffectC::sample_value_array_float( const PajaTypes::Vector2C* pSamplePosArray, float* pOutArray, PajaTypes::uint32 ui32NumSamples, const PajaTypes::Matrix2C& rTM )
{
}

void
WaveFieldEffectC::sample_value_array_vector2( const PajaTypes::Vector2C* pSamplePosArray, PajaTypes::Vector2C* pOutArray, PajaTypes::uint32 ui32NumSamples, const PajaTypes::Matrix2C& rTM )
{
}


enum WaveFieldEffectChunksE {
	CHUNK_WAVEFIELD_BASE =			0x1000,
	CHUNK_WAVEFIELD_TRANSGIZMO =		0x2000,
	CHUNK_WAVEFIELD_ATTRIBGIZMO =	0x3000,
};

const uint32	WAVEFIELD_VERSION = 1;

uint32
WaveFieldEffectC::save( SaveC* pSave )
{
	uint32	ui32Error = IO_OK;

	// EffectI base class
	pSave->begin_chunk( CHUNK_WAVEFIELD_BASE, WAVEFIELD_VERSION );
		ui32Error = EffectI::save( pSave );
	pSave->end_chunk();

	// Transform
	pSave->begin_chunk( CHUNK_WAVEFIELD_TRANSGIZMO, WAVEFIELD_VERSION );
		ui32Error = m_pTraGizmo->save( pSave );
	pSave->end_chunk();

	// Attribute
	pSave->begin_chunk( CHUNK_WAVEFIELD_ATTRIBGIZMO, WAVEFIELD_VERSION );
		ui32Error = m_pAttGizmo->save( pSave );
	pSave->end_chunk();

	return ui32Error;
}

uint32
WaveFieldEffectC::load( LoadC* pLoad )
{
	uint32	ui32Error = IO_OK;

	while( (ui32Error = pLoad->open_chunk()) == IO_OK ) {

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_WAVEFIELD_BASE:
			// EffectI base class
			if( pLoad->get_chunk_version() == WAVEFIELD_VERSION )
				ui32Error = EffectI::load( pLoad );
			break;

		case CHUNK_WAVEFIELD_TRANSGIZMO:
			// Transform
			if( pLoad->get_chunk_version() == WAVEFIELD_VERSION )
				ui32Error = m_pTraGizmo->load( pLoad );
			break;

		case CHUNK_WAVEFIELD_ATTRIBGIZMO:
			// Attribute
			if( pLoad->get_chunk_version() == WAVEFIELD_VERSION )
				ui32Error = m_pAttGizmo->load( pLoad );
			break;

		default:
			assert( 0 );
		}

		pLoad->close_chunk();

		if( ui32Error != IO_OK && ui32Error != IO_END )
			return ui32Error;
	}

	return ui32Error;
}
