#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// input_buffer contains a 7-character string whose characters are '0' and '1'
// and that represents an ASCII code in binary.
// This function does Hamming coding of the input buffer, and the resulting
// 11 binary digits are saved at output_buffer.
void convert_to_codeword(char * input_buffer, char * output_buffer);


// input_buffer contains an 11-character string whose characters
// are '0' and '1' and that represents the Hamming coding of an ASCII code
// in binary.
// This function decodes the Hamming code, and the stores the original 
// ASCII code in output_buffer, as a string as a string of 7 
// binary ('0' and '1') characters.
// returns:
// 1 if no error was detected.
// 0 if an error was detected and corrected.
// -1 if an error was detected but not corrected.
int convert_to_original_word(char * input_buffer, char * output_buffer);

// converts a character (represented as an ASCII code) to a 
// string of 7 characters that are '0' or '1' and that represent 
// that ASCII code in binary.
void character_to_binary(char character, char * buffer)
{
  int number = (int) character;
  int counter;
  for (counter = 0; counter < 7; counter++)
  {
    int remainder = number % 2;
    number = number / 2;
    if (remainder == 0)
    {
      buffer[6-counter] = '0';
    }
    else
    {
      buffer[6-counter] = '1';
    }
  }
}


// The input is a string of 7 characters that are '0' or '1' and that represent 
// an ASCII code in binary. 
// The output is the character corresponding to that ASCII code.
char binary_to_character(char * buffer)
{
  int number = 0;
  int power = 1;
  int counter;
  for (counter = 0; counter < 7; counter++)
  {
    if (buffer[6-counter] == '1')
    {
      number = number + power;
    }
    power = power * 2;
  }

  return (char) number;
}

// Reads data from the input file (represented by input_handle)
// in chunks of 7 characters, that represent an ASCII code in binary digits.
// Each chunk is encoded using Hamming coding, and the 11-character-long
// result is stored at the output file (represented by output_handle).
void encode(FILE * input_handle, FILE * output_handle)
{
   char input_buffer[7];
   char output_buffer[11];

   while(1)
   {
      // read the next "word" from the input file
      int items = fread(input_buffer, sizeof(char), 7, input_handle);
      if (items != 7)
      {
          break;
      }
      
      // This is where your function is called.
      convert_to_codeword(input_buffer, output_buffer);
      
      // Write the "codeword" to the output file.
      fwrite(output_buffer, sizeof(char), 11, output_handle);
   }
}


// Reads data from the input file (represented by input_handle)
// in chunks of 11 characters, that represent the Hamming coding of 
// an ASCII code in binary digits.
// Each chunk is decoded, and the 7-character-long
// result (containing '0' and '1' characters and representing the ASCII
// code in binary) is stored at the output file (represented by 
// output_handle).
// 
// This function also prints, for each detected error:
// - the position of the word containing the error.
// - whether that error was corrected or not.
void decode_and_correct(FILE * input_handle, FILE * output_handle)
{
   char input_buffer[11];
   char output_buffer[7];
   int word_counter = 0;
   
   while(1)
   {
      int items = fread(input_buffer, sizeof(char), 11, input_handle);
      if (items != 11)
      {
          break;
      }
      
      // This is where your function is called.
      int result = convert_to_original_word(input_buffer, output_buffer);
      fwrite(output_buffer, sizeof(char), 7, output_handle);
      if (result == 0)
      {
         printf("error corrected at word %d\n", word_counter);
      }
      else if (result == -1)
      {
         printf("error detected and NOT corrected at word %d\n", word_counter);
      }
      
      word_counter++;
   }
}


// This is a helper function, that can be used 
// (and modified) so as to create some "binary" test files
// from text.
// Note: this function creates uncoded files, with 7 bits/character
// not coded files with 11 bits per character.
void text_to_binary(char * filename, char * text)
{
   FILE * handle = fopen(filename, "wb");
 
   int length = strlen(text);
   char buffer[7];
   int counter;
   for (counter = 0; counter < length; counter++)
   {
      character_to_binary(text[counter], buffer);
      fwrite(buffer, sizeof(char), 7, handle);
   }
      
   fclose(handle);
}


// This is a helper function, that can be used 
// (and modified) so as to translate some "binary" test files
// into text and print that text.
// Note: this function translates uncoded files, with 7 bits/character
// not coded files with 11 bits per character.
void binary_to_text(char * filename)
{
   FILE * handle = fopen(filename, "rb");
  
   printf("\nStart of translation:\n");
   char buffer[7];
   while(1)
   {
      int items = fread(buffer, sizeof(char), 7, handle);
      if (items != 7)
      {
         break;
      }
      char c = binary_to_character(buffer);
      printf("%c", c);
   }
   
   printf("\nEnd of translation\n");	      
   fclose(handle);
}


int main(int argc, char ** argv)
{
   if (argc != 4)
   {
      printf("error: three command-line arguments needed:\n");
      printf("       number input_file output_file\n");
      return 1;
   }

//   text_to_binary("text1.txt", "The kangaroo is an animal that lives in Australia.");
//   int number = 0;
//   input_file  = "in1.txt";   
//   output_file = "out1.txt";

   int number = atoi(argv[1]);
   char * input_file  = argv[2];   
   char * output_file = argv[3];
  
   FILE * input_handle = fopen(input_file, "rb");
   FILE * output_handle = fopen(output_file, "wb");

   if (number == 0) // we must do encoding
   {
      // Print the contents of the input file in text format (for debugging)
      binary_to_text(input_file);
      
      // do the encoding
      encode(input_handle, output_handle);
      
      // close the files.
      fclose(input_handle);
      fclose(output_handle);
   }
   else if (number == 1)
   {
      // do the decoding and correction.
      decode_and_correct(input_handle, output_handle);
      
      // close the files.
      fclose(input_handle);
      fclose(output_handle);

      // Print the contents of the output file in text format (for debugging)
      binary_to_text(output_file);
   }
   
   return 0;
}   
