/*
  stations.c - managing of the stations-database
  Written by Stefan Pfeiffer, DL1ELY, 940502
  Last Modification: 940823
  (C) 1994 by Stefan Pfeiffer, DL1ELY
*/  

#include "structs.h"
#include <stdio.h>
#include <string.h>
#include <alloc.h>
#include <conio.h>
#include <ctype.h>
#include <stdlib.h>

#define PRGNAME "STATIONS"
#define VERSION "v1.02"
#define AUTHOR "Stefan Pfeiffer, DL1ELY"
#define DATE "940823"

#define INDEX_FILE "INDEX.59"
#define STATION_FILE "STATIONS.59"


typedef struct comp_data_record
{
  char call[7];
  char locator[7];
  char info[80];
};  

struct index *ind;
FILE *f;
unsigned ind_cnt;
char magic[5];
unsigned i;
char call[128];
char cntname[128];
char info[80];
struct data_record entry;
long pos;
struct data_record db;
struct comp_data_record cdb;


void save_index(void);
char valid_locator(char *);
void delete_record(unsigned);
void edit_record(unsigned);
void add_cnt_file(char *);
unsigned find_call(char *);
void read_record(FILE *,struct comp_data_record *);
void write_record(FILE *,struct comp_data_record);
void add_record(struct comp_data_record);
void check_file(void);
int cmp_index(const void *,const void *);


int far cmp_index(const void *a, const void *b)
{
  return(strncmp((char *)a,(char *)b,6));
};
  
  
void read_record(FILE *f,struct comp_data_record *cdb)
{
  unsigned i;
  
  fread((void *)cdb->call,1,7,f);
  fread((void *)cdb->locator,1,7,f);
  fread((void *)&i,1,2,f);
  
  cdb->info[i] = 0;
  if(i) fread((void *)cdb->info,1,i,f);
};


void write_record(FILE *f,struct comp_data_record cdb)
{
  unsigned i;
  
  fwrite((void *)cdb.call,1,7,f);
  fwrite((void *)cdb.locator,1,7,f);
  i = strlen(cdb.info);
  fwrite((void *)&i,1,2,f);
  if(i) fwrite((void *)cdb.info,1,i,f);
};
  

void save_index()
{
  FILE *f;
  
  qsort((void *)ind,ind_cnt,sizeof(struct index),cmp_index);
  
  f = fopen(INDEX_FILE,"wb");
  strcpy(magic,"SYS\x1A");
  fwrite((void *)magic,1,4,f);
  fwrite((void *)&ind_cnt,1,2,f);
  fwrite((void *)ind,ind_cnt,sizeof(struct index),f);
  fclose(f);
};


char valid_locator(char *qthkenn)
{
  return((!*(qthkenn+6)) &&
         ((toupper(*qthkenn)>='A')&&(toupper(*qthkenn)<='R')) &&
         ((toupper(*(qthkenn+1))>='A')&&(toupper(*(qthkenn+1))<='R')) &&
         (isdigit(*(qthkenn+2))) &&
         (isdigit(*(qthkenn+3))) &&
         ((toupper(*(qthkenn+4))>='A')&&(toupper(*(qthkenn+1))<='X')) &&
         ((toupper(*(qthkenn+5))>='A')&&(toupper(*(qthkenn+1))<='X')));
};         



void delete_record(unsigned nr)
{
  long bytes;
  struct comp_data_record cdb;
  FILE *f1;
  FILE *f2;
  
  bytes = (ind+nr)->pos;
  memmove((ind+nr),(ind+nr+1),(ind_cnt-nr-1)*sizeof(struct index));
  ind_cnt--;
  ind = realloc(ind,ind_cnt*sizeof(struct index));
  
  f1 = fopen(STATION_FILE,"rb");
  f2 = fopen("$$$$$$$$.59","wb");
  
  fread((void *)magic,1,4,f1);
  fwrite((void *)magic,1,4,f2);
  
  while(bytes > ftell(f1))
  {
    read_record(f1,&cdb);
    write_record(f2,cdb);
  };
  
  read_record(f1,&cdb);
  
  while(!feof(f1))
  {
    read_record(f1,&cdb);
    pos = find_call(cdb.call);
    (ind+pos)->pos = ftell(f2);
    write_record(f2,cdb);
  }
  
  fclose(f1);
  fclose(f2);
  
  unlink(STATION_FILE);
  rename("$$$$$$$$.59",STATION_FILE);
  
  save_index();    
};


void edit_record(unsigned nr)
{
  struct comp_data_record cdb;

  f = fopen(STATION_FILE,"rb");
  fseek(f,(ind+nr)->pos,SEEK_SET);
  read_record(f,&cdb);
  fclose(f);

  delete_record(nr);
  printf("Alter Locator: %s\n",cdb.locator);
  do
  {
    printf("Neuer Locator: ");
    gets(cdb.locator);
    strupr(cdb.locator);
  } while(!valid_locator(cdb.locator));

  printf("Alte Information: %s\n",cdb.info);
  printf("Neue Information: ");
  gets(cdb.info);

  add_record(cdb);
  save_index();
};


void add_cnt_file(char *name)
{
  FILE *qsof;
  FILE *f;
  struct header h;
  struct qso q;
  unsigned i;
  struct comp_data_record cdb;
  unsigned found;
  long pos;
  char ch;
  
  unsigned approved = 0;
  unsigned changed = 0;
  unsigned added = 0;
  unsigned ignored = 0;
  
  if((qsof = fopen(name,"rb")) == NULL)
  {
    printf("Datei %s nicht gefunden!\n",name);
    return;
  }
  
  printf("Eintragen von %s:\n",name);
  
  fread((void *)&h,1,HEADER_SIZE,qsof);
  for(i=1;i<=h.qso_cnt;i++)
  {
    fread((void *)&q,1,QSO_SIZE,qsof);
    found = find_call(q.barecall);
    if(found == ind_cnt)
    {
      printf("Call %-6s wird hinzugefuegt: %s\n",q.barecall,q.locator);
      strcpy(cdb.call,q.barecall);
      strcpy(cdb.locator,q.locator);
      cdb.info[0] = 0;
      add_record(cdb);
      added++;
    }
    else
    {
      pos = (ind+found)->pos;
      f = fopen(STATION_FILE,"rb");
      fseek(f,pos,SEEK_SET);
      read_record(f,&cdb);
      fclose(f);
      if(!strcmp(cdb.locator,q.locator))
      {
        printf("Call %-6s (%s) schon in der Datenbank!\n",q.barecall,q.locator);
        approved++;
      }
      else
      {
        printf("%-6s: Anderer Locator in der Datenbank gefunden!\n",q.barecall);
        printf("Datenbank: %s   Contest-File: %s\n",cdb.locator,q.locator);
        printf("Datenbank anpassen? [J/N]");
        do
        {
          ch = getch();
          ch = toupper(ch);
        } while(ch != 'J' && ch != 'N');
        printf("\n");
        if(ch == 'J')
        {
          delete_record(found);
          strcpy(cdb.locator,q.locator);
          add_record(cdb);
          printf("Eintrag %s geaendert!",q.barecall);
          changed++;
        }
        else
        {
          printf("Datenbank nicht veraendert!\n");
          ignored++;
        };  
      };
    };            
  };    
  save_index();
  printf("\nHinzugefuegt: %d\n",added);
  printf("Bestaetigt: %d\n",approved);
  printf("Geaendert: %d\n",changed);
  printf("Ignoriert: %d\n",ignored);
};


unsigned find_call(char *call)
{
  unsigned i = 0;
  
  while(i < ind_cnt && strncmp((ind+i)->call,call,6)) i++;
  return(i);
};


void add_record(struct comp_data_record cdb)
{
  long pos;
  FILE *f;
  struct data_record db;

  strcpy(db.call,cdb.call);
  strcpy(db.loc,cdb.locator);
  db.info_len = strlen(cdb.info);

  f = fopen(STATION_FILE,"ab");
  fseek(f,0,SEEK_END);
  pos = ftell(f);
  fwrite((void *)&db,1,sizeof(struct data_record),f);
  if(db.info_len) fwrite((void *)cdb.info,1,strlen(cdb.info),f);
  fclose(f);

  ind_cnt++;
  ind = realloc(ind,ind_cnt*sizeof(struct index));
  (ind+ind_cnt-1)->pos = pos;
  memcpy((ind+ind_cnt-1)->call,cdb.call,6);
};


  
void check_file()
{
  unsigned ind_alloc = 0;
  FILE *tmpf;
  FILE *statf;
  unsigned char call[7];
  unsigned char locator[7];
  unsigned char info[80];
  unsigned int ilen;
  unsigned long pos;
  unsigned i;
  
  free(ind);
  ind_cnt = 0;
  if((tmpf = fopen("$$$$$$$$.59","wb")) == NULL) return;
  if((statf = fopen(STATION_FILE,"rb")) == NULL) return;
  
  fread((void *)info,1,4,statf);
  fwrite((void *)info,1,4,tmpf);
  
  while(!feof(statf))
  {
    pos = ftell(statf);
    
    fread((void *)call,1,7,statf);
    fread((void *)locator,1,7,statf);
    fread((void *)&ilen,1,2,statf);
    info[ilen] = 0;
    if(ilen) fread((void *)info,1,ilen,statf);
    printf("%ld: %s, %s, (%d) %s - ",pos,call,locator,ilen,info);
  
    if(call[0])
    {
      i = 0;
      while((i<ind_cnt)&&strncmp((ind+i)->call,call,6)) i++;
      
      if(i==ind_cnt)
      {
        printf("OK!\n");
        if(ind_cnt==ind_alloc)
        {
          ind_alloc+=10;
          ind = realloc(ind,ind_alloc*sizeof(struct index));
        };
      
        pos = ftell(tmpf);
      
        strncpy((ind+ind_cnt)->call,call,6);
        (ind+ind_cnt)->pos = pos;
      
        fwrite((void *)call,1,7,tmpf);
        fwrite((void *)locator,1,7,tmpf);
        fwrite((void *)&ilen,1,2,tmpf);
        if(ilen) fwrite((void *)info,1,ilen,tmpf);
      
        ind_cnt++;
      }
      else printf(" DOUBLE!\n");
    }  
    else printf(" NO!\n");  
        
  };
  
  printf("%d indizes written!\n",ind_cnt);
  
  fclose(statf);
  fclose(tmpf);  

  unlink(STATION_FILE);
  rename("$$$$$$$$.59",STATION_FILE);
  
  save_index();
};


int main(int argc, char **argv)
{
  printf("%s %s by %s, %s\n",PRGNAME,VERSION,AUTHOR,DATE);
  
  if((f = fopen(INDEX_FILE,"rb")) == NULL)
  {
    printf("\nIndex-file %s nicht gefunden!\n",INDEX_FILE);
    f = fopen(INDEX_FILE,"wb");
    strcpy(magic,"SYS\x1A");
    fwrite((void *)magic,1,4,f);
    ind_cnt = 0;
    fwrite((void *)&ind_cnt,1,2,f);
    fclose(f);
  }
  
  fread((void *)magic,1,4,f);
  if(strncmp(magic,"SYS\x1A",4))
  {
    printf("\nFile %s ist zerstrt!\n",INDEX_FILE);
    return(2);
  }
  
  fread((void *)&ind_cnt,1,2,f);
  if(argc > 1)
  {
    ind = malloc(ind_cnt*sizeof(struct index));
    fread((void *)ind,1,ind_cnt*sizeof(struct index),f);
  }
  printf("\n%d Indexeintraege gelesen!\n",ind_cnt);
  fclose(f);
  
  if((f = fopen(STATION_FILE,"rb")) == NULL)
  {
    printf("File %s nicht gefunden!\n",STATION_FILE);
    f = fopen(STATION_FILE,"wb");
    fwrite((void *)magic,1,4,f);
    fclose(f);
    return(1);
  }

  if(argc == 1)
  {
    check_file();
    return(0);
  }
    
  strcpy(call,argv[1]);
  if(call[0] == '@')
  {
    char *cp;
    
    strcpy(cntname,(char *)(call+1));
    strupr(cntname);

    cp = strchr(cntname,'.');
    if(!cp) strcat(cntname,".CNT");
    
    add_cnt_file(cntname);
    return(0);
  }

  call[6] = 0;
  strupr(call);
  
  i = find_call(call);
  
  if(i == ind_cnt)
  {
    char ch;
    
    printf("Call %s nicht gefunden. Hinzufgen?",call);
    do
    {
      ch = getch();
      ch = toupper(ch);
    } while(ch != 'J' && ch != 'N');
    printf("\n");
    if(ch == 'N')
    {
      free(ind);
      return(0);
    }
    
    strcpy(cdb.call,call);
    
    do
    {
      printf("Locator: ");
      gets(cdb.locator);
      strupr(cdb.locator);
    } while(!valid_locator(cdb.locator));
    
    printf("Informationen: ");
    gets(cdb.info);
    
    add_record(cdb);
    
    save_index();
    
    printf("OK!\n");
  }
  else
  {
    char ch;
    
    printf("\nCall %s gefunden:\n",call);
    
    f = fopen(STATION_FILE,"rb");
    fseek(f,(ind+i)->pos,SEEK_SET);
    read_record(f,&cdb);
    fclose(f);
    printf("%s  %s  %s\n",cdb.call,cdb.locator,cdb.info);
    
    printf("\nL)schen, E)ditieren, A)bbrechen? [L/E/A] ");
    do
    {
      ch = getch();
      ch = toupper(ch);
    } while(ch != 'L' && ch != 'E' && ch != 'A');
    
    printf("\n");
    
    switch(ch)
    {
      case 'L': 
        delete_record(i);
        printf("Eintrag %s gelscht!\n",call);
        break;
      case 'E':
        edit_record(i);
        printf("Eintrag %s gendert!\n",call);
    }
  }        
  
  free(ind);
  return(0);
}
      
