/* Direct3D Common functions
 * Copyright (c) 1998 Lionel ULMER
 *
 * This file contains all common miscellaneous code that spans
 * different 'objects'
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <stdarg.h>

#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "windef.h"
#include "winbase.h"
#include "objbase.h"
#include "wingdi.h"
#include "ddraw.h"
#include "d3d.h"
#include "wine/debug.h"

#include "d3d_private.h"

WINE_DEFAULT_DEBUG_CHANNEL(ddraw);

const char *_get_renderstate(D3DRENDERSTATETYPE type) {
    static const char * const states[] = {
        "ERR",
	"D3DRENDERSTATE_TEXTUREHANDLE",
	"D3DRENDERSTATE_ANTIALIAS",
	"D3DRENDERSTATE_TEXTUREADDRESS",
	"D3DRENDERSTATE_TEXTUREPERSPECTIVE",
	"D3DRENDERSTATE_WRAPU",
	"D3DRENDERSTATE_WRAPV",
	"D3DRENDERSTATE_ZENABLE",
	"D3DRENDERSTATE_FILLMODE",
	"D3DRENDERSTATE_SHADEMODE",
	"D3DRENDERSTATE_LINEPATTERN",
	"D3DRENDERSTATE_MONOENABLE",
	"D3DRENDERSTATE_ROP2",
	"D3DRENDERSTATE_PLANEMASK",
	"D3DRENDERSTATE_ZWRITEENABLE",
	"D3DRENDERSTATE_ALPHATESTENABLE",
	"D3DRENDERSTATE_LASTPIXEL",
	"D3DRENDERSTATE_TEXTUREMAG",
	"D3DRENDERSTATE_TEXTUREMIN",
	"D3DRENDERSTATE_SRCBLEND",
	"D3DRENDERSTATE_DESTBLEND",
	"D3DRENDERSTATE_TEXTUREMAPBLEND",
	"D3DRENDERSTATE_CULLMODE",
	"D3DRENDERSTATE_ZFUNC",
	"D3DRENDERSTATE_ALPHAREF",
	"D3DRENDERSTATE_ALPHAFUNC",
	"D3DRENDERSTATE_DITHERENABLE",
	"D3DRENDERSTATE_ALPHABLENDENABLE",
	"D3DRENDERSTATE_FOGENABLE",
	"D3DRENDERSTATE_SPECULARENABLE",
	"D3DRENDERSTATE_ZVISIBLE",
	"D3DRENDERSTATE_SUBPIXEL",
	"D3DRENDERSTATE_SUBPIXELX",
	"D3DRENDERSTATE_STIPPLEDALPHA",
	"D3DRENDERSTATE_FOGCOLOR",
	"D3DRENDERSTATE_FOGTABLEMODE",
	"D3DRENDERSTATE_FOGTABLESTART",
	"D3DRENDERSTATE_FOGTABLEEND",
	"D3DRENDERSTATE_FOGTABLEDENSITY",
	"D3DRENDERSTATE_STIPPLEENABLE",
	"D3DRENDERSTATE_EDGEANTIALIAS",
	"D3DRENDERSTATE_COLORKEYENABLE",
	"ERR",
	"D3DRENDERSTATE_BORDERCOLOR",
	"D3DRENDERSTATE_TEXTUREADDRESSU",
	"D3DRENDERSTATE_TEXTUREADDRESSV",
	"D3DRENDERSTATE_MIPMAPLODBIAS",
	"D3DRENDERSTATE_ZBIAS",
	"D3DRENDERSTATE_RANGEFOGENABLE",
	"D3DRENDERSTATE_ANISOTROPY",
	"D3DRENDERSTATE_FLUSHBATCH",
	"D3DRENDERSTATE_TRANSLUCENTSORTINDEPENDENT",
	"D3DRENDERSTATE_STENCILENABLE",
	"D3DRENDERSTATE_STENCILFAIL",
	"D3DRENDERSTATE_STENCILZFAIL",
	"D3DRENDERSTATE_STENCILPASS",
	"D3DRENDERSTATE_STENCILFUNC",
	"D3DRENDERSTATE_STENCILREF",
	"D3DRENDERSTATE_STENCILMASK",
	"D3DRENDERSTATE_STENCILWRITEMASK",
	"D3DRENDERSTATE_TEXTUREFACTOR",
	"ERR",
	"ERR",
	"ERR",
	"D3DRENDERSTATE_STIPPLEPATTERN00",
	"D3DRENDERSTATE_STIPPLEPATTERN01",
	"D3DRENDERSTATE_STIPPLEPATTERN02",
	"D3DRENDERSTATE_STIPPLEPATTERN03",
	"D3DRENDERSTATE_STIPPLEPATTERN04",
	"D3DRENDERSTATE_STIPPLEPATTERN05",
	"D3DRENDERSTATE_STIPPLEPATTERN06",
	"D3DRENDERSTATE_STIPPLEPATTERN07",
	"D3DRENDERSTATE_STIPPLEPATTERN08",
	"D3DRENDERSTATE_STIPPLEPATTERN09",
	"D3DRENDERSTATE_STIPPLEPATTERN10",
	"D3DRENDERSTATE_STIPPLEPATTERN11",
	"D3DRENDERSTATE_STIPPLEPATTERN12",
	"D3DRENDERSTATE_STIPPLEPATTERN13",
	"D3DRENDERSTATE_STIPPLEPATTERN14",
	"D3DRENDERSTATE_STIPPLEPATTERN15",
	"D3DRENDERSTATE_STIPPLEPATTERN16",
	"D3DRENDERSTATE_STIPPLEPATTERN17",
	"D3DRENDERSTATE_STIPPLEPATTERN18",
	"D3DRENDERSTATE_STIPPLEPATTERN19",
	"D3DRENDERSTATE_STIPPLEPATTERN20",
	"D3DRENDERSTATE_STIPPLEPATTERN21",
	"D3DRENDERSTATE_STIPPLEPATTERN22",
	"D3DRENDERSTATE_STIPPLEPATTERN23",
	"D3DRENDERSTATE_STIPPLEPATTERN24",
	"D3DRENDERSTATE_STIPPLEPATTERN25",
	"D3DRENDERSTATE_STIPPLEPATTERN26",
	"D3DRENDERSTATE_STIPPLEPATTERN27",
	"D3DRENDERSTATE_STIPPLEPATTERN28",
	"D3DRENDERSTATE_STIPPLEPATTERN29",
	"D3DRENDERSTATE_STIPPLEPATTERN30",
	"D3DRENDERSTATE_STIPPLEPATTERN31"
    };
    static const char * const states_2[] = {
        "D3DRENDERSTATE_WRAP0",
	"D3DRENDERSTATE_WRAP1",
	"D3DRENDERSTATE_WRAP2",
	"D3DRENDERSTATE_WRAP3",
	"D3DRENDERSTATE_WRAP4",
	"D3DRENDERSTATE_WRAP5",
	"D3DRENDERSTATE_WRAP6",
	"D3DRENDERSTATE_WRAP7",
	"D3DRENDERSTATE_CLIPPING",
	"D3DRENDERSTATE_LIGHTING",
	"D3DRENDERSTATE_EXTENTS",
	"D3DRENDERSTATE_AMBIENT",
	"D3DRENDERSTATE_FOGVERTEXMODE",
	"D3DRENDERSTATE_COLORVERTEX",
	"D3DRENDERSTATE_LOCALVIEWER",
	"D3DRENDERSTATE_NORMALIZENORMALS",
	"D3DRENDERSTATE_COLORKEYBLENDENABLE",
	"D3DRENDERSTATE_DIFFUSEMATERIALSOURCE",
	"D3DRENDERSTATE_SPECULARMATERIALSOURCE",
	"D3DRENDERSTATE_AMBIENTMATERIALSOURCE",
	"D3DRENDERSTATE_EMISSIVEMATERIALSOURCE",
	"ERR",
	"ERR",
	"D3DRENDERSTATE_VERTEXBLEND",
	"D3DRENDERSTATE_CLIPPLANEENABLE",
    };
    if (type >= D3DRENDERSTATE_WRAP0) {
        type -= D3DRENDERSTATE_WRAP0;
	if (type >= (sizeof(states_2) / sizeof(states_2[0]))) return "ERR";
	return states_2[type];
    }
    if (type >= (sizeof(states) / sizeof(states[0]))) return "ERR";
    return states[type];
}

void
dump_D3DCOLORVALUE(D3DCOLORVALUE *lpCol)
{
    DPRINTF("%f %f %f %f", lpCol->u1.r, lpCol->u2.g, lpCol->u3.b, lpCol->u4.a);
}

void
dump_D3DVECTOR(D3DVECTOR *lpVec)
{
    DPRINTF("%f %f %f", lpVec->u1.x, lpVec->u2.y, lpVec->u3.z);
}

void
dump_D3DMATERIAL7(LPD3DMATERIAL7 lpMat)
{
    DPRINTF(" - diffuse  : "); dump_D3DCOLORVALUE(&(lpMat->u.diffuse)); DPRINTF("\n");
    DPRINTF(" - ambient  : "); dump_D3DCOLORVALUE(&(lpMat->u1.ambient)); DPRINTF("\n");
    DPRINTF(" - specular : "); dump_D3DCOLORVALUE(&(lpMat->u2.specular)); DPRINTF("\n");
    DPRINTF(" - emissive : "); dump_D3DCOLORVALUE(&(lpMat->u3.emissive)); DPRINTF("\n");
    DPRINTF(" - power    : %f\n", lpMat->u4.power);
}

void
dump_D3DLIGHT7(LPD3DLIGHT7 lpLight)
{
    DPRINTF(" - light type : %s\n", (lpLight->dltType == D3DLIGHT_POINT ? "D3DLIGHT_POINT" : 
				     (lpLight->dltType == D3DLIGHT_SPOT ? "D3DLIGHT_SPOT" : 
				      (lpLight->dltType == D3DLIGHT_DIRECTIONAL ? "D3DLIGHT_DIRECTIONAL" : 
				       "UNSUPPORTED"))));
    DPRINTF(" - diffuse       : "); dump_D3DCOLORVALUE(&(lpLight->dcvDiffuse)); DPRINTF("\n");
    DPRINTF(" - specular      : "); dump_D3DCOLORVALUE(&(lpLight->dcvSpecular)); DPRINTF("\n");
    DPRINTF(" - ambient       : "); dump_D3DCOLORVALUE(&(lpLight->dcvAmbient)); DPRINTF("\n");
    DPRINTF(" - position      : "); dump_D3DVECTOR(&(lpLight->dvPosition)); DPRINTF("\n");
    DPRINTF(" - direction     : "); dump_D3DVECTOR(&(lpLight->dvDirection)); DPRINTF("\n");
    DPRINTF(" - dvRange       : %f\n", lpLight->dvRange);
    DPRINTF(" - dvFalloff     : %f\n", lpLight->dvFalloff);
    DPRINTF(" - dvAttenuation : %f %f %f\n", lpLight->dvAttenuation0, lpLight->dvAttenuation1, lpLight->dvAttenuation2);
    DPRINTF(" - dvTheta       : %f\n", lpLight->dvTheta);
    DPRINTF(" - dvPhi         : %f\n", lpLight->dvPhi);
}

void
dump_DPFLAGS(DWORD dwFlags)
{
    static const flag_info flags[] =
    {
        FE(D3DDP_WAIT),
	FE(D3DDP_OUTOFORDER),
	FE(D3DDP_DONOTCLIP),
	FE(D3DDP_DONOTUPDATEEXTENTS),
	FE(D3DDP_DONOTLIGHT)
    };

    DDRAW_dump_flags(dwFlags, flags, sizeof(flags)/sizeof(flags[0]));
}

void
dump_D3DMATRIX(D3DMATRIX *mat)
{
    DPRINTF("  %f %f %f %f\n", mat->_11, mat->_12, mat->_13, mat->_14);
    DPRINTF("  %f %f %f %f\n", mat->_21, mat->_22, mat->_23, mat->_24);
    DPRINTF("  %f %f %f %f\n", mat->_31, mat->_32, mat->_33, mat->_34);
    DPRINTF("  %f %f %f %f\n", mat->_41, mat->_42, mat->_43, mat->_44);
}

DWORD get_flexible_vertex_size(DWORD d3dvtVertexType)
{
    DWORD size = 0;
    int i;
    
    if (d3dvtVertexType & D3DFVF_NORMAL) size += 3 * sizeof(D3DVALUE);
    if (d3dvtVertexType & D3DFVF_DIFFUSE) size += sizeof(DWORD);
    if (d3dvtVertexType & D3DFVF_SPECULAR) size += sizeof(DWORD);
    if (d3dvtVertexType & D3DFVF_RESERVED1) size += sizeof(DWORD);
    switch (d3dvtVertexType & D3DFVF_POSITION_MASK) {
        case D3DFVF_XYZ: size += 3 * sizeof(D3DVALUE); break;
        case D3DFVF_XYZRHW: size += 4 * sizeof(D3DVALUE); break;
	default: TRACE(" matrix weighting not handled yet...\n");
    }
    for (i = 0; i < GET_TEXCOUNT_FROM_FVF(d3dvtVertexType); i++) {
	size += GET_TEXCOORD_SIZE_FROM_FVF(d3dvtVertexType, i) * sizeof(D3DVALUE);
    }
    
    return size;
}

void dump_flexible_vertex(DWORD d3dvtVertexType)
{
    static const flag_info flags[] = {
        FE(D3DFVF_NORMAL),
	FE(D3DFVF_RESERVED1),
	FE(D3DFVF_DIFFUSE),
	FE(D3DFVF_SPECULAR)
    };
    unsigned int i;
    
    if (d3dvtVertexType & D3DFVF_RESERVED0) DPRINTF("D3DFVF_RESERVED0 ");
    switch (d3dvtVertexType & D3DFVF_POSITION_MASK) {
#define GEN_CASE(a) case a: DPRINTF(#a " "); break
        GEN_CASE(D3DFVF_XYZ);
	GEN_CASE(D3DFVF_XYZRHW);
	GEN_CASE(D3DFVF_XYZB1);
	GEN_CASE(D3DFVF_XYZB2);
	GEN_CASE(D3DFVF_XYZB3);
	GEN_CASE(D3DFVF_XYZB4);
	GEN_CASE(D3DFVF_XYZB5);
    }
    DDRAW_dump_flags_(d3dvtVertexType, flags, sizeof(flags)/sizeof(flags[0]), FALSE);
    switch (d3dvtVertexType & D3DFVF_TEXCOUNT_MASK) {
        GEN_CASE(D3DFVF_TEX0);
	GEN_CASE(D3DFVF_TEX1);
	GEN_CASE(D3DFVF_TEX2);
	GEN_CASE(D3DFVF_TEX3);
	GEN_CASE(D3DFVF_TEX4);
	GEN_CASE(D3DFVF_TEX5);
	GEN_CASE(D3DFVF_TEX6);
	GEN_CASE(D3DFVF_TEX7);
	GEN_CASE(D3DFVF_TEX8);
    }
#undef GEN_CASE
    for (i = 0; i < GET_TEXCOUNT_FROM_FVF(d3dvtVertexType); i++) {
        DPRINTF(" T%d-s%ld", i + 1, GET_TEXCOORD_SIZE_FROM_FVF(d3dvtVertexType, i));
    }
    DPRINTF("\n");
}

void
convert_FVF_to_strided_data(DWORD d3dvtVertexType, LPVOID lpvVertices, D3DDRAWPRIMITIVESTRIDEDDATA *strided, DWORD dwStartVertex)
{
    int current_offset = 0;
    unsigned int tex_index;
    int size = get_flexible_vertex_size(d3dvtVertexType);

    lpvVertices = ((BYTE *) lpvVertices) + (size * dwStartVertex);
    
    if ((d3dvtVertexType & D3DFVF_POSITION_MASK) == D3DFVF_XYZ) {
        strided->position.lpvData = lpvVertices;
        current_offset += 3 * sizeof(D3DVALUE);
    } else {
        strided->position.lpvData  = lpvVertices;
        current_offset += 4 * sizeof(D3DVALUE);
    }
    if (d3dvtVertexType & D3DFVF_RESERVED1) {
        current_offset += sizeof(DWORD);
    }
    if (d3dvtVertexType & D3DFVF_NORMAL) { 
        strided->normal.lpvData  = ((char *) lpvVertices) + current_offset;
        current_offset += 3 * sizeof(D3DVALUE);
    }
    if (d3dvtVertexType & D3DFVF_DIFFUSE) { 
        strided->diffuse.lpvData  = ((char *) lpvVertices) + current_offset;
        current_offset += sizeof(DWORD);
    }
    if (d3dvtVertexType & D3DFVF_SPECULAR) {
        strided->specular.lpvData  = ((char *) lpvVertices) + current_offset;
        current_offset += sizeof(DWORD);
    }
    for (tex_index = 0; tex_index < GET_TEXCOUNT_FROM_FVF(d3dvtVertexType); tex_index++) {
        strided->textureCoords[tex_index].lpvData  = ((char *) lpvVertices) + current_offset;
        current_offset += GET_TEXCOORD_SIZE_FROM_FVF(d3dvtVertexType, tex_index) * sizeof(D3DVALUE);
    }
    strided->position.dwStride = current_offset;
    strided->normal.dwStride   = current_offset;
    strided->diffuse.dwStride  = current_offset;
    strided->specular.dwStride = current_offset;
    for (tex_index = 0; tex_index < ((d3dvtVertexType & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT); tex_index++)
        strided->textureCoords[tex_index].dwStride  = current_offset;   
}

void
dump_D3DVOP(DWORD dwVertexOp)
{
    static const flag_info flags[] =
    {
        FE(D3DVOP_LIGHT),
	FE(D3DVOP_CLIP),
	FE(D3DVOP_EXTENTS),
	FE(D3DVOP_TRANSFORM)
    };
    DDRAW_dump_flags(dwVertexOp, flags, sizeof(flags)/sizeof(flags[0]));
}

void
dump_D3DPV(DWORD dwFlags)
{
    if (dwFlags == D3DPV_DONOTCOPYDATA) DPRINTF("D3DPV_DONOTCOPYDATA\n");
    else if (dwFlags != 0) DPRINTF("Unknown !!!\n");
    else DPRINTF("\n");
}

void multiply_matrix(LPD3DMATRIX dest, LPD3DMATRIX src1, LPD3DMATRIX src2)
{
    D3DMATRIX temp;

    /* Now do the multiplication 'by hand'.
       I know that all this could be optimised, but this will be done later :-) */
    temp._11 = (src1->_11 * src2->_11) + (src1->_21 * src2->_12) + (src1->_31 * src2->_13) + (src1->_41 * src2->_14);
    temp._21 = (src1->_11 * src2->_21) + (src1->_21 * src2->_22) + (src1->_31 * src2->_23) + (src1->_41 * src2->_24);
    temp._31 = (src1->_11 * src2->_31) + (src1->_21 * src2->_32) + (src1->_31 * src2->_33) + (src1->_41 * src2->_34);
    temp._41 = (src1->_11 * src2->_41) + (src1->_21 * src2->_42) + (src1->_31 * src2->_43) + (src1->_41 * src2->_44);

    temp._12 = (src1->_12 * src2->_11) + (src1->_22 * src2->_12) + (src1->_32 * src2->_13) + (src1->_42 * src2->_14);
    temp._22 = (src1->_12 * src2->_21) + (src1->_22 * src2->_22) + (src1->_32 * src2->_23) + (src1->_42 * src2->_24);
    temp._32 = (src1->_12 * src2->_31) + (src1->_22 * src2->_32) + (src1->_32 * src2->_33) + (src1->_42 * src2->_34);
    temp._42 = (src1->_12 * src2->_41) + (src1->_22 * src2->_42) + (src1->_32 * src2->_43) + (src1->_42 * src2->_44);

    temp._13 = (src1->_13 * src2->_11) + (src1->_23 * src2->_12) + (src1->_33 * src2->_13) + (src1->_43 * src2->_14);
    temp._23 = (src1->_13 * src2->_21) + (src1->_23 * src2->_22) + (src1->_33 * src2->_23) + (src1->_43 * src2->_24);
    temp._33 = (src1->_13 * src2->_31) + (src1->_23 * src2->_32) + (src1->_33 * src2->_33) + (src1->_43 * src2->_34);
    temp._43 = (src1->_13 * src2->_41) + (src1->_23 * src2->_42) + (src1->_33 * src2->_43) + (src1->_43 * src2->_44);

    temp._14 = (src1->_14 * src2->_11) + (src1->_24 * src2->_12) + (src1->_34 * src2->_13) + (src1->_44 * src2->_14);
    temp._24 = (src1->_14 * src2->_21) + (src1->_24 * src2->_22) + (src1->_34 * src2->_23) + (src1->_44 * src2->_24);
    temp._34 = (src1->_14 * src2->_31) + (src1->_24 * src2->_32) + (src1->_34 * src2->_33) + (src1->_44 * src2->_34);
    temp._44 = (src1->_14 * src2->_41) + (src1->_24 * src2->_42) + (src1->_34 * src2->_43) + (src1->_44 * src2->_44);

    /* And copy the new matrix in the good storage.. */
    memcpy(dest, &temp, 16 * sizeof(D3DVALUE));    
}
