/*
 *			GPAC - MPEG-4 Systems C Development Kit
 *
 *			Copyright (c) Jean Le Feuvre 2000-2005
 *					All rights reserved
 *
 *  This file is part of GPAC / 3D rendering plugin
 *
 *  GPAC 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 2, or (at your option)
 *  any later version.
 *   
 *  GPAC 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 GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *
 */



#include "render3d_nodes.h"

typedef struct
{
	SFNode *owner;
	struct scene_renderer *compositor;
	M4Mesh *mesh;
	Bool (*IntersectWithRay)(SFNode *owner, M4Ray *ray, SFVec3f *outPoint, SFVec3f *outNormal, SFVec2f *outTexCoords);
	Bool (*ClosestFace)(SFNode *owner, SFVec3f user_pos, Fixed min_dist, SFVec3f *outPoint);
	SFVec2f size;
} BitmapStack;


static Bool BitmapIntersectWithRay(SFNode *owner, M4Ray *ray, SFVec3f *outPoint, SFVec3f *outNormal, SFVec2f *outTexCoords)
{
	Bool inside;
	BitmapStack *st = (BitmapStack *)Node_GetPrivate(owner);

	if (!R3D_Get2DPlaneIntersection(ray, outPoint)) return 0;

	/*note this is broken when a single bitmap is used with # texture*/
	inside = ( (outPoint->x >= -st->size.x/2) && (outPoint->y >= -st->size.y/2) 
			&& (outPoint->x <= st->size.x/2) && (outPoint->y <= st->size.y/2) 
			) ? 1 : 0;

	if (!inside) return 0;

	if (outNormal) { outNormal->x = outNormal->y = 0; outNormal->z = 1; }
	if (outTexCoords) { 
		outTexCoords->x = m4_divfix(outPoint->x, st->size.x) + FIX_ONE/2;
		outTexCoords->y = m4_divfix(outPoint->y, st->size.y) + FIX_ONE/2;
	}
	return 1;
}


static void RenderBitmap(SFNode *node, void *rs)
{
	Fixed x, y;
	Aspect2D asp;
	char *data;
	u32 format;
	TextureHandler *txh;
	Fixed sx, sy;
	SFVec2f size;
	BitmapStack *st = (BitmapStack *)Node_GetPrivate(node);
	RenderEffect3D *eff = (RenderEffect3D *)rs;
	M_Bitmap *bmp = (M_Bitmap *)st->owner;
	Render3D *sr = (Render3D*)st->compositor->visual_renderer->user_priv;

	if (!eff->appear) return;
	if (! ((M_Appearance *)eff->appear)->texture) return;
	txh = R3D_GetTextureHandler(((M_Appearance *)eff->appear)->texture);
	if (!txh || !txh->hwtx || !txh->width || !txh->height) return;
	
	sx = bmp->scale.x; if (sx<0) sx = FIX_ONE;
	sy = bmp->scale.y; if (sy<0) sy = FIX_ONE;

	R3D_CompositeAdjustScale(txh->owner, &sx, &sy);

	/*check size change*/
	size.x = txh->width*sx;
	size.y = txh->height*sy;


	/*we're in meter metrics*/
	if (!eff->is_pixel_metrics) {
		size.x = m4_divfix(size.x, eff->min_hsize);
		size.y = m4_divfix(size.y, eff->min_hsize);
	}
	if ((st->size.x != size.x) || (st->size.y != size.y) /*|| (Node_GetDirty(node) & SG_NODE_DIRTY)*/) {
		st->size = size;
		mesh_new_rectangle(st->mesh, size);
		if (eff->traversing_mode!=TRAVERSE_GET_BOUNDS) Node_SetDirty(node, 1);
		Node_ClearDirty(node);
	}

	if (eff->traversing_mode==TRAVERSE_GET_BOUNDS) {
		eff->bbox = st->mesh->bounds;
		return;
	}

	VS_GetAspect2D(eff, &asp);

	/*texture is available in hw, use it - if blending, force using texture*/
	if (!tx_needs_reload(txh) || (asp.alpha!=FIX_ONE) || !sr->bitmap_use_pixels) {
		if (tx_set_image(txh, 0)) {
			VS3D_SetState(eff->surface, F3D_LIGHT, 0);
			VS3D_SetAntiAlias(eff->surface, 0);
			if (asp.alpha != FIX_ONE) {
				VS3D_SetMaterial2D(eff->surface, asp.fill_color, asp.alpha);
				tx_set_blend_mode(txh, TX_MODULATE);
			} else if (txh->transparent) {
				tx_set_blend_mode(txh, TX_REPLACE);
			} else {
				VS3D_SetState(eff->surface, F3D_BLEND, 0);
			}
			/*ignore texture transform for bitmap*/
 			tx_enable(txh, NULL);
			VS3D_DrawMesh(eff, st->mesh, 0);
 			tx_disable(txh);
			return;
		}
	}


	/*otherwise use glDrawPixels*/
	data = tx_get_data(txh, &format);
	if (!data) return;

	x = INT2FIX(txh->width) / -2;
	y = INT2FIX(txh->height) / 2;

	{
	Fixed g[16];

	/*add top level scale if any*/
	sx = m4_mulfix(sx, sr->scale_x);
	sy = m4_mulfix(sy, sr->scale_y);

	/*get & apply current transform scale*/
	VS3D_GetMatrix(eff->surface, MAT_MODELVIEW, g);

	if (g[0]<0) g[0] *= -FIX_ONE;
	if (g[5]<0) g[5] *= -FIX_ONE;
	sx = m4_mulfix(sx, g[0]);
	sy = m4_mulfix(sy, g[5]);
	x = m4_mulfix(x, sx);
	y = m4_mulfix(y, sy);

	}

	VS3D_DrawImage(eff->surface, x, y, txh->width, txh->height, format, data, sx, sy);
}

static void Bitmap_PreDestroy(SFNode *n)
{
	BitmapStack *st = (BitmapStack *)Node_GetPrivate(n);
	mesh_free(st->mesh);
	free(st);
}

void R3D_InitBitmap(Render3D *sr, SFNode *node)
{
	BitmapStack *st = SAFEALLOC(st, sizeof(BitmapStack));
	stack_setup(st, node, sr->compositor);
	Node_SetPrivate(node, st);
	Node_SetPreDestroyFunction(node, Bitmap_PreDestroy);
	Node_SetRenderFunction(node, RenderBitmap);
	st->IntersectWithRay = BitmapIntersectWithRay;
}
