/*==========================================================
  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_PORTCVAR_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_listcvarcvar = 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_listcvarcvar = temp)
  {
    temp = cvar->next;

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

cvar_t *alias_new(char *keychar *valueint flags)
{
  cvar_t *cvar;

  if(!(cvar = cvar_create(keyvalueflags)))
    return 0;

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

cvar_t *cvar_new(char *keychar *valueint flags)
{
  cvar_t *cvar;

  if(!(cvar = cvar_create(keyvalueflags)))
    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 *keychar *valueint 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\')"keyvalue);

  return cvar;
}

void cvar_destroy(cvar_t *cvar)
{
  if(cvar)
  {
    if(cvar_cvardebug && atoi(cvar_cvardebug->value))
      CON_printf("Destroying cvar: (\'%s\' = \'%s\')"cvar->keycvar->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->keycvar->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 *cvarcvar_t **list)
{
  if(cvar)
  {
    cvar->flags |= CVAR_LISTED;

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

void cvar_remove(cvar_t *cvarcvar_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_listcc = c->next)
    if(!stricmp(keyc->key))
      return c;
  return 0;
}

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

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

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

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

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

  I_halfChop(argumentskeyvalue);

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

  if(!(cvar = cvar_findKey(key)))
  {
    cvar = cvar_new(keyvalue0);
    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(argumentskeyvalue);

  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 *commandchar *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->keyvalue ? "enabled" : "disabled");
    }
    else
      CON_printf("\'%s\' == \'%s\'"cvar->keycvar->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 = 0c = gc_cvar_listcc = c->nexti++)
    CON_printf("\'%s\' == \'%s\'"c->keyc->value);
  CON_printf("%d console variables"i);
}

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

  I_halfChop(argumentskeyvalue);

  cvar = alias_findKey(key);

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

  if(!cvar)
  {
    cvar = alias_new(keyvalue0);
    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(argumentskeyvalue);

  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 *commandchar *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 = 0c = gc_alias_listcc = c->nexti++)
    CON_printf("\'%s\' == \'%s\'"c->keyc->value);
  CON_printf("%d aliases"i);
}