/************************************************************************/
/*                                                                      */
/* This is the module that implements the "standard" FX DLL used for    */
/* the 'classic' and most simple themes.                                */
/*                                                                      */
/* If you want to create a custom fx dll, this is the file to edit.     */
/*                                                                      */
/************************************************************************/

#include <windows.h>
#include <stdio.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <string>
#include <sstream>
#include "../fxdll.h"           // NOTE: you might need to adjust the path to this include file
#include "../../dll_common.h"   // NOTE: you might need to adjust the path to this include file

using namespace std;

// Some utility functions
void render_indicator(int x);
bool file_exists(const char* filename);
char* search_file(const char* filename);

// Global stuff
enum {
    SPR_BACKGROUND,
    SPR_BALL,
    SPR_SHADOW,
    SPR_POST,
    NUM_SPRITES
};
unsigned sprites[NUM_SPRITES];

enum {
    SND_BALL_PLAYER_CONTACT,
    SND_BALL_NET_CONTACT,
    SND_WHISTLE,
    NUM_SOUNDS
};
unsigned sounds[NUM_SOUNDS];

char theme_path[512];
float game_floor;

/************************************************************************/
/* This function is called by the time the theme is loaded.             */
/* Use this e.g. to load custom sprites, sounds, etc...                 */
/* It should return true if everything went ok, false otherwise.        */
/* The parameter is the current theme name, which can be used for       */
/* assembling path names.                                               */
/************************************************************************/
DLL_EXPORT bool fx_init(const char* theme)
{
    // Build the theme path relative from current directory
    // (current directory is always the dir of harry.exe).
    sprintf(theme_path,"data/%s/",theme);

    // Get some physics parameters that we need..
    get_physparm("GAME_FLOOR",&game_floor);

    // Load some sounds. If the sound files are not found in the current
    // theme, they are loaded from the classic theme.
    sounds[SND_BALL_PLAYER_CONTACT] = sound_load(search_file("ball_contact.ogg"));
    sounds[SND_BALL_NET_CONTACT] = sound_load(search_file("ball_net_contact.ogg"));
    sounds[SND_WHISTLE] = sound_load(search_file("whistle.ogg"));
    for(int i=0;i<NUM_SOUNDS;i++)
        if(!sounds[i]) return false;

    // The same for some sprites...
    sprites[SPR_BACKGROUND] = sprite_load(search_file("background_game.jpg"),false,0,0,0);
    sprites[SPR_BALL] = sprite_load(search_file("ball.png"),true,255,0,255);
    sprites[SPR_SHADOW] = sprite_load(search_file("shadow.png"),true,255,0,255);
    sprites[SPR_POST] = sprite_load(search_file("post.png"),true,255,0,255);
    for(int i=0;i<NUM_SPRITES;i++)
        if(!sprites[i]) return false;

    return true;
}


/************************************************************************/
/* This function is called by the time the theme is unloaded.           */
/* Make sure to cleanly unload all the resources you loaded in fx_init. */
/* It should return true if everything went ok, false otherwise.        */
/************************************************************************/
DLL_EXPORT bool fx_shutdown()
{
    for(int i=0;i<NUM_SOUNDS;i++)
        sound_free(sounds[i]);

    for(int i=0;i<NUM_SPRITES;i++)
        sprite_free(sprites[i]);

    return true;
}


/************************************************************************/
/* This function is called right BEFORE the player characters are       */
/* rendered. Stuff you render in here will therefore appear BEHIND the  */
/* players.                                                             */
/* See dll_common.h for a description of the FX_FrameInfo structure.    */
/* The return value controls whether the game should actually render the*/
/* player characters. If false is returned, rendering of the players    */
/* is omitted and fx_frame_fg is called directly. This can be used if   */
/* you write a totally customized renderer (e.g. a 3D mode or so) and   */
/* don't want ANY rendering by the game at all. Note that some of the   */
/* normal player rendering code is still executed within the game menu  */
/* and the winner screen.                                               */
/************************************************************************/
DLL_EXPORT bool fx_frame_bg ( const FX_FrameInfo *fi )
{
    int y,w,h;

    // play sound effects
    if ( fi->pcoll_x >= 0 && fi->pcoll_y >= 0 )
        sound_play(sounds[SND_BALL_PLAYER_CONTACT]);
    if ( fi->ncoll_x >= 0 && fi->ncoll_y >= 0 )
        sound_play(sounds[SND_BALL_NET_CONTACT]);
    if ( fi->rally_state_changed && fi->rally_state == 2 )
        sound_play(sounds[SND_WHISTLE]);

    // render background
    sprite_render(sprites[SPR_BACKGROUND],0,0,255,255,255,0,1.0f,1.0f,1.0f,0);

    // render player shadows
    sprite_getinfo(sprites[SPR_SHADOW],&w,&h);
    y = (int)game_floor - fi->p0_y;
    sprite_render ( sprites[SPR_SHADOW], fi->p0_x-w/2+y/6, (int)game_floor-h-y/25, 255,255,255, 0, 1.0f, 1.0f, 0.3f, 0 );
    y = (int)game_floor - fi->p1_y;
    sprite_render ( sprites[SPR_SHADOW], fi->p1_x-w/2+y/6, (int)game_floor-h-y/25, 255,255,255, 0, 1.0f, 1.0f, 0.3f, 0 );
    if ( fi->num_players == 4 )
    {
        y = (int)game_floor - fi->p2_y;
        sprite_render ( sprites[SPR_SHADOW], fi->p2_x-w/2+y/6, (int)game_floor-h-y/25, 255,255,255, 0, 1.0f, 1.0f, 0.3f, 0 );
        y = (int)game_floor - fi->p3_y;
        sprite_render ( sprites[SPR_SHADOW], fi->p3_x-w/2+y/6, (int)game_floor-h-y/25, 255,255,255, 0, 1.0f, 1.0f, 0.3f, 0 );
    }
    
    // render ball shadow
    y = (int)game_floor - fi->ball_y;
    sprite_render ( sprites[SPR_SHADOW], fi->ball_x-w/2+y/6, (int)game_floor-h-y/25+5, 255,255,255, 0, 1.0f, 1.0f, 0.3f, 0 );
    // render ball
    sprite_getinfo(sprites[SPR_BALL],&w,&h);
    sprite_render(sprites[SPR_BALL], fi->ball_x-w/2, fi->ball_y-h/2, 255,255,255, fi->ball_angle, 1.0f, 1.0f, 1.0f, 0);


    // we want the game to render the players..
    return true;
}


/************************************************************************/
/* This function is called right AFTER the player characters are        */
/* rendered. Stuff you render in here will therefore appear IN FRONT OF */
/* the players.                                                         */
/************************************************************************/
DLL_EXPORT void fx_frame_fg ( const FX_FrameInfo *fi )
{
    int w,h;
    
    // render post, so players are covered by it when they get close
    sprite_getinfo(sprites[SPR_POST],&w,&h);
    sprite_render(sprites[SPR_POST],RES_X/2-w/2, 202, 255,255,255, 0, 1.0f, 1.0f, 1.0f, 0 );


    // render ball indicator at top of the screen
    render_indicator(fi->ball_x);


    // render names and score
    for(int i=0;i<2;i++)
    {
        float size = 0.5f;
        unsigned char r,g,b;
        int x,dx,dy;
        stringstream s;

        if ( !fi->serving_side==i )
            size = 0.35f;
        
        if ( i==1 ) x = RES_X/2;
        else x = 0;

        if ( i==0 )
        {
            r = fi->s0_r;
            g = fi->s0_g;
            b = fi->s0_b;
        }
        else
        {
            r = fi->s1_r;
            g = fi->s1_g;
            b = fi->s1_b;
        }

        // render name(s)
        s << (i==0?fi->p0_name:fi->p1_name);
        if(fi->num_players==4)
            s << " & " << (i==0?fi->p2_name:fi->p3_name);
        font_get_text_extent(0,&dx, &dy, size, s.str().c_str() );
        font_render(0,x+RES_X/4-dx/2, 10, size, s.str().c_str(), r,g,b );

        // render score
        s.str(""); s.clear();
        s << (i==0?fi->left_score:fi->right_score);
        font_get_text_extent(0,&dx, &dy, size, s.str().c_str());
        font_render(0,x+RES_X/4-dx/2, 40, size, s.str().c_str(), r,g,b );
    }
}


/************************************************************************/
/* This function is called when the match is over and the game is       */
/* returning to the menu. It can be used to clean up some temporary     */
/* stuff that can't wait until the entire theme is unloaded.            */
/************************************************************************/
DLL_EXPORT void fx_returning_to_menu()
{
}


/************************************************************************/
/* render ball x pos indicator                                          */
/************************************************************************/
void render_indicator(int x)
{
    glLineWidth(5.0f);
    glBindTexture(GL_TEXTURE_2D, NULL);
    glPushMatrix();
    glLoadIdentity();    
    glBegin(GL_LINES);
    glColor3f(0.9f,0.9f,0.9f);
    glVertex2f((float)x, 10.0f);
    glVertex2f((float)x, 0.0f);
    glEnd();
    glPopMatrix();
}


/************************************************************************/
/* check if a file exists                                               */
/************************************************************************/
bool file_exists(const char* filename)
{
    return ( GetFileAttributes(filename) != INVALID_FILE_ATTRIBUTES );
}


/************************************************************************/
/* this function checks whether a given filename exists in the current  */
/* theme directory, and if so, returns a path to it. if not, it returns */
/* the path to the same filename in the classic theme directory         */
/* (even if the file does not exist there!)                             */
/************************************************************************/
char* search_file(const char* filename)
{
    static char ret[512];
    sprintf(ret,"%s%s",theme_path,filename);
    if ( file_exists(ret) ) return ret;
    sprintf(ret,"data/classic/%s",filename);
    return ret;
}