#include <windows.h>
#include <stdio.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <string>
#include <sstream>
#include <iomanip>
#include "../fxdll.h"
#include "../../dll_common.h"

using namespace std;

// Some utility functions
void render_indicator(int x);
bool file_exists(const char* filename);
char* search_file(const char* filename);
void get_ball_spr(float angle,int *index,float *rot_add);

// Global stuff
enum {
    SPR_BACKGROUND,
    SPR_BALL_0,
    SPR_BALL_15,
    SPR_BALL_30,
    SPR_BALL_45,
    SPR_BALL_60,
    SPR_BALL_75,
    SPR_BALL_SHADOW,
    SPR_PLAYER_SHADOW,
    SPR_POST,
    SPR_REF_FULLPOINT,
    SPR_REF_WHITE,
    SPR_REF_RED,
    SPR_REF_BEGIN,
    SPR_REF_SERVE,
    SPR_DOT,
    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;
double referee_talk_time;
int ref_what;
bool ref_was_fullpoint;

/************************************************************************/
/* 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_0] = sprite_load(search_file("ball_0.png"),true,255,0,255);
    sprites[SPR_BALL_15] = sprite_load(search_file("ball_15.png"),true,255,0,255);
    sprites[SPR_BALL_30] = sprite_load(search_file("ball_30.png"),true,255,0,255);
    sprites[SPR_BALL_45] = sprite_load(search_file("ball_45.png"),true,255,0,255);
    sprites[SPR_BALL_60] = sprite_load(search_file("ball_60.png"),true,255,0,255);
    sprites[SPR_BALL_75] = sprite_load(search_file("ball_75.png"),true,255,0,255);
    sprites[SPR_BALL_SHADOW] = sprite_load(search_file("ball_shadow.png"),true,255,0,255);
    sprites[SPR_PLAYER_SHADOW] = sprite_load(search_file("player_shadow.png"),true,255,0,255);
    sprites[SPR_POST] = sprite_load(search_file("post.png"),true,255,0,255);
    sprites[SPR_REF_FULLPOINT] = sprite_load(search_file("ref_fullpoint.png"),true,255,0,255);    
    sprites[SPR_REF_BEGIN] = sprite_load(search_file("ref_begin.png"),true,255,0,255);
    sprites[SPR_REF_WHITE] = sprite_load(search_file("ref_white.png"),true,255,0,255);
    sprites[SPR_REF_RED] = sprite_load(search_file("ref_red.png"),true,255,0,255);
    sprites[SPR_REF_SERVE] = sprite_load(search_file("ref_serve.png"),true,255,0,255);
    sprites[SPR_DOT] = sprite_load(search_file("dot.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;

    static int old_serving_side;    
    if ( fi->first_frame ) 
    {
        old_serving_side = 0;
        referee_talk_time = 1e100;
    }

    // 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 referee talking
    //
    if ( fi->first_frame || (fi->rally_state_changed && fi->rally_state==0) )
    {
        referee_talk_time = 0;
        ref_what = SPR_REF_BEGIN;
    }
    if ( fi->rally_state_changed && fi->rally_state==2 )
    {
        referee_talk_time = 0;
        ref_what = fi->serving_side==0 ? SPR_REF_WHITE:SPR_REF_RED;
        ref_was_fullpoint = fi->serving_side == old_serving_side;
    }

    const double REF_TALK_TIME = 900;
    if ( referee_talk_time < REF_TALK_TIME  )
    {
        sprite_render(sprites[ref_what],650,460,255,255,255,0,1.0f,1.0f,1.0f,0);
        referee_talk_time += fi->time_elapsed;
        if ( referee_talk_time > REF_TALK_TIME && (ref_what==SPR_REF_WHITE||ref_what==SPR_REF_RED) )
        {
            referee_talk_time = 0;
            if ( ref_was_fullpoint )
                ref_what = SPR_REF_FULLPOINT;
            else ref_what = SPR_REF_SERVE;
        }
    }
    old_serving_side = fi->serving_side;



    // render player shadows
    sprite_getinfo(sprites[SPR_PLAYER_SHADOW],&w,&h);
    y = (int)game_floor - fi->p0_y;
    sprite_render ( sprites[SPR_PLAYER_SHADOW], fi->p0_x-w/2+y/6-12, (int)game_floor-h-y/25+4, 255,255,255, 0, 1.0f, 1.0f, 1.0f, 0 );
    y = (int)game_floor - fi->p1_y;
    sprite_render ( sprites[SPR_PLAYER_SHADOW], fi->p1_x-w/2+y/6-12, (int)game_floor-h-y/25+4, 255,255,255, 0, 1.0f, 1.0f, 1.0f, 0 );
    if ( fi->num_players == 4 )
    {
        y = (int)game_floor - fi->p2_y;
        sprite_render ( sprites[SPR_PLAYER_SHADOW], fi->p2_x-w/2+y/6-12, (int)game_floor-h-y/25+4, 255,255,255, 0, 1.0f, 1.0f, 1.0f, 0 );
        y = (int)game_floor - fi->p3_y;
        sprite_render ( sprites[SPR_PLAYER_SHADOW], fi->p3_x-w/2+y/6-12, (int)game_floor-h-y/25+4, 255,255,255, 0, 1.0f, 1.0f, 1.0f, 0 );
    }
    
    // render ball shadow
    sprite_getinfo(sprites[SPR_BALL_SHADOW],&w,&h);
    y = (int)game_floor - fi->ball_y;
    sprite_render ( sprites[SPR_BALL_SHADOW], fi->ball_x-w/2+y/6, (int)game_floor-h-y/25+3, 255,255,255, 0, 1.0f, 1.0f, 1.0f, 0 );
    // render ball
    int ballindex;
    float rotadd;
    get_ball_spr(fi->ball_angle,&ballindex,&rotadd);
    sprite_getinfo(sprites[SPR_BALL_0],&w,&h);
    sprite_render(sprites[SPR_BALL_0+ballindex], fi->ball_x-w/2, fi->ball_y-h/2, 255,255,255, rotadd, 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, 242, 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 dx,dy;
        stringstream s;

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

        s << (i==0?fi->p0_name:fi->p1_name);        
        s << "  " << setfill('0') << setw(2) << (i==0?fi->left_score:fi->right_score);
        if ( i == 1 ) 
        {
            font_get_text_extent(0,&dx, &dy, size, s.str().c_str() );
            font_render(0,RES_X-64-dx, -3, size, s.str().c_str(), r,g,b );
        }
        else
        {
            font_render(0,64, -3, size, s.str().c_str(), r,g,b );
        }

        if(fi->num_players==4)
        {
            s.str("");s.clear();
            s << "& " << (i==0?fi->p2_name:fi->p3_name);      
            if ( i == 1 ) 
            {
                font_get_text_extent(0,&dx, &dy, size, s.str().c_str() );
                font_render(0,RES_X-30-dx, 26, size, s.str().c_str(), r,g,b );
            }
            else
            {
                font_render(0,30, 26, size, s.str().c_str(), r,g,b );
            }
        }
    }
    // render dot to on serving side
    if ( fi->serving_side==0 )
        sprite_render(sprites[SPR_DOT], 4, 1, 62,49,162,0,1.0f,1.0f,1.0f,0);
    else
        sprite_render(sprites[SPR_DOT], RES_X-32-4, 1, 62,49,162,0,1.0f,1.0f,1.0f,0);

    // speed-o-meter
    static int speed = 0;
    if ( fi->rally_state != 2 )
        speed = (int)((fi->speed_factor-1)*6);
    for(int i=0;i<12;i++)
    {
        if ( i < speed )
            sprite_render(sprites[SPR_DOT],RES_X/2-6*32+i*32, 30, 187,119,109 ,0,1.0f,1.0f,1.0f,0);
        else
            sprite_render(sprites[SPR_DOT],RES_X/2-6*32+i*32, 30, 171,171,171 ,0,1.0f,1.0f,1.0f,0);
    }

    // touch-o-meter
    for(int i=0;i<fi->s0_touched;i++)
        sprite_render(sprites[SPR_DOT],RES_X/2-(i+1)*32, 1, 255,255,255 ,0,1.0f,1.0f,1.0f,0);
    for(int i=0;i<fi->s1_touched;i++)
        sprite_render(sprites[SPR_DOT],RES_X/2+i*32, 1, 195,60,60 ,0,1.0f,1.0f,1.0f,0);
}


/************************************************************************/
/* 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;
}


/************************************************************************/
/*                                                                      */
/************************************************************************/
void get_ball_spr(float angle,int *index,float *rot_add)
{
    *rot_add = 0;
    while ( angle > 90-7.5f )
    {
        *rot_add += 90;
        angle -= 90;
    }
    *index = (int)((angle+7.5f)/15.0f);
}



