//////////////////////////////////////////////////////////////
//
//
//  1. Eliminate all words longer than 15 letters and all
//     words shorter than 7 letters, not counting spaces.
//
//  2. Among what's left, find all entries 12-15 letters
//     long for which, when a string of 5 letters is removed,
//     the remaining string is a word/phrase still in the database.
//
//     For 15-letter entries:
//       it must be the 5 letters in the middle;
//
//     for 14-letter entries, 
//       it can be the 5 letters after the 4th or 5th letter; 
//
//     for 13-letter entries,
//       it can be the 5 letters after the 3rd, 4th, or 5th letter;
//
//     for 12-letter entries,
//       it can be the 5 letters after the 3rd or 4th letter.
//
//  3. Display the results in two columns, with the first column 
//     showing the longer words, in descending length from 15 to 12,
//     and the second column showing the corresponding shortened word.
//
//
//////////////////////////////////////////////////////////////

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


const int MAX_WORD = 24;

const char keyName[] = "db/final/key.txt";
FILE* key;
int lengths[MAX_WORD];

const char dbFormat[] = "db/final/words-len-%02d.txt";
FILE* db[MAX_WORD];

void usage( char* programName );
void verify();
bool find( char* word );


void puzzle1() {
  FILE* sol = fopen( "matches.txt", "w" );
  if( sol == NULL ) {
    printf( "Error: cannot open matches.txt for reading.\n" );
    exit( -1 );
  }


  fprintf( sol, "-- 15's --\n\n" );

  char lwordName[100];
  sprintf( lwordName, dbFormat, 15 );
  FILE* wl = fopen( lwordName, "r" );
  if( wl == NULL ) {
    printf( "Error: cannot open %s for reading.\n", lwordName );
    exit( -1 );
  }

  while( !feof( wl ) ) {
    char word[MAX_WORD];
    if( fgets( word, MAX_WORD, wl ) < 0 ) {
      printf( "Error reading %s.\n", lwordName );
    }
    if( feof( wl ) ) { continue; }

    word[strlen( word ) - 1] = '\0';
    char sword[MAX_WORD];
    memcpy( (void*) (sword+0), (void*) (word+ 0), 5 );
    memcpy( (void*) (sword+5), (void*) (word+10), 6 );

    if( find( sword ) ) {
      fprintf( sol, "%s\t%s\n", word, sword );
    }
  }


  fprintf( sol, "\n\n-- 14's --\n\n" );

  sprintf( lwordName, dbFormat, 14 );
  wl = fopen( lwordName, "r" );
  if( wl == NULL ) {
    printf( "Error: cannot open %s for reading.\n", lwordName );
    exit( -1 );
  }

  while( !feof( wl ) ) {
    char word[MAX_WORD];
    if( fgets( word, MAX_WORD, wl ) < 0 ) {
      printf( "Error reading %s.\n", lwordName );
    }
    if( feof( wl ) ) { continue; }

    word[strlen( word ) - 1] = '\0';
    char sword[MAX_WORD];

    memcpy( (void*) (sword+0), (void*) (word+ 0), 5 );
    memcpy( (void*) (sword+5), (void*) (word+10), 5 );
    if( find( sword ) ) {
      fprintf( sol, "%s\t%s\n", word, sword );
    }

    memcpy( (void*) (sword+0), (void*) (word+ 0), 4 );
    memcpy( (void*) (sword+4), (void*) (word+ 9), 6 );
    if( find( sword ) ) {
      fprintf( sol, "%s\t%s\n", word, sword );
    }
  }


  fprintf( sol, "\n\n-- 13's --\n\n" );

  sprintf( lwordName, dbFormat, 13 );
  wl = fopen( lwordName, "r" );
  if( wl == NULL ) {
    printf( "Error: cannot open %s for reading.\n", lwordName );
    exit( -1 );
  }

  while( !feof( wl ) ) {
    char word[MAX_WORD];
    if( fgets( word, MAX_WORD, wl ) < 0 ) {
      printf( "Error reading %s.\n", lwordName );
    }
    if( feof( wl ) ) { continue; }

    word[strlen( word ) - 1] = '\0';
    char sword[MAX_WORD];

    memcpy( (void*) (sword+0), (void*) (word+ 0), 5 );
    memcpy( (void*) (sword+5), (void*) (word+10), 4 );
    if( find( sword ) ) {
      fprintf( sol, "%s\t%s\n", word, sword );
    }

    memcpy( (void*) (sword+0), (void*) (word+ 0), 4 );
    memcpy( (void*) (sword+4), (void*) (word+ 9), 5 );
    if( find( sword ) ) {
      fprintf( sol, "%s\t%s\n", word, sword );
    }

    memcpy( (void*) (sword+0), (void*) (word+ 0), 3 );
    memcpy( (void*) (sword+3), (void*) (word+ 8), 6 );
    if( find( sword ) ) {
      fprintf( sol, "%s\t%s\n", word, sword );
    }
  }


  fprintf( sol, "\n\n-- 12's --\n\n" );

  sprintf( lwordName, dbFormat, 12 );
  wl = fopen( lwordName, "r" );
  if( wl == NULL ) {
    printf( "Error: cannot open %s for reading.\n", lwordName );
    exit( -1 );
  }

  while( !feof( wl ) ) {
    char word[MAX_WORD];
    if( fgets( word, MAX_WORD, wl ) < 0 ) {
      printf( "Error reading %s.\n", lwordName );
    }
    if( feof( wl ) ) { continue; }

    word[strlen( word ) - 1] = '\0';
    char sword[MAX_WORD];

    memcpy( (void*) (sword+0), (void*) (word+ 0), 4 );
    memcpy( (void*) (sword+4), (void*) (word+ 9), 4 );
    if( find( sword ) ) {
      fprintf( sol, "%s\t%s\n", word, sword );
    }

    memcpy( (void*) (sword+0), (void*) (word+ 0), 3 );
    memcpy( (void*) (sword+3), (void*) (word+ 8), 5 );
    if( find( sword ) ) {
      fprintf( sol, "%s\t%s\n", word, sword );
    }
  }


  fclose( sol );
}


int main( int argc, char** argv ) {

  if( argc != 2 ) {
    usage( argv[0] );
    exit( 0 );
  }

  for( int i = 0; i < MAX_WORD; ++i ) {
    char dbName[100];
    sprintf( dbName, dbFormat, i );
    db[i] = fopen( dbName, "r" );
    if( db[i] == NULL ) {
      printf( "Error: cannot open %s for reading.\n", dbName );
      exit( -1 );
    }
  }

  key = fopen( keyName, "r" );
  if( key == NULL ) {
    printf( "Error: cannot open %s for reading.\n", keyName );
    exit( -1 );
  }

  for( int i = 0; i < MAX_WORD; ++i ) {
    char line[100];
    if( fgets( line, 100, key ) < 0 ) {
      printf( "Error reading key file.\n" );
      exit( -1 );
    }
    line[strlen( line ) - 1] = '\0';    
    lengths[i] = atoi( line );
  }
  
  char verifyStr[] = "verify";
  char puz1Str[] = "puz1";
  
  if( strncmp( argv[1], verifyStr, sizeof( verifyStr ) ) == 0 ) {
    verify();

  } else if( strncmp( argv[1], puz1Str, sizeof( puz1Str ) ) == 0 ) {
    puzzle1();

  } else {
    usage( argv[0] );
    exit( 0 );
  }

  return 0;
}


void usage( char* programName ) {
  printf( "usage:\n" );
  printf( "%s verify\n", programName );
}


void verify() {
  printf( "verifying word database...\n" );

  char* word     = new char[MAX_WORD];
  char* lastword = new char[MAX_WORD];

  for( int i = 0; i < MAX_WORD; ++i ) {
    int lineno = 1;

    if( fgets( lastword, MAX_WORD, db[i] ) < 0 ) {
      printf( "Error reading first word of %d list.\n", i );
      exit( -1 );
    }

    lastword[strlen( word ) - 1] = '\0';
    
    while( !feof( db[i] ) ) {
      ++lineno;
      
      if( fgets( word, MAX_WORD, db[i] ) < 0 ) {
	printf( "Error reading word on line %d of %d list.\n", lineno, i );
	exit( -1 );
      }

      if( feof( db[i] ) ) {
	continue;
      }

      word[strlen( word ) - 1] = '\0';

      if( strcmp( lastword, word ) >= 0 ) {
	printf( "Word on line %d of %d list is out of order.\n", lineno, i );
	exit( -1 );
      }

      strcpy( lastword, word );
    }
  }
}


bool find( char* word ) {
  int len = strlen( word );
  
  int min = 0;
  int max = lengths[len] - 1;
  int pos = (min+max)/2;

  while( true ) {
    fseek( db[len], pos*(len+1), SEEK_SET );

    char rword[MAX_WORD];
    if( fgets( rword, MAX_WORD, db[len] ) < 0 ) {
      printf( "Error reading word %d of %d list.\n", pos, len );
      exit( -1 );
    }

    rword[strlen( rword ) - 1] = '\0';

    int result = strcmp( word, rword );
    if( result == 0 ) {
      return true;
    }

    if( result < 0 ) {
      if( max == pos ) {
	return false;
      }
      max = pos;

    } else {
      if( min == pos ) {
	return false;
      }
      min = pos;
    }

    pos = (min+max)/2;
  }
}
