/***************************************************************************/
/*  Killer List Of Video-games Database.                                   */
/*  Copyright (C) 1993  John Keay                                          */
/*                                                                         */
/*  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., 675 Mass Ave, Cambridge, MA 02139, USA.              */
/*                                                                         */
/*  For more details see 'COPYING'                                         */
/*  John Keay (keay@tiuk.ti.com)                                           */
/***************************************************************************/

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

#include "proto.h"
#include "parse.h"
#include "game.h"
#include "klovdb.h"

/* Not all libraries contain the strncasecmp function so I've added my own. */
/* This implementation only returns equal/not equal information */
/*   unlike the real strncasecmp */
int my_strncasecmp(s1,s2,n)
char *s1;
char *s2;
unsigned n;
{
  while(n && *s1 && *s2) {
    if(tolower(*s1) != tolower(*s2))
      return(1);
    s1++;
    s2++;
    n--;
  }
  if(!n) return(0);            /* all characters matched */
  if(!*s1 && !*s2) return(0);  /* both strings ended together */
  return(1);                   /* one string ended before the other */
}

condition_t get_condition(s)
char *s;
{ condition_t c = CND_NONE;

  if(s) {
    while(*s && *s != '<' && *s != '>' && *s != '!' && *s != '=')
      s++;
    if(*s == '<') {
      if(s[1] == '=') return(CND_LE);
      if(s[1] == '>') return(CND_NE);
      return(CND_LT);
    } else if(*s == '>') {
      if(s[1] == '=') return(CND_GE);
      return(CND_GT);
    } else if(*s == '!') {
      return(CND_NE);
    } else if(*s == '=') {
      if(s[1] == '>') return(CND_GE);
      return(CND_EQ);
    }
  }
  return(c);
}

int find_number(s)
char *s;
{ int n = -1;
  if(s) {
    while(*s && !isdigit(*s))
      s++;
    if(*s) 
      n = atoi(s);
  }
  return(n);
}

int my_compare(s,k)
char *s;
char *k;
{ condition_t cond;
  int n1,n2;

  if(!k) return(1);  /* Not interested in this field */
  if(!s) return(0);  /* No string to compare to */

  cond = get_condition(k);
  n1 = find_number(s);
  n2 = find_number(k);

  if(cond != CND_NONE && n1 != -1 && n2 != -1) {
    if(n1 != -1 && n2 != -1) {
      switch (cond) {
        case CND_EQ: return(n1 == n2);
        case CND_NE: return(n1 != n2);
        case CND_LT: return(n1 <  n2);
        case CND_LE: return(n1 <= n2);
        case CND_GT: return(n1  > n2);
        case CND_GE: return(n1 >= n2);
        case CND_NONE: break;
      }
    }
  } else {
    int len = strlen(k);
    for(;*s;s++)
      if(!my_strncasecmp(s,k,len))
        return(1);
  }
  return(0);
}

void list_matches(l,k,fp,fn)
list_ptr l;
game_ptr k;
FILE *fp;
print_fn fn;
{ game_ptr g;
  int num_match = 0;
  int num = 0;

  if(!l) return;

  for(g=l->head;g;g=g->next) {
    int match = 1;
    num++;
    if(!my_compare(g->name,k->name))
      match = 0;
    else if(!my_compare(g->date,k->date))
      match = 0;
    else if(!my_compare(g->company,k->company))
      match = 0;
    else if(k->flags && (g->flags & k->flags) != k->flags)
      match = 0;
    else if(!my_compare(g->produced,k->produced))
      match = 0;
    else if(!my_compare(g->players,k->players))
      match = 0;
    else if(!my_compare(g->text,k->text))
      match = 0;
    if(match) {
      num_match++;
      (*fn)(g,fp);
    }
  }
  fprintf(fp,"[  %4d game%s checked    %4d match%s  ]\n",
            num,((num==1)?" ":"s"),
            num_match,((num_match==1)?"  ":"es"));
}

void prompt(p,s,fp)
char *p;
char *s;
FILE *fp;
{ int ch;

  fprintf(stdout,"%s",p);
  ch = fgetc(fp);
  while(ch != '\n') {
    *s++ = ch;
    ch = fgetc(fp);
  }
  *s = '\0';
}

game_ptr get_search_key(fp)
FILE *fp;
{ game_ptr key = (game_ptr) safe_malloc(sizeof(game_t));
  char in[1024];

  key->name     = NULL;
  key->date     = NULL;
  key->company  = NULL;
  key->flags    = 0;
  key->produced = NULL;
  key->players  = NULL;
  key->text     = NULL;

  prompt("Game Name :",in,fp);  if(in[0]) key->name = newstr(in);
  prompt("Date      :",in,fp);  if(in[0]) key->date = newstr(in);
  prompt("Company   :",in,fp);  if(in[0]) key->company = newstr(in);
  prompt("# Players :",in,fp);  if(in[0]) key->players = newstr(in);
  prompt("# Produced:",in,fp);  if(in[0]) key->produced = newstr(in);
  prompt("Text      :",in,fp);  if(in[0]) key->text = newstr(in);
  prompt("Flags? y/n:",in,fp);
  if(tolower(in[0]) == 'y') {
    prompt("  3D      :",in,fp); if(tolower(in[0]) == 'y') key->flags |= F_3D;
    prompt("  BnW     :",in,fp); if(tolower(in[0]) == 'y') key->flags |= F_BNW;
    prompt("  Laser   :",in,fp); if(tolower(in[0]) == 'y') key->flags |= F_LASER;
    prompt("  Vector  :",in,fp); if(tolower(in[0]) == 'y') key->flags |= F_VECTOR;
    prompt("  Pinball :",in,fp); if(tolower(in[0]) == 'y') key->flags |= F_PINBALL;
  }
  return(key);
}

static char *cmd_help[NUM_CMD] = {
  "",
  "",
  "Quits out of program",
  "Search database for entries",
  "Enable full descriptions",
  "Enable brief descriptions",
  "Show this info",
  "Send output to specified file",
  "Read a new game list",
  "Append a new game list to database"
};

static command_t cmds[] = {
  { "?",       C_HELP   },
  { "Append",  C_APPEND },
  { "Brief",   C_BRIEF  },
  { "Exit",    C_QUIT   },
  { "Find",    C_FIND   },
  { "Help",    C_HELP   },
  { "LOG",     C_LOG,   },
  { "LONg",    C_LONG   },
  { "Quit",    C_QUIT   },
  { "Read",    C_READ   },
  { "Search",  C_FIND   }
};

void split_string(s,argc,argv)
char *s;
int *argc;
char *argv[MAX_PARAMS];
{ char *token;
  
  *argc = 0;
  do {
    while(*s == ' ') s++;                  /* skip leading spaces */
    token = s;
    while(*s && *s!='\n' && *s!=' ') s++;  /* skip to end of token */
    if(token != s) {
      if(*s) *s++ = '\0';
      argv[*argc] = token;
      (*argc)++;
    }  
  } while (token != s && *argc < MAX_PARAMS);
}

type_t check_commands(cmd,margc,margv)
char *cmd;
int  *margc;
char *margv[MAX_PARAMS];
{ type_t cmd_type = C_NONE;
  int match = 0;
  int i;

  split_string(cmd,margc,margv);

  if(*margc) {
    int len = strlen(margv[0]);
    for(i=0;i<sizeof(cmds)/sizeof(command_t);i++) {
      if(!my_strncasecmp(cmds[i].cmd,margv[0],len)) {
        cmd_type = cmds[i].type;
        match++;       
      }                
    }                  
    if(match > 1)      
      cmd_type = C_AMB;
  }
  return(cmd_type);
}    

int main(argc,argv)
int argc;
char *argv[];
{ list_ptr l;
  char cmd[1024];
  int brief = 1;
  int i;
  type_t cmd_type;
  FILE *log = stdout;

  l = list_create();
  for(i=1;i<argc;i++)
    list_append(l,argv[i]);

  do {
    int i;
    game_ptr key;
    int margc;
    char *margv[MAX_PARAMS];

    prompt(">",cmd,stdin);
    cmd_type = check_commands(cmd,&margc,margv);

    switch(cmd_type) {
      case C_QUIT:
        break;
      case C_FIND: 
        key = get_search_key(stdin);
        list_matches(l,key,log,(brief)?game_print_brief:game_print);
        game_destroy(key);
        break;
      case C_LONG:
        brief = 0;
        break;
      case C_BRIEF:
        brief = 1;
        break;
      case C_HELP:
        for(i=0;i<sizeof(cmds)/sizeof(command_t);i++)
          fprintf(stdout,"%-10s %s\n",cmds[i].cmd,cmd_help[cmds[i].type]);
        break;
      case C_LOG:
        if(margc == 2) {
          if(log != stdout)
            fclose(log);
          if(!(log = fopen(margv[1],"w"))) {
            fprintf(stdout,"Can't open \"%s\" for write\n",margv[1]);
            log = stdout;
          }
        } else
          fprintf(stdout,"Wrong number of parameters\n");
        break;
      case C_READ: 
        if(margc == 2) {
          list_destroy(l);
          l = list_read(margv[1]);
        } else
          fprintf(stdout,"Wrong number of parameters\n");
        break;
      case C_APPEND: 
        if(margc == 2) 
          list_append(l,margv[1]);
        else
          fprintf(stdout,"Wrong number of parameters\n");
        break;
      case C_AMB: 
        fprintf(stdout,"Ambiguous command \"%s\"\n",cmd);
        break;
      default:
        fprintf(stdout,"Unrecgonized command \"%s\"\n",cmd);
    }
  } while(cmd_type != C_QUIT);

  if(log != stdout)
    fclose(log);

  list_destroy(l);

  return(0);
}

