/* XRACER (C) 1999-2000 Richard W.M. Jones <rich@annexia.org> and other AUTHORS
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * $Id: panel.c,v 1.2 2000/01/09 15:26:37 rich Exp $
 */

#include "config.h"

#include <stdio.h>

#include <GL/gl.h>

#include "xracer.h"
#include "xracer-panel.h"
#include "xracer-player.h"
#include "xracer-log.h"
#include "xracer-text.h"
#include "xracer-lcd.h"

/* Fonts used for lap and rank and other things. */
static void *font = 0, *font_large = 0;

/* Game-mode specific variables. */
static int mode = 0, nr_laps = 0;

/* For the thrust- and shieldometers these are the
 * size parameters:
 *      A       B         C
 *                   __________
 *               ----          |
 * H            /              | I
 *             /            ___|
 *    _____----           --
 *   |                   /       J
 * G |                  /
 *   |_______________---
 *           F           E    D
 * Note that A + B + C = D + E + F and that G + H = I + J.
 * Parameter K is the width of each bar, and parameter
 * L is the separation between each bar. n * (K+L) = A + B + C
 */

struct meter_param
{
  int a, b, c, d, e, f, g, h, i, j, k, l;

  /* The following fields are filled in by meter_init. */
  int width, height;
  int nr_bars;
  int *top_line;                /* METER_WIDTH elements */
  int *bottom_line;             /* METER_WIDTH elements */
  GLfloat *bar_colour;          /* 4*(METER_WIDTH+1) elements */
  GLfloat *bar_fill_colour;     /* 4*(METER_WIDTH+1) elements */
};

static struct meter_param thrust_meter = {
  20, 70, 30, 10, 40, 70,
  20, 80, 15, 85,
  10, 5
};

static struct meter_param shield_meter = {
  20, 70, 30, 10, 40, 70,
  -10, -40, -12, -38,
  10, 5
};

/* Initialize meter structures. */
static void
meter_init (struct meter_param *meter)
{
  int x;

  meter->width = meter->a + meter->b + meter->c;
  meter->height = meter->g + meter->h;
  meter->nr_bars = meter->width / (meter->k + meter->l);
  meter->top_line = xmalloc (sizeof (int) * meter->width);
  meter->bottom_line = xmalloc (sizeof (int) * meter->width);
  meter->bar_colour = xmalloc (4 * sizeof (GLfloat) * (meter->width+1));
  meter->bar_fill_colour = xmalloc (4 * sizeof (GLfloat) * (meter->width+1));

  for (x = 0; x < meter->a; ++x)
    meter->top_line[x] = meter->g;

  for (x = meter->a; x < meter->a + meter->b; ++x)
    meter->top_line[x] =
      meter->g +
      (1 + sin (- M_PI/2 + M_PI * (x - meter->a) / (GLfloat) meter->b))
      * (meter->h/2.);

  for (x = meter->a + meter->b; x < meter->width; ++x)
    meter->top_line[x] = meter->height;

  for (x = 0; x < meter->f; ++x)
    meter->bottom_line[x] = 0;

  for (x = meter->f; x < meter->f + meter->e; ++x)
    meter->bottom_line[x] =
      (1 + sin (- M_PI/2 + M_PI * (x - meter->f) / (GLfloat) meter->e))
      * (meter->j/2.);

  for (x = meter->f + meter->e; x < meter->width; ++x)
    meter->bottom_line[x] = meter->j;

  for (x = 0; x <= meter->width; ++x)
    {
      meter->bar_colour[x*4] = 1;
      meter->bar_colour[x*4+1] = 0;
      meter->bar_colour[x*4+2] = 1;
      meter->bar_colour[x*4+3] = 1;
      meter->bar_fill_colour[x*4] = 1;
      meter->bar_fill_colour[x*4+1] = (GLfloat) x / meter->width;
      meter->bar_fill_colour[x*4+2] = ((GLfloat) x / meter->width)
                                    * ((GLfloat) x / meter->width);
      meter->bar_fill_colour[x*4+3] = 0.8;
    }
}

static void
fill_incomplete_bar (struct meter_param *meter, int i, int w, int x, int y)
{
  glBegin (GL_POLYGON);
  glColor4fv (&meter->bar_fill_colour[i*4]);
  glVertex2i (x + i, y + meter->bottom_line[i]);
  glColor4fv (&meter->bar_fill_colour[(i + w - 1)*4]);
  glVertex2i (x + i + w - 1, y + meter->bottom_line[i + w - 1]);
  glColor4fv (&meter->bar_fill_colour[(i + w - 1)*4]);
  glVertex2i (x + i + w - 1, y + meter->top_line[i + w - 1]);
  glColor4fv (&meter->bar_fill_colour[i*4]);
  glVertex2i (x + i, y + meter->top_line[i]);
  glEnd ();
}

static void
fill_bar (struct meter_param *meter, int i, int x, int y)
{
  fill_incomplete_bar (meter, i, meter->k, x, y);
}

static void
outline_bar (struct meter_param *meter, int i, int x, int y)
{
  glBegin (GL_LINE_LOOP);
  glColor4fv (&meter->bar_colour[i*4]);
  glVertex2i (x + i, y + meter->bottom_line[i]);
  glColor4fv (&meter->bar_colour[(i + meter->k - 1)*4]);
  glVertex2i (x + i + meter->k - 1, y + meter->bottom_line[i + meter->k - 1]);
  glColor4fv (&meter->bar_colour[(i + meter->k - 1)*4]);
  glVertex2i (x + i + meter->k - 1, y + meter->top_line[i + meter->k - 1]);
  glColor4fv (&meter->bar_colour[i*4]);
  glVertex2i (x + i, y + meter->top_line[i]);
  glEnd ();
}

/* Draw the generic meter. */
static void
draw_meter (struct meter_param *meter, GLfloat v, int x, int y)
{
  int i, vi = v * meter->width;

  /* XXX We should be able to make much more use of
   * display lists in this function.
   */

  glShadeModel (GL_SMOOTH);

  for (i = 0; i < vi; i += meter->k + meter->l)
    {
      if (i + meter->k + meter->l < vi)
        fill_bar (meter, i, x, y);
      else
        fill_incomplete_bar (meter,
                             i,
                             (GLfloat) meter->k * (vi - i)
                             / (GLfloat) (meter->k + meter->l),
                             x, y);
    }

  for (i = 0; i < meter->width; i += meter->k + meter->l)
    outline_bar (meter, i, x, y);

  glShadeModel (GL_FLAT);
}

/* Draw the thrust meter. */
static inline void
thrustometer (const struct xrPlayer *player)
{
  GLfloat v = player->thrust / player->craft->max_thrust;

  draw_meter (&thrust_meter, v, xrWidth - 140, 150);
}

/* Draw the shield meter. */
static inline void
shieldometer (const struct xrPlayer *player)
{
  draw_meter (&shield_meter, player->shield, xrWidth - 140, 130);
}

/* Draw the speedometer. */
static inline void
speedometer (const struct xrPlayer *player)
{
  int speed;

  speed = xrPlayerGetSpeed (player);

  xrLCDPrintf (xrWidth - 200, 16, 48,
	       "%3d.%d",
	       speed/10, speed%10);
}

/* Draw the lapometer. */
static inline void
lapometer (const struct xrPlayer *player)
{
  static char buffer[64];
  int x = xrWidth - 32, y = 32;

  snprintf (buffer, sizeof buffer,
	    "Lap %d of %d",
	    xrPlayerGetCurrentLap (player), nr_laps);

  x -= xrTextGetWidth (font, buffer);

  xrTextPuts (font, buffer, x, y);
}

/* Draw the rankometer. */
static inline void
rankometer (const struct xrPlayer *player)
{
  xrLogNotImpl ();
}

/* Convert time to printable format. */
static void
printable_time (char *buffer, int buffer_size, double t)
{
  int n;

  if (t < 0) t = 0; else if (t > 999.9) t = 999.9;
  n = t * 10;

  snprintf (buffer, buffer_size, "%03u.%u", n/10, n%10);
}

/* Draw the time display. */
static inline void
timeometer (const struct xrPlayer *player)
{
  static char buffer[64];
  int i;

  /* Draw the current lap time. */
  printable_time (buffer, sizeof buffer, xrPlayerGetCurrentLapTime (player));
  xrLCDDrawString (buffer, 16, 16, 48);

  /* Draw the other lap times. */
  for (i = 1; i < xrPlayerGetCurrentLap (player); ++i)
    {
      double lap_time = xrPlayerGetLapTime (player, i);

      printable_time (buffer, sizeof buffer, lap_time);
      xrTextPrintf (font, 16, xrHeight - (80 + (nr_laps - i) * 16),
		    "Lap %d: %s", i, buffer);
    }
}

/* Program-level initializations. */
void
xrPanelInit ()
{
  meter_init (&thrust_meter);
  meter_init (&shield_meter);

  font = xrTextFindFont ("crillee", 14);
  xrLogAssert (font != 0);

  font_large = xrTextFindFont ("crillee", 48);
  xrLogAssert (font_large != 0);
}

/* This function is called when the game starts. */
void
xrPanelStartGame (int _mode, int _nr_laps)
{
  mode = _mode;
  nr_laps = _nr_laps;
}

/* This function is called to do clean-up when the game ends. */
void
xrPanelEndGame ()
{
}

/* This function is called once per frame to display the panel. */
void
xrPanelDisplay (const struct xrPlayer *player)
{
  GLfloat saved_color[4];

  glGetFloatv (GL_CURRENT_COLOR, saved_color);
  
  thrustometer (player);
  shieldometer (player);
  speedometer (player);
  lapometer (player);
  if (mode != XR_GAME_MODE_TIME_TRIAL) rankometer (player);
  timeometer (player);

  glColor3fv (saved_color);
}
