// Nick Cash
// Computer Science II (810:062:02)
// Assignment 5

#include <cstdlib>
#include <iostream>
#include <stdio.h>

using namespace std;

// for easy changing and reference without hard coding
#define MAX_MESH      39824
#define MAX_READ      128

struct sMesh
{
    string      text;
    fpos_t      addr; // disk address
} mesh[MAX_MESH];

// function prototypes
bool  read_line( FILE *f );
bool  read_line_seek( FILE *f, const fpos_t *pos, sMesh& m );

// globals to make life easier
int last_mesh; // used as an index. Holds the number of mesh's and the next slot to use

/////

// used for qsort/bsearch
int compare(const void *m1, const void *m2)
{
        struct sMesh *mi1 = (struct sMesh *) m1;
        struct sMesh *mi2 = (struct sMesh *) m2;

        return strcmp(mi1->text.c_str(), mi2->text.c_str());
}

int main()
{
    FILE *x;
    
    x = fopen("mdata","r");
    
    // loop until we fill all slots, unless we hit EOF or have a read error first
    while ( last_mesh < MAX_MESH )
    {
       if ( read_line( x ) == false )
        break;      
    }
  
    qsort( mesh, last_mesh, sizeof(struct sMesh), compare);
      
    ///
    
    // grab input
    char txt[MAX_READ];
    
    cout << "\n\nInput text to find:\n\n>";
    cin.getline(txt, MAX_READ);
    
    // loop until input is terminated
    while ( 1 )
    {
      if ( !strcmp(txt,"quit") || txt[0] == 'q' || txt[0] == '\0' )
       break;

      struct sMesh k, *res = NULL;
      k.text = txt;
      
      res = (sMesh *) bsearch(&k, mesh, last_mesh, sizeof(struct sMesh), compare);

      if (res == NULL)
         cout << "No match for '" << txt << "'\n\n";
      else
      {
         // reread values into k
         read_line_seek( x, &res->addr, k );

         cout << "Match (" << txt << ") ->  text == '" << k.text << "'; addr == " << k.addr << endl << endl;
      }
         
      // prompt
      cout << "> ";
      cin.getline(txt, MAX_READ);
    }

    return EXIT_SUCCESS;
}

// seeks to a specified spot in f (based on pos) and reads a line into an sMesh structure that
// is passed down. Returns false on errors
bool read_line_seek( FILE *f, const fpos_t *pos, sMesh& m )
{
   char     buf[MAX_READ]; // temp buffer
   string   s;

   if ( !f )
    return false;

   // some default values
   m.text = "";
   m.addr = -1;

   // set the position and check for bad return
   if ( fsetpos(f, pos) != 0 )
    return false;

   // get the string
   if ( fgets(buf,MAX_READ,f) == NULL )
    return false;

   buf[strlen(buf)-1]='\0'; // chop off the \n
   s = buf;

   // chop off hiearchy code
   while ( s[0] != ';' )
     s.erase(0,1);

   // chop off leading space and semi-colon
   s.erase(0,2);


   m.addr = *pos;
   m.text = s;
   
   return true;
}


// read's one line from f. keeps the text and file position
// returns false on bad file pointer or EOF. True otherwise.
bool read_line( FILE *f )
{
   fpos_t   pos;           // file position
   char     buf[MAX_READ]; // temp buffer
   string   s;

   // check file pointer
   if ( !f )
    return false;     
    
   // grab the file position
   if ( fgetpos( f, &pos ) != 0 )
    return false;
   
   // get the string
   if ( fgets(buf,MAX_READ,f) == NULL )
    return false;
    
   buf[strlen(buf)-1]='\0'; // chop off the \n
   s = buf;
   
   // chop off hiearchy code
   while ( s[0] != ';' )
     s.erase(0,1);

   // chop off leading space and semi-colon
   s.erase(0,2);

   
   mesh[last_mesh].text = s;
   mesh[last_mesh++].addr = pos;
   
   return true;
}