/*==========================================================
  File:  cvar.c
  Author:  _pragma

  Description:  Handles console variables and aliases.
  ==========================================================*/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <GL/gl.h>
#include <SDL/SDL.h>

#include "cvar.h"
#include "util.h"
#include "interp.h"
#include "commands.h"
#include "texture.h"
#include "console.h"
#include "bsp.h"
#include "ms3d.h"
#include "model.h"
#include "entity.h"
#include "server.h"

       /** List of console variables **/

cvar_t *gc_cvar_list = 0, 

       /** Rendering **/

       *cvar_r_maxfps = 0, 
       *cvar_r_fov = 0,

       /** Physics **/

       *cvar_friction = 0,
       *cvar_gravity = 0,

       /** Movement **/

       *cvar_moveSpeed = 0,
       *cvar_maxSpeed = 0,
       *cvar_moveForward = 0, 
       *cvar_moveBack = 0, 
       *cvar_moveLeft = 0, 
       *cvar_moveRight = 0, 
       *cvar_moveUp = 0, 
       *cvar_moveDown = 0, 
       *cvar_noclip = 0,

       /** keyboard view **/

       *cvar_lookLeft = 0,
       *cvar_lookRight = 0,
       *cvar_lookUp = 0,
       *cvar_lookDown = 0,
       *cvar_yawSpeed = 0,
       *cvar_pitchSpeed = 0,

       /** Console **/

       *cvar_paging_size = 0,
       *cvar_con_rottime = 0,
       *cvar_con_index_screen = 0, 

       /** mouse view **/

       *cvar_myawSpeed = 0,
       *cvar_mpitchSpeed = 0,
       *cvar_msensitivity = 0,
       *cvar_thirdperson = 0,

       /** Debug messages **/

       *cvar_framedebug = 0,
       *cvar_cvardebug = 0, 
       *cvar_cameradebug = 0,
       *cvar_physicsdebug = 0,
       *cvar_movedebug = 0,
       *cvar_condebug = 0,

       /** Visual Debug **/

       *cvar_renderBoundingSphere,

       /** Informative messages **/

       *cvar_showfps = 0,
       *cvar_showtime = 0,

       /** Server variables **/

       *cvar_serverPort = 0;

cvar_t *gc_alias_list = 0;

#define CVAR_INIT(c, k, v, f) if(!( (c) = cvar_new((k), (v),(f)) )) return false
#define ALIAS_INIT(c, k, v, f) if(!( (c) = alias_new((k), (v),(f)) )) return false

int cvar_init(void)
{
  CVAR_INIT(cvar_condebug,    "con_debug",     "0",   CVAR_PRIVATE | CVAR_TOGGLE);
  CVAR_INIT(cvar_cvardebug,   "cvar_debug",    "0",   CVAR_PRIVATE | CVAR_TOGGLE);
  CVAR_INIT(cvar_framedebug,  "frame_debug",   "0",   CVAR_PRIVATE | CVAR_TOGGLE);
  CVAR_INIT(cvar_cameradebug, "cam_debug",     "0",   CVAR_PRIVATE | CVAR_TOGGLE);
  CVAR_INIT(cvar_physicsdebug,"physics_debug", "0",   CVAR_PRIVATE | CVAR_TOGGLE);
  CVAR_INIT(cvar_movedebug,   "move_debug",    "0",   CVAR_PRIVATE | CVAR_TOGGLE);

  CVAR_INIT(cvar_renderBoundingSphere,   "d_showsphere",    "0",   CVAR_PRIVATE | CVAR_TOGGLE);

  CVAR_INIT(cvar_r_maxfps,     "r_maxfps",   "120",  CVAR_PRIVATE);
  CVAR_INIT(cvar_r_fov,        "r_fov",      "70.0", CVAR_PRIVATE);
  CVAR_INIT(cvar_showtime,     "showtime",   "0",    CVAR_PRIVATE | CVAR_TOGGLE);
  CVAR_INIT(cvar_showfps,      "r_drawfps",  "0",    CVAR_PRIVATE | CVAR_TOGGLE);

  /** Physics **/

  CVAR_INIT(cvar_gravity,      "p_gravity",    "-9.8", CVAR_PRIVATE);
  CVAR_INIT(cvar_friction,     "p_friction",   "0.85",  CVAR_PRIVATE);

  /** Movement **/

  CVAR_INIT(cvar_moveSpeed,   "movespeed",   "75", CVAR_PRIVATE);
  CVAR_INIT(cvar_maxSpeed,    "maxspeed",   "600", CVAR_PRIVATE);
  CVAR_INIT(cvar_moveForward, "moveforward", "0", CVAR_PRIVATE);
  CVAR_INIT(cvar_moveBack,    "moveback",    "0", CVAR_PRIVATE);
  CVAR_INIT(cvar_moveLeft,    "moveleft",    "0", CVAR_PRIVATE);
  CVAR_INIT(cvar_moveRight,   "moveright",   "0", CVAR_PRIVATE);
  CVAR_INIT(cvar_moveUp,      "moveup",      "0", CVAR_PRIVATE);
  CVAR_INIT(cvar_moveDown,    "movedown",    "0", CVAR_PRIVATE);
  CVAR_INIT(cvar_noclip,      "noclip",      "0", CVAR_PRIVATE | CVAR_TOGGLE);

  /** Console **/

  CVAR_INIT(cvar_paging_size,      "paging_size", "5",    CVAR_PRIVATE);
  CVAR_INIT(cvar_con_rottime,      "con_rottime", "2000", CVAR_PRIVATE);
  CVAR_INIT(cvar_con_index_screen, "con_screen",  "8",    CVAR_PRIVATE);

  /** Server **/

  CVAR_INIT(cvar_serverPort,   "sv_port", SERVER_DEFAULT_PORT, CVAR_PRIVATE);

  /** View **/

  CVAR_INIT(cvar_lookLeft,     "lookleft",     "0",     CVAR_PRIVATE);
  CVAR_INIT(cvar_lookRight,    "lookright",    "0",     CVAR_PRIVATE);
  CVAR_INIT(cvar_yawSpeed,     "kb_yawspeed",  "0.035", CVAR_PRIVATE);

  CVAR_INIT(cvar_lookUp,       "lookup",         "0",     CVAR_PRIVATE);
  CVAR_INIT(cvar_lookDown,     "lookdown",       "0",     CVAR_PRIVATE);
  CVAR_INIT(cvar_pitchSpeed,   "kb_pitchspeed",  "0.035", CVAR_PRIVATE);

  CVAR_INIT(cvar_mpitchSpeed,   "m_pitchspeed",  "0.035", CVAR_PRIVATE);
  CVAR_INIT(cvar_myawSpeed,     "m_yawspeed",    "0.035", CVAR_PRIVATE);
  CVAR_INIT(cvar_msensitivity,  "m_sensitivity", "3",     CVAR_PRIVATE);
  CVAR_INIT(cvar_thirdperson,   "c_thirdperson", "0",     CVAR_PRIVATE | CVAR_TOGGLE);

  return true;
}

int alias_init(void)
{
  cvar_t *alias;

  /** Movement **/

  ALIAS_INIT(alias,  "+forward",   "moveforward 1", CVAR_PRIVATE);
  ALIAS_INIT(alias,  "-forward",   "moveforward 0", CVAR_PRIVATE);
  ALIAS_INIT(alias,  "+back",      "moveback 1",    CVAR_PRIVATE);
  ALIAS_INIT(alias,  "-back",      "moveback 0",    CVAR_PRIVATE);
  ALIAS_INIT(alias,  "+moveleft",  "moveleft 1",    CVAR_PRIVATE);
  ALIAS_INIT(alias,  "-moveleft",  "moveleft 0",    CVAR_PRIVATE);
  ALIAS_INIT(alias,  "+moveright", "moveright 1",   CVAR_PRIVATE);
  ALIAS_INIT(alias,  "-moveright", "moveright 0",   CVAR_PRIVATE);
  ALIAS_INIT(alias,  "+moveup",    "moveup 1",      CVAR_PRIVATE);
  ALIAS_INIT(alias,  "-moveup",    "moveup 0",      CVAR_PRIVATE);
  ALIAS_INIT(alias,  "+movedown",  "movedown 1",    CVAR_PRIVATE);
  ALIAS_INIT(alias,  "-movedown",  "movedown 0",    CVAR_PRIVATE);

  /** View **/
 
  ALIAS_INIT(alias,  "+lookright", "lookright 1",    CVAR_PRIVATE);
  ALIAS_INIT(alias,  "-lookright", "lookright 0",    CVAR_PRIVATE);
  ALIAS_INIT(alias,  "+lookleft",  "lookleft 1",     CVAR_PRIVATE);
  ALIAS_INIT(alias,  "-lookleft",  "lookleft 0",     CVAR_PRIVATE);

  ALIAS_INIT(alias,  "+lookup",    "lookup 1",       CVAR_PRIVATE);
  ALIAS_INIT(alias,  "-lookup",    "lookup 0",       CVAR_PRIVATE);
  ALIAS_INIT(alias,  "+lookdown",  "lookdown 1",     CVAR_PRIVATE);
  ALIAS_INIT(alias,  "-lookdown",  "lookdown 0",     CVAR_PRIVATE);

  return true;
}

void cvar_destroyCvars(void)
{
  cvar_t *cvar, *temp;

  for(cvar = gc_cvar_list; cvar; cvar = temp)
  {
    temp = cvar->next;

    if(cvar->key)
      free(cvar->key);
    if(cvar->value)
      free(cvar->value);
    free(cvar);
  }
}

void cvar_destroyAliases(void)
{
  cvar_t *cvar, *temp;

  for(cvar = gc_alias_list; cvar; cvar = temp)
  {
    temp = cvar->next;

    if(cvar->key)
      free(cvar->key);
    if(cvar->value)
      free(cvar->value);
    free(cvar);
  }
}

cvar_t *alias_new(char *key, char *value, int flags)
{
  cvar_t *cvar;

  if(!(cvar = cvar_create(key, value, flags)))
    return 0;

  cvar_add(cvar, &gc_alias_list);
  return cvar;
}

cvar_t *cvar_new(char *key, char *value, int flags)
{
  cvar_t *cvar;

  if(!(cvar = cvar_create(key, value, flags)))
    return 0;

  cvar_add(cvar, &gc_cvar_list);

  //printf("cvar: %s, %s, %d\n", cvar->key, cvar->value, cvar->flags);

  return cvar;
}

cvar_t *cvar_create(char *key, char *value, int flags)
{
  cvar_t *cvar;

  if(!(cvar = malloc(sizeof(cvar_t))))
  {
    perror("cvar_create");
    return 0;
  }

  cvar->key = strdup(key);
  cvar->value = strdup(value);

  cvar->next = 0;
  cvar->flags = flags;

  if(cvar_cvardebug && atoi(cvar_cvardebug->value))
    CON_printf("New cvar: (\'%s\' = \'%s\')", key, value);

  return cvar;
}

void cvar_destroy(cvar_t *cvar)
{
  if(cvar)
  {
    if(cvar_cvardebug && atoi(cvar_cvardebug->value))
      CON_printf("Destroying cvar: (\'%s\' = \'%s\')", cvar->key, cvar->value);

    if(cvar->key)
      free(cvar->key);
    if(cvar->value)
      free(cvar->value);
 
    if(cvar->flags & CVAR_LISTED)
      cvar_remove(cvar, &gc_cvar_list);

    free(cvar);
    cvar = 0;
  }
}

void alias_destroy(cvar_t *cvar)
{
  if(cvar)
  {
    if(cvar_cvardebug && atoi(cvar_cvardebug->value))
      CON_printf("Destroying alias: (\'%s\' = \'%s\')", cvar->key, cvar->value);

    if(cvar->key)
      free(cvar->key);
    if(cvar->value)
      free(cvar->value);
 
    if(cvar->flags & CVAR_LISTED)
      cvar_remove(cvar, &gc_alias_list);

    free(cvar);
    cvar = 0;
  }
}

void cvar_add(cvar_t *cvar, cvar_t **list)
{
  if(cvar)
  {
    cvar->flags |= CVAR_LISTED;

    cvar->next = *list;
    *list = cvar;
  }
}

void cvar_remove(cvar_t *cvar, cvar_t **list)
{
  cvar_t *temp;

  if(cvar == *list) 
    *list = cvar->next;
  else 
  { 
    temp = *list; 
    
    while (temp && (temp->next != (cvar))) 
      temp = temp->next; 

    if (temp) 
      temp->next = cvar->next; 
  }
}

cvar_t *cvar_findKey(char *key)
{
  cvar_t *c;

  for(c = gc_cvar_list; c; c = c->next)
    if(!stricmp(key, c->key))
      return c;
  return 0;
}

cvar_t *alias_findKey(char *key)
{
  cvar_t *c;

  for(c = gc_alias_list; c; c = c->next)
    if(!stricmp(key, c->key))
      return c;
  return 0;
}

cvar_t *cvar_findValue(void *value)
{
  cvar_t *c;

  for(c = gc_cvar_list; c; c = c->next)
    if(!stricmp(value, c->value))
      return c;
  return 0;
}

COMMAND(CMD_set)
{
  char key[CON_BCS], value[CON_BCS];
  cvar_t *cvar;

  I_halfChop(arguments, key, value);

  if(!*value)
  {
    CON_printf("Usage: set <variable> <value>");
    return;
  }

  if(!(cvar = cvar_findKey(key)))
  {
    cvar = cvar_new(key, value, 0);
    return;
  }

  if(cvar->flags & CVAR_LOCKED)
  {
    CON_printf("This variable cannot be modified.");
    return;
  }

  if(cvar->value)
    free(cvar->value);

  cvar->value = strdup(value);
}

COMMAND(CMD_unset)
{
  char key[CON_BCS], value[CON_BCS];
  cvar_t *cvar;

  I_halfChop(arguments, key, value);

  if(!(cvar = cvar_findKey(key)))
  {
    CON_printf("No such variable.");
    return;
  }

  if(cvar->flags & CVAR_PRIVATE)
  {
    CON_printf("This variable cannot be unset.");
    return;
  }

  if(cvar->flags & CVAR_LOCKED)
  {
    CON_printf("This variable cannot be modified.");
    return;
  }

  cvar_destroy(cvar);
}

int cvar_parseCommand(char *command, char *arguments)
{
  cvar_t *cvar;
  int value;

  if(!(cvar = cvar_findKey(command)))
    return false;

  if(!*arguments)
  {
    if((cvar->flags & CVAR_TOGGLE) && !(cvar->flags & CVAR_LOCKED))
    {
      value = !atoi(cvar->value);
      sprintf(cvar->value, "%d", value);
      CON_printf("\'%s\' %s.", cvar->key, value ? "enabled" : "disabled");
    }
    else
      CON_printf("\'%s\' == \'%s\'", cvar->key, cvar->value);

    return true;
  }
    
  if(cvar->flags & CVAR_LOCKED)
  {
    CON_printf("This variable cannot be modified.");
   return true;
  }

  if(cvar->value)
    free(cvar->value);

  cvar->value = strdup(arguments);
  return true;
}

COMMAND(CMD_cvars)
{
  cvar_t *c;
  int i;

  for(i = 0, c = gc_cvar_list; c; c = c->next, i++)
    CON_printf("\'%s\' == \'%s\'", c->key, c->value);
  CON_printf("%d console variables", i);
}

COMMAND(CMD_alias)
{
  char key[CON_BCS], value[CON_BCS];
  cvar_t *cvar;

  I_halfChop(arguments, key, value);

  cvar = alias_findKey(key);

  if(!*value)
  {
    if(!cvar)
      CON_printf("Usage: alias <alias> <command>");
    else
      CON_printf("\'%s\' == \'%s\'", cvar->key, cvar->value);
    return;
  }

  if(!cvar)
  {
    cvar = alias_new(key, value, 0);
    return;
  }

  if(cvar->flags & CVAR_LOCKED)
  {
    CON_printf("This alias cannot be modified.");
    return;
  }

  if(cvar->value)
    free(cvar->value);

  cvar->value = strdup(value);
}

COMMAND(CMD_unalias)
{
  char key[CON_BCS], value[CON_BCS];
  cvar_t *cvar;

  I_halfChop(arguments, key, value);

  if(!(cvar = alias_findKey(key)))
  {
    CON_printf("No such alias.");
    return;
  }

  if(cvar->flags & CVAR_PRIVATE)
  {
    CON_printf("This alias cannot be unset.");
    return;
  }

  if(cvar->flags & CVAR_LOCKED)
  {
    CON_printf("This alias cannot be modified.");
    return;
  }

  alias_destroy(cvar);
}

int alias_parseCommand(char *command, char *arguments)
{
  cvar_t *cvar;
//  char buf[CON_BCS];

  if(!(cvar = alias_findKey(command)))
    return false;

  if(!*arguments)
  {
  //  strcpy(buf, cvar->value);
    CON_addCommand(cvar->value);
    return true;
  }
    
  if(cvar->flags & CVAR_LOCKED)
  {
    CON_printf("This variable cannot be modified.");
   return true;
  }

  if(cvar->value)
    free(cvar->value);

  cvar->value = strdup(arguments);
  return true;
}

COMMAND(CMD_aliaslist)
{
  cvar_t *c;
  int i;

  for(i = 0, c = gc_alias_list; c; c = c->next, i++)
    CON_printf("\'%s\' == \'%s\'", c->key, c->value);
  CON_printf("%d aliases", i);
}