
#ifndef INC_GFWK_TEST
#include "gfwk_test.h"
#endif
#ifndef INC_DISPLAYENGINE
#include "DisplayEngine.h"
#endif
#ifndef INC_GAME_LOOP
#include "GameLoop.h"
#endif

#define _USE_MATH_DEFINES
#include <math.h>

#include <string>
#include <vector>

#include <allegro.h>

//----------------------------------------------------------------------------

#define IRAND(n) (int)(n*(rand()/(double)RAND_MAX))
#define DRAND(n) ((double)n*(rand()/(double)RAND_MAX))

//----------------------------------------------------------------------------

class WorldMap : public GameClockIfc, public ViewPortBase, public KbdHndlIfc
{
public:
    WorldMap( const ScreenRect&asr );
    ~WorldMap();
    virtual void advance_game_time();
    virtual void handle_kbd( int scan_code, int shift_flags );
    virtual void display( BITMAP*const );

private:
    void add_balls( int );
    void rem_balls( int );
    BITMAP* bmp_ball;
    struct BallPosDir
    {
        BallPosDir( double ax, double ay, double adx, double ady ) : x_(ax), y_(ay), dx_(adx), dy_(ady)
        {}
        double x_, y_;   // position
        double dx_, dy_; // direction
    };
    std::vector< BallPosDir > balls;
};

//----------------------------------------------------------------------------

WorldMap::WorldMap( const ScreenRect&asr )
        : ViewPortBase( asr )
        , bmp_ball(0)
{
    BITMAP*bmp_tmp = load_bitmap( "ball.tga", 0 );
    if( !bmp_tmp )
        fatalError( "cannot load ball.tga" );
#if 0
    bmp_ball = bmp_tmp;
#else
    // bmp_tmp is NOT a system bitmap and NOT a video bitmap
    bmp_ball = create_system_bitmap( bmp_tmp->w, bmp_tmp->h );
    blit( bmp_tmp, bmp_ball, 0, 0, 0, 0, bmp_tmp->w, bmp_tmp->h );
    destroy_bitmap( bmp_tmp );
#endif
}

WorldMap::~WorldMap()
{
    if( bmp_ball )
        destroy_bitmap( bmp_ball );
    bmp_ball=0;
}

void WorldMap::add_balls( int nb )
{
    for( int i=0; i<nb; ++i )
    {
        double angle = DRAND(2*M_PI);
        double speed = 1+DRAND(5);
        double dx = speed * sin(angle);
        double dy = speed * cos(angle);
        balls.push_back( BallPosDir( area.x + area.w/2, area.y + area.h/2, dx, dy ) );
    }
}

void WorldMap::rem_balls( int nb )
{
    if( balls.size() == 0 )
        return;
    for( int i=0; i<nb && balls.size()>0; ++i )
    {
        balls.pop_back();
    }
}

//----------------------------------------------------------------------------

void WorldMap::advance_game_time()
{
    for( size_t i=0; i<balls.size(); ++i )
    {
        BallPosDir&bpd = balls[i];
        bpd.x_ += bpd.dx_;
        bpd.y_ += bpd.dy_;
        if(  bpd.x_ <= area.x
                || bpd.x_ >= area.x+area.w-bmp_ball->w
          )
        {
            bpd.dx_ = -bpd.dx_;
        }
        if(  bpd.y_ <= area.y
                || bpd.y_ >= area.y+area.h-bmp_ball->h
          )
        {
            bpd.dy_ = -bpd.dy_;
        }
    }
}

void WorldMap::handle_kbd( int scan_code, int shift_flags )
{
    switch( scan_code )
    {
    case KEY_X:
        add_balls(50);
        break;
    case KEY_Z:
        rem_balls(50);
        break;
    case KEY_S:
        add_balls(1000);
        break;
    case KEY_A:
        rem_balls(1000);
        break;
    }
}

//----------------------------------------------------------------------------

void WorldMap::display( BITMAP*const db )
{
    for( size_t i=0; i<balls.size(); ++i )
    {
        const BallPosDir&bpd = balls[i];
        draw_sprite( db, bmp_ball, bpd.x_, bpd.y_ );
    }
    set_trans_blender(0,0,0,150);
    drawing_mode( DRAW_MODE_TRANS, db, 0, 0 );
    rectfill( db, area.x, area.y, area.x+area.w, area.y+22, 0 );
    const int text_color = makecol(150,150,150);
    textprintf_ex( db, font, area.x+3, area.y+ 3, text_color, -1, "%u balls", balls.size() );
}

//----------------------------------------------------------------------------

const char* main_ini_fn = "gfwk.ini";

int main(int argc, char* argv[], char* envp[])
{
    // initialize subsystems
    set_uformat( U_ASCII );
    allegro_init();
    install_timer();
    install_keyboard();
    install_mouse();

    DisplayEngine display_engine;
    display_engine.init( main_ini_fn );

    const int bar_height = text_height(font) + 6;
    WorldMap world_map( ScreenRect( 0, bar_height, SCREEN_W, SCREEN_H-bar_height ) );

    GameLoop game_loop( display_engine );
    game_loop.config( main_ini_fn );

    GameLoopDebugView game_loop_debug_view( ScreenRect( 0, 0, SCREEN_W, bar_height ), game_loop );

    game_loop.add_view_port( game_loop_debug_view );
    game_loop.add_view_port( world_map );

    game_loop.add_kbd_handler( world_map );

    game_loop.add_gcl_handler( world_map );

    game_loop.loop();

    return 0;
}
END_OF_MAIN();

//----------------------------------------------------------------------------
