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

// lungimea maxima a fisierului sursa - schimba aici pentru alte valori
#define MAX_SOURCE_SIZE 10240

#define OK 0
#define LIN_INCORECT 1
#define REZ_INCORECT 2
#define LIPSA 3
#define VECTORI 4
#define BUCLE 5

#define FTYPE_ERR 0
#define FTYPE_CPP 1
#define FTYPE_C 2
#define FTYPE_PAS 3

char *ext[] = { "", ".cpp", ".c", ".pas" };
char *erori[] = { "OK", "linie incorecta", "rezultat incorect", "lipsa",
                  "foloseste vectori", "foloseste cicluri" };
char linie[1000001];

void error(char msg[], int p) {
  fprintf(stderr, msg);
  printf("%d", p);
  exit(0);
}

void bun(int p) {
  fprintf( stderr, "OK\n" );
  printf("%d", p);
  exit(0);
}

// copiaza src in dest, inclusiv '\0', si returneaza lungimea
int cpstr( char *dest, char *src ) {
  int i = 0;
  do dest[i] = src[i]; while ( src[i++] );
  return i-1;
}

// returneaza tipul sursei, vezi constantele definite mai sus
int openSource( char *name, FILE **dest ) {
  char newname[50];
  int len;

  len = cpstr( newname, name );
  
  cpstr( newname + len, ext[FTYPE_CPP] ); // incercam cpp
  if ( ((*dest) = fopen( newname, "r" )) )
    return FTYPE_CPP;
  
  cpstr( newname + len, ext[FTYPE_C] ); // incercam c
  if ( ((*dest) = fopen( newname, "r" )) )
    return FTYPE_C;
  
  cpstr( newname + len, ext[FTYPE_PAS] ); // incercam pas
  if ( ((*dest) = fopen( newname, "r" )) )
    return FTYPE_PAS;

  return FTYPE_ERR; // n-am gasit nici un fisier sursa, eroare
}

// returneaza 1 daca sirul este fie NULL, fie contine doar whitespace
int sempty( char *s ) {
  if ( !s )
    return 1;
  while ( *s && isspace( *s ) )
    s++;
  return (*s) == '\0';
}

// copiaza primul cuvint din src in dest
// returneaza unde am ramas in src sau NULL daca nu am gasit cuvint
// ignora whitespace
char *sgetw( char *src, char *dest ) {
  while ( isspace( *src ) ) // ignoram spatii
    src++;
  if ( (*src) == '\0' ) // n-am gasit nici un cuvint
    return NULL;
  while ( *src && !isspace( *src ) )
    (*(dest++)) = (*(src++));
  *dest = '\0';
  return src;
}

// returneaza:
//   0 daca sint la fel (diff returneaza sirul vid)
//   1 daca difera
//   2 daca lipseste .out
//   3 daca lipseste .ok
int graderDiff( char *nout, char *nok, int lineLen ) {
  FILE *fout, *fok;
  char linout[lineLen], linok[lineLen];
  char wordout[lineLen], wordok[lineLen];
  char *lout, *lok;
  int error = 0;

  fout = fopen( nout, "r" );
  if( !fout )
    return 2;

  fok = fopen( nok, "r" );
  if( !fok )
    return 3;

  linout[lineLen-1] = '\0';
  lout = fgets( linout, lineLen, fout );
  lok = fgets( linok, lineLen, fok );
  if ( linout[lineLen-1] )
    error = 1;
  while ( lout && lok && !error ) {
    do {
      lout = sgetw( lout, wordout );
      lok =  sgetw( lok, wordok );
    } while ( lout && lok && !strcmp( wordout, wordok ) );
    if ( !lout && !lok ) { // liniile sint egale
      linout[lineLen-1] = '\0';
      lout = fgets( linout, lineLen, fout ); // citim urmatoarele linii
      lok = fgets( linok, lineLen, fok );
      if ( linout[lineLen-1] )
        error = 1;
    } else {
      error = 1;
    }
  }
  if ( !error )
    error = !(sempty(lout) && sempty(lok));
  lout = fgets( linout, lineLen, fout );
  lok = fgets( linok, lineLen, fok );
  if ( lout || lok ) // daca vreunul din fisiere mai are linii, eroare
    error = 1;
  fclose( fout );
  fclose( fok );

  return error;
}

int main() {
  FILE *fsource;
  int ftype;
  size_t len;

  // deschidem si citim fisierul sursa in linie[]
  ftype = openSource( "plaja2", &fsource );
  if ( ftype == FTYPE_ERR )
    error("Fisier sursa lipsa!", 0);
  len = fread( linie, sizeof(char), 1000000, fsource );
  if ( len == 0 )
    error("Fisier sursa necitibil!", 0);
  if ( len > MAX_SOURCE_SIZE )
    error("Sursa depaseste limita admisa", 0);
  fclose( fsource );
  linie[len] = '\0'; // ca sa fim siguri

  if ( strstr( linie, "[" ) ) // foloseste vectori?
    error(erori[VECTORI], 0);

  /*
  if ( strstr( linie, "for" )
       || strstr( linie, "while" )
       || strstr( linie, "goto" ) ) // foloseste bucle?
    error(erori[BUCLE], 0);
  */

  switch ( graderDiff( "plaja2.out", "plaja2.ok", 100 ) ) {
  case 0: bun( 10 ); break;
  case 1: error("Incorect", 0); break;
  case 2: error("Fisier iesire lipsa", 0); break;
  case 3: error("Fisier intern .ok lipsa", 0); break;
  default: error("Eroare necunoscuta de diff", 0);
  }
  return 0;
}
