/*
*****************************************
* This is Snake 256!                    *
* Author: Tony Wasserka                 *
* Version: 0.0.3                        *
* License: GPL                          *
* See Readme.txt                        *
* Enjoy this!                           *
*****************************************
*/

#define ALLEGRO_STATICLINK
#include <fstream>
#include <string.h>
#include <math.h>
#include <sys/time.h>
#include <allegro.h>
#ifdef ALLEGRO_WINDOWS
#include <winalleg.h>
#endif
#include <alfont.h>

using namespace std;

#define MENU_SINGLEPLAYER 0
#define MENU_MULTIPLAYER  1
#define MENU_OPTIONS      2
#define MENU_EXIT         3

#define MAX_PLAYERS 8
#define MAX_SCORE 65436
#define min_length 100

#define FONT_NORMAL   0
#define FONT_SUBTITLE 1
#define FONT_SNAKE256 2
#define FONT_GETREADY 3
#define FONT_3        4
#define FONT_2        5
#define FONT_1        6
#define FONT_GO       7

#ifndef switch_antialias
#define switch_antialias(bpp) \
switch(Config::antialias) \
{ \
 case 4: AntialiasScreenBuffer##bpp(); \
 case 3: AntialiasScreenBuffer##bpp(); \
 case 2: AntialiasScreenBuffer##bpp(); \
 case 1: Antialias##bpp(); break; \
 default: blit(screenbmp, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H); \
}
#endif


typedef struct KEY_TABLE
{
 int left, right, fast;
} KEY_TABLE;

class Config
{
public:
 Config() {};
 ~Config() {};

 static int antialias;
 static bool first_start;
 static int card;
 static int screenw, screenh, bpp;
 static int num_gfx_mode;
 static bool show_fps;
 static bool do_vsync;
 static int music_volume, sound_volume;
 static int num_snakes;
 static int colconfig_method[8];
 static int r[8], g[8], b[8];
 static KEY_TABLE keys[8];
 static bool is_cpu[8];
};

class Snake
{
public:
 Snake() {};
 Snake(int _num);
 ~Snake() {};

 float x[65536], y[65536];
 float angle;
 float score, new_score;
 int color;
 bool state;
 int num;
 bool is_turning;

 void Logic(double d_time);
 void Draw(BITMAP *bmp, double d_time);
 void DrawScore(BITMAP *bmp, double d_time);
};

typedef struct SnakeTarget
{
 int x, y;
 float r;
} SnakeTarget;

extern SnakeTarget target;
extern Snake snake[8];

extern BITMAP *screenbmp;
extern ALFONT_FONT *myfont[8];
extern double time_frequency;
extern double dtime;
#ifdef ALLEGRO_WINDOWS
extern LONGLONG start_t, end_t;
extern double frequency;
#else
extern timeval start_t, end_t;
#endif
extern float fps;
extern float score_size;

extern int scrshot_count;

extern long **scrnbmpln;
extern short **s_scrnbmpln;

int DoMenu();

void GetConfig();
void SaveConfig();

void Reset();

void Intro();

void Screenshot();

extern int num_snakes;

inline void Antialias15()
{
 unsigned long address;
 short **s_scrnbmpln = (short**)screenbmp->line;

 for(int y = 1;y < SCREEN_H - 2;++y)
 {
  address = bmp_write_line(screen, y);
  bmp_select(screen);
  for(int x = 1;x < SCREEN_W - 2;++x)
   if(s_scrnbmpln[y - 1][x] != s_scrnbmpln[y + 1][x] != s_scrnbmpln[y][x - 1] != s_scrnbmpln[y][x + 1] != s_scrnbmpln[y][x])
#define _getr(pixel) _rgb_scale_5[pixel & 0x1F]
#define _getg(pixel) _rgb_scale_5[(pixel >> 5) & 0x1F]
#define _getb(pixel) _rgb_scale_5[(pixel >> 10) & 0x1F]
    bmp_write16(address + (x << 1), (((_getr(s_scrnbmpln[y][x]) + _getr(s_scrnbmpln[y][x + 1]) + _getr(s_scrnbmpln[y][x - 1]) +
                                    _getr(s_scrnbmpln[y + 1][x]) + _getr(s_scrnbmpln[y - 1][x])) / 5) >> 3) |
                                    ((((_getg(s_scrnbmpln[y][x]) + _getg(s_scrnbmpln[y][x + 1]) + _getg(s_scrnbmpln[y][x - 1]) +
                                    _getg(s_scrnbmpln[y + 1][x]) + _getg(s_scrnbmpln[y - 1][x])) / 5) >> 3) << 5) |
                                    ((((_getb(s_scrnbmpln[y][x]) + _getb(s_scrnbmpln[y][x + 1]) + _getb(s_scrnbmpln[y][x - 1]) +
                                    _getb(s_scrnbmpln[y + 1][x]) + _getb(s_scrnbmpln[y - 1][x])) / 5) >> 3) << 10));
   else
    bmp_write16(address + (x << 1), s_scrnbmpln[y][x]);
#undef _getr
#undef _getg
#undef _getb
  bmp_unwrite_line(screen);
 }
}

inline void Antialias16()
{
 unsigned long address;
 short **s_scrnbmpln = (short**)screenbmp->line;

 for(int y = 1;y < SCREEN_H - 2;++y)
 {
  address = bmp_write_line(screen, y);
  bmp_select(screen);
  for(int x = 1;x < SCREEN_W - 2;++x)
   if(s_scrnbmpln[y - 1][x] != s_scrnbmpln[y + 1][x] != s_scrnbmpln[y][x - 1] != s_scrnbmpln[y][x + 1] != s_scrnbmpln[y][x])
#define _getr(pixel) _rgb_scale_5[pixel & 0x1F]
#define _getg(pixel) _rgb_scale_6[(pixel >> 5) & 0x3F]
#define _getb(pixel) _rgb_scale_5[(pixel >> 11) & 0x1F]
    bmp_write16(address + (x << 1), (((_getr(s_scrnbmpln[y][x]) + _getr(s_scrnbmpln[y][x + 1]) + _getr(s_scrnbmpln[y][x - 1]) +
                                    _getr(s_scrnbmpln[y + 1][x]) + _getr(s_scrnbmpln[y - 1][x])) / 5) >> 3) |
                                    ((((_getg(s_scrnbmpln[y][x]) + _getg(s_scrnbmpln[y][x + 1]) + _getg(s_scrnbmpln[y][x - 1]) +
                                    _getg(s_scrnbmpln[y + 1][x]) + _getg(s_scrnbmpln[y - 1][x])) / 5) >> 2) << 5) |
                                    ((((_getb(s_scrnbmpln[y][x]) + _getb(s_scrnbmpln[y][x + 1]) + _getb(s_scrnbmpln[y][x - 1]) +
                                    _getb(s_scrnbmpln[y + 1][x]) + _getb(s_scrnbmpln[y - 1][x])) / 5) >> 3) << 11));
   else
    bmp_write16(address + (x << 1), s_scrnbmpln[y][x]);
#undef _getr
#undef _getg
#undef _getb
  bmp_unwrite_line(screen);
 }
}

inline void Antialias32()
{
 unsigned long address;
 long **scrnbmpln = (long**)screenbmp->line;

 for(int y = 1;y < SCREEN_H - 2;++y)
 {
  address = bmp_write_line(screen, y);
  bmp_select(screen);
  for(int x = 1;x < SCREEN_W - 2;++x)
   if(scrnbmpln[y - 1][x] != scrnbmpln[y + 1][x] != scrnbmpln[y][x - 1] != scrnbmpln[y][x + 1] != scrnbmpln[y][x])
#define _getr(pixel) (pixel & 0xFF)
#define _getg(pixel) ((pixel >> 8) & 0xFF)
#define _getb(pixel) ((pixel >> 16) & 0xFF)
    bmp_write32(address + (x << 2), ((_getr(scrnbmpln[y][x]) + _getr(scrnbmpln[y][x + 1]) + _getr(scrnbmpln[y][x - 1]) +
                                    _getr(scrnbmpln[y - 1][x]) + _getr(scrnbmpln[y + 1][x])) / 5) |
                                    (((_getg(scrnbmpln[y][x]) + _getg(scrnbmpln[y][x + 1]) + _getg(scrnbmpln[y][x - 1]) +
                                    _getg(scrnbmpln[y - 1][x]) + _getg(scrnbmpln[y + 1][x])) / 5) << 8) |
                                    (((_getb(scrnbmpln[y][x]) + _getb(scrnbmpln[y][x + 1]) + _getb(scrnbmpln[y][x - 1]) +
                                    _getb(scrnbmpln[y - 1][x]) + _getb(scrnbmpln[y + 1][x])) / 5) << 16));
   else
    bmp_write32(address + (x << 2), scrnbmpln[y][x]);
#undef _getr
#undef _getg
#undef _getb

  bmp_unwrite_line(screen);
 }
}

inline void AntialiasScreenBuffer15()
{
 unsigned long address;
 short **s_scrnbmpln = (short**)screenbmp->line;

 for(int y = 1;y < SCREEN_H - 2;++y)
 {
  address = bmp_write_line(screenbmp, y);
  bmp_select(screenbmp);
  for(int x = 1;x < SCREEN_W - 2;++x)
   if(s_scrnbmpln[y - 1][x] != s_scrnbmpln[y + 1][x] != s_scrnbmpln[y][x - 1] != s_scrnbmpln[y][x + 1] != s_scrnbmpln[y][x])
#define _getr(pixel) _rgb_scale_5[pixel & 0x1F]
#define _getg(pixel) _rgb_scale_5[(pixel >> 5) & 0x1F]
#define _getb(pixel) _rgb_scale_5[(pixel >> 10) & 0x1F]
    bmp_write16(address + (x << 1), (((_getr(s_scrnbmpln[y][x]) + _getr(s_scrnbmpln[y][x + 1]) + _getr(s_scrnbmpln[y][x - 1]) +
                                    _getr(s_scrnbmpln[y + 1][x]) + _getr(s_scrnbmpln[y - 1][x])) / 5) >> 3) |
                                    ((((_getg(s_scrnbmpln[y][x]) + _getg(s_scrnbmpln[y][x + 1]) + _getg(s_scrnbmpln[y][x - 1]) +
                                    _getg(s_scrnbmpln[y + 1][x]) + _getg(s_scrnbmpln[y - 1][x])) / 5) >> 3) << 5) |
                                    ((((_getb(s_scrnbmpln[y][x]) + _getb(s_scrnbmpln[y][x + 1]) + _getb(s_scrnbmpln[y][x - 1]) +
                                    _getb(s_scrnbmpln[y + 1][x]) + _getb(s_scrnbmpln[y - 1][x])) / 5) >> 3) << 10));
#undef _getr
#undef _getg
#undef _getb
  bmp_unwrite_line(screenbmp);
 }
}

inline void AntialiasScreenBuffer16()
{
 unsigned long address;
 short **s_scrnbmpln = (short**)screenbmp->line;

 for(int y = 1;y < SCREEN_H - 2;++y)
 {
  address = bmp_write_line(screenbmp, y);
  bmp_select(screenbmp);
  for(int x = 1;x < SCREEN_W - 2;++x)
   if(s_scrnbmpln[y - 1][x] != s_scrnbmpln[y + 1][x] != s_scrnbmpln[y][x - 1] != s_scrnbmpln[y][x + 1] != s_scrnbmpln[y][x])
#define _getr(pixel) _rgb_scale_5[pixel & 0x1F]
#define _getg(pixel) _rgb_scale_6[(pixel >> 5) & 0x3F]
#define _getb(pixel) _rgb_scale_5[(pixel >> 11) & 0x1F]
    bmp_write16(address + (x << 1), (((_getr(s_scrnbmpln[y][x]) + _getr(s_scrnbmpln[y][x + 1]) + _getr(s_scrnbmpln[y][x - 1]) +
                                    _getr(s_scrnbmpln[y + 1][x]) + _getr(s_scrnbmpln[y - 1][x])) / 5) >> 3) |
                                    ((((_getg(s_scrnbmpln[y][x]) + _getg(s_scrnbmpln[y][x + 1]) + _getg(s_scrnbmpln[y][x - 1]) +
                                    _getg(s_scrnbmpln[y + 1][x]) + _getg(s_scrnbmpln[y - 1][x])) / 5) >> 2) << 5) |
                                    ((((_getb(s_scrnbmpln[y][x]) + _getb(s_scrnbmpln[y][x + 1]) + _getb(s_scrnbmpln[y][x - 1]) +
                                    _getb(s_scrnbmpln[y + 1][x]) + _getb(s_scrnbmpln[y - 1][x])) / 5) >> 3) << 11));
#undef _getr
#undef _getg
#undef _getb
  bmp_unwrite_line(screenbmp);
 }
}

inline void AntialiasScreenBuffer32()
{
 long **scrnbmpln = (long**)screenbmp->line;

 for(int y = 1;y < SCREEN_H - 2;++y)
 {
  for(int x = 1;x < SCREEN_W - 2;++x)
   if(scrnbmpln[y - 1][x] != scrnbmpln[y + 1][x] != scrnbmpln[y][x - 1] != scrnbmpln[y][x + 1] != scrnbmpln[y][x])
#define _getr(pixel) (pixel & 0xFF)
#define _getg(pixel) ((pixel >> 8) & 0xFF)
#define _getb(pixel) ((pixel >> 16) & 0xFF)
    scrnbmpln[y][x] = ((_getr(scrnbmpln[y][x]) + _getr(scrnbmpln[y][x + 1]) + _getr(scrnbmpln[y][x - 1]) +
                                    _getr(scrnbmpln[y - 1][x]) + _getr(scrnbmpln[y + 1][x])) / 5) |
                                    (((_getg(scrnbmpln[y][x]) + _getg(scrnbmpln[y][x + 1]) + _getg(scrnbmpln[y][x - 1]) +
                                    _getg(scrnbmpln[y - 1][x]) + _getg(scrnbmpln[y + 1][x])) / 5) << 8) |
                                    (((_getb(scrnbmpln[y][x]) + _getb(scrnbmpln[y][x + 1]) + _getb(scrnbmpln[y][x - 1]) +
                                    _getb(scrnbmpln[y - 1][x]) + _getb(scrnbmpln[y + 1][x])) / 5) << 16);
#undef _getr
#undef _getg
#undef _getb
 }
}
