/* 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: load_track.c,v 1.2 2000/03/12 12:58:54 rich Exp $
 */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>

#ifdef HAVE_STRING_H
#include <string.h>
#endif

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif

#include <GL/glut.h>

#include "xracer.h"
#include "xracer-mode.h"
#include "xracer-log.h"
#include "xracer-text.h"
#include "xracer-file-browser.h"
#include "xracer-track.h"
#include "xracer-menu.h"

/* The file browser object. */
static void *file_browser;

/* Font details. */
static void *font = 0;
static int height = 0;
static void *small_font = 0;	/* Small font for footnote. */
static int small_height = 0;	/* Height of small font. */

/* Where we are on the screen and what we are displaying. */
static int nr_entries, posn, top, nr_items;

/* Current path. */
static const char *cwd;

static int fb_display (int index, const char *dirname, const char *filename,
		       const struct stat *stat, void *args);
static int fb_filter (const char *dirname, const char *filename,
		      const struct stat *stat);

static struct xrFileBrowserCallbacks callbacks = {
  display: fb_display,
  filter: fb_filter
};

/* Program-level initializations for this mode. */
void
xrLoadTrackModeInit ()
{
  file_browser = xrFileBrowserCreate ("tracks", &callbacks);
  nr_entries = xrFileBrowserGetNrEntries (file_browser);
  cwd = xrFileBrowserGetDirectory (file_browser);

  /* Select a font for drawing menus in. */
  font = xrTextFindFont ("crillee", 24);
  xrLogAssert (font != 0);

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

  height = xrTextGetHeight (font);
  small_height = xrTextGetHeight (small_font);
}

struct fb_display_args
{
  int items_x, items_y;
};

/* Display each filename. */
static int
fb_display (int index, const char *dirname, const char *filename,
	    const struct stat *stat, void *vargs)
{
  struct fb_display_args *args = (struct fb_display_args *) vargs;
  char trackname[256];
  int len = strlen (filename);

  /* File or directory name? */
  if (S_ISDIR (stat->st_mode))
    {
      if (strcmp (filename, ".") == 0)
	{
	  strcpy (trackname, "Cancel");
	}
      else if (strcmp (filename, "..") == 0)
	{
	  strcpy (trackname, "Go to parent directory");
	}
      else
	{
	  strncpy (trackname, filename, sizeof trackname);
	  trackname[sizeof trackname - 1] = '\0';
	}
    }
  else
    {
      /* Extract the track name from the filename. */
      strncpy (trackname, filename + 8, sizeof trackname);
      if (len-11 < sizeof trackname) trackname[len-11] = '\0';
      else trackname[sizeof trackname - 1] = '\0';
    }

  /* Print it. */
  if (top <= index && index <= top + nr_items - 1)
    xrTextPuts (font, trackname,
		args->items_x, args->items_y + (index - top) * height);

  return 0;
}

/* Callback from file browser - filter out only those names of
 * interest.
 */
static int
fb_filter (const char *dirname, const char *filename,
	   const struct stat *stat)
{
  int len = strlen (filename);

  return
    S_ISDIR (stat->st_mode) ||
    (strncmp (filename, "libtrack", 8) == 0 &&
     strncmp (filename+len-3, ".so", 3) == 0);
}

/* This function is called when we enter this mode. */
static void
start_mode (const void *args)
{
  posn = 1;
  top = 0;
}

/* This function is called when we leave this mode, for clean-up. */
static void
end_mode ()
{
}

/* This function is called to redisplay the screen. */
static void
display ()
{
  int width, items_x, items_y;
  struct fb_display_args args;
  static const char footer_text[] = "Arrow keys: move | [Return] select | [Esc] cancel";

  /* Clear display buffers. */
  glClear (GL_COLOR_BUFFER_BIT);

  /* Switch to orthographic projection. */
  glMatrixMode (GL_PROJECTION);
  glPushMatrix ();
  glLoadIdentity ();
  glOrtho (0, (GLdouble) xrWidth, 0, (GLdouble) xrHeight, 0, 1000);
  glMatrixMode (GL_MODELVIEW);
  glPushMatrix ();
  glLoadIdentity ();

  /* Enable alpha blending for text. */
  glEnable (GL_BLEND);
  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  /* Display the current directory. */
  width = xrTextGetWidth (font, cwd);
  xrTextPuts (font, cwd, (xrWidth - width) / 2, xrHeight * 1/5);

  /* How many items can we fit on the screen? */
  items_x = 64;
  items_y = xrHeight * 3/10;
  nr_items = (xrHeight * 4/5 - items_y) / height;

  /* Display them. */
  args.items_x = items_x;
  args.items_y = items_y;
  xrFileBrowserDisplay (file_browser, (void *) &args);

  /* Display the cursor. */
  xrTextPuts (font, ">", items_x - 32, items_y + (posn - top) * height);

  /* Display the footer. */
  width = xrTextGetWidth (small_font, footer_text);
  xrTextPuts (small_font, footer_text,
	      (xrWidth - width) / 2, xrHeight * 9/10);

  glDisable (GL_BLEND);

  /* Restore matrices. */
  glMatrixMode (GL_MODELVIEW);
  glPopMatrix ();
  glMatrixMode (GL_PROJECTION);
  glPopMatrix ();
}

/* Select the current track under the cursor. */
static void
select_item ()
{
  const char *filename;
  const struct stat *stat;
  void *track;
  char path[PATH_MAX];

  /* Get the information about the file. */
  xrFileBrowserGetFileInfo (file_browser, posn, &filename, &stat);

  if (posn == 0)
    {
      xrEnterMenu (&xrStartMenu, 0);
    }
  /* File or directory? */
  else if (S_ISDIR (stat->st_mode))
    {
      if (xrFileBrowserChangeDirectory (file_browser, filename) == -1)
	{
	  xrLog (LOG_ERROR, "cannot change to directory: %s", filename);
	  return;
	}
      nr_entries = xrFileBrowserGetNrEntries (file_browser);
      cwd = xrFileBrowserGetDirectory (file_browser);
      top = 0;
      posn = 1;
    }
  else
    {
      strcpy (path, cwd);
      strcat (path, "/");
      strcat (path, filename);

      /* Try to load the track. */
      track = xrTrackLoadByFilename (path);

      if (track == 0)
	{
	  xrLog (LOG_ERROR, "cannot load track: %s", path);
	  return;
	}

      xrTrackMakeCurrent (track);
      xrEnterMenu (&xrStartMenu, 0);
    }
}

/* Keyboard callback. */
static int
keyboard (unsigned char key, int x, int y)
{
  switch (key)
    {
    case '\n':
    case '\r':
    case ' ':
      select_item ();
      return 1;
    case 27:			/* Escape key. */
      xrEnterMenu (&xrStartMenu, 0);
      return 1;
    }

  return 0;
}

static int
special (int key, int x, int y)
{
  switch (key)
    {
    case GLUT_KEY_UP:
      if (posn > 0)
	{
	  posn--;
	  if (posn < top)
	    {
	      top -= 5;
	      if (top < 0)
		top = 0;
	    }
	}
      return 1;

    case GLUT_KEY_DOWN:
      if (posn < nr_entries-1)
	{
	  posn++;
	  if (posn > top + nr_items - 1)
	    top += 5;
	}
      return 1;

    case GLUT_KEY_PAGE_UP:
      if (posn > 0)
	{
	  posn -= 5;
	  if (posn < 0)
	    posn = 0;
	  if (posn < top)
	    {
	      top -= 5;
	      if (top < 0)
		top = 0;
	    }
	}
      return 1;

    case GLUT_KEY_PAGE_DOWN:
      if (posn < nr_entries-1)
	{
	  posn += 5;
	  if (posn >= nr_entries)
	    posn = nr_entries - 1;
	  if (posn > top + nr_items - 1)
	    top += 5;
	}
      return 1;
    }

  return 0;
}

struct xrMode xrLoadTrackMode = {
  name: "load track mode",
  start_mode: start_mode,
  end_mode: end_mode,
  display: display,
  keyboard: keyboard,
  special: special
};
