#include <stdlib.h>
#include "ep_internal.h"
//#include "cr.h"

LG_context_t* ep_ctx;
/*
  EP_init: initializes the system.
 */
void EP_init(LG_context_t* ctx) {
  ep_ctx = ctx;
  MM_init(ctx);
}

/*
  EP_end: terminates the system.
 */
void EP_end() {
}

/*
  Unfolds input.

  stream  - The input stream
  is_mail - is it mail?

  return  - The structure of the input

  This functions is the most important entry point of EP, its where
  the input is parsed.
 */
EP_input_structure_t* EP_unfold(FILE* stream, gboolean is_mail) {
  GString* string_stream; // no need to free - done in ep_a_d_free
  ep_authenticated_data_t* data;
  ep_input_structure_t* input_structure;

  string_stream = ep_stream_to_string(stream);
  data = ep_authenticated_data_new();

  data->data->data = string_stream;
  data->credentials = NULL;
  if (is_mail) {
    data->data->type = EP_MAIL;
  }
  else {
    data->data->type = EP_TEXT;
  }
  LG_log(ep_ctx, LG_DEBUG, "Calling ep_explode");
  input_structure = ep_explode(data);
  ep_authenticated_data_free(data);

  return input_structure;
}

/*
  Returns candidate keywords.

  input - Input structure.

  return - Candidate keywords.
 */
gchar* EP_get_candidate_keywords(EP_input_structure_t* input) {
  ep_input_structure_t* inp;
  GString* result;
  gchar** tokens;
  int curr_token;
  gchar* return_result;

  inp = (EP_input_structure_t*) input;
  if (!inp->mail_info) {
    return "";
  }
  tokens = g_strsplit(inp->mail_info->subject->str, " ", 0);
  result = g_string_new("");

  for (curr_token = 0; tokens[curr_token]; curr_token++) {
    if (tokens[curr_token][0] == 0) {
      continue;
    }
    if (curr_token) {
      g_string_append(result, ",");
    }
    g_string_append(result, tokens[curr_token]);
  }
 
  g_strfreev(tokens);
  return_result = result->str;
  g_string_free(result, FALSE);
  return return_result;
}

/*
  For each blob in an input structure apply a function

  input     - the input structure
  func      - the function
  user_data - extra data to be passed to the function
*/
void EP_blobs_foreach(EP_input_structure_t* input,
  ObjFunc func, gpointer user_data) {
  GList* blobs;

  blobs = ((ep_input_structure_t*)input)->flattened_authenticated_data;
  if (blobs == NULL) {
    return;
  }
  do {
    func(blobs->data, user_data);
  } while (blobs = g_list_next(blobs));
}

/*
  Returns the requested attribute of a mail_info.

  input  - the input structure

  field  - the field

  return - the address
*/
const gchar* EP_get_mail_hdr_field(EP_input_structure_t* input, gchar* field) {
  ep_input_structure_t* inp;

  inp = (ep_input_structure_t*)input;
  if (inp->mail_info) {
    if (strcasecmp(field, "from") == 0) {
      return inp->mail_info->from->str;
    }
    if (strcasecmp(field, "subject") == 0) {
      return inp->mail_info->subject->str;
    }
    if (strcasecmp(field, "cc") == 0) {
      return inp->mail_info->cc->str;
    }
    if (strcasecmp(field, "date") == 0) {
      return inp->mail_info->date->str;
    }
    if (strcasecmp(field, "replyto") == 0) {
      return inp->mail_info->reply_to->str;
    }
    if (strcasecmp(field, "message-id") == 0) {
      return inp->mail_info->message_id->str;
    }
    return NULL;
  }
  else {
    return NULL;
  }
}

/*
  Returns a blob from a blob_credential.

  input  - the blob_credential

  return - the blob
*/
const gchar* EP_get_blob(EP_blob_credential_t* input) {
  return ((ep_authenticated_data_t*)input)->data->data->str;
}

/*
  Returns a credential from a blob_credential

  input  - the blob_credential

  return - the credential 
*/
GList* EP_get_credentials(EP_blob_credential_t* input) {
  return ((ep_authenticated_data_t*)input)->credentials;
}

//void ep_authenticated_data_free(ep_authenticated_data_t* data);
void ep_input_structure_remove_auth_data(gpointer data, gpointer user) {
  ep_authenticated_data_t* auth_data;

  auth_data = (ep_authenticated_data_t*) data;
  ep_authenticated_data_free(auth_data);
}

void EP_input_structure_free(EP_input_structure_t* input) {
  ep_input_structure_t* structure;

  structure = (ep_input_structure_t*) input;
  if (structure->mail_info) {
    ep_mail_info_free(structure->mail_info);
  }
  g_list_foreach(structure->flattened_authenticated_data, ep_input_structure_remove_auth_data, NULL);
  g_list_free(structure->flattened_authenticated_data);
    
  g_free(structure);

}

/*
  Frees a mail_info structure.

  mail_info - mail_info structure.
*/
void ep_mail_info_free(ep_mail_info_t* mail_info) {
  g_string_free(mail_info->from, TRUE);
  g_string_free(mail_info->subject, TRUE);
  g_string_free(mail_info->date, TRUE);
  g_string_free(mail_info->reply_to, TRUE);
  g_string_free(mail_info->cc, TRUE);
  g_string_free(mail_info->message_id, TRUE);
  g_free(mail_info);
}

/*
  Explodes a piece of input.

  data   - data to explode

  return - the representative input structure
*/
ep_input_structure_t* ep_explode(ep_authenticated_data_t* data) {
  switch (data->data->type) {
    case EP_TEXT:
      return ep_text_driver(data);
    case EP_MAIL:
      return ep_mail_driver(data);
    break;
  }
}

/*
  ep_stream_to_string - Converts a file to a GString

  stream - FILE stream.

  return - a GString with the whole file contents

  The file is supposedly a text string (without 0s).
*/
GString* ep_stream_to_string(FILE* stream) {
#define ESTS_BUFFER_SIZE 1024
  GString* return_string;
  char buffer[ESTS_BUFFER_SIZE];

  return_string = g_string_new("");
  while (!feof(stream)) {
    memset(buffer, 0, ESTS_BUFFER_SIZE);
    fread(buffer, 1, ESTS_BUFFER_SIZE, stream);
    g_string_append(return_string, buffer);
  }

  return return_string;
}

/*
  Creates a new input_structure.

  return - input_structure.
 */
ep_input_structure_t* ep_input_structure_new() {
  ep_input_structure_t * input;

  input = g_malloc(sizeof(ep_input_structure_t));
  input->mail_info = NULL;
  input->flattened_authenticated_data = NULL;

  return input;
}

/*
  Creates a new authenticated_data structure.

  return - authenticated_data structure.
*/
ep_authenticated_data_t* ep_authenticated_data_new() {
  ep_authenticated_data_t* data;

  data = g_malloc(sizeof(ep_authenticated_data_t));
  data->data = ep_exploded_data_new();
  data->credentials = NULL;

  return data;
}

/*
  Frees an authenticated_data structure.

  data - authenticated_data structure.
 */
void ep_authenticated_data_free(ep_authenticated_data_t* data) {
  ep_exploded_data_free(data->data);
  CR_credential_list_free(data->credentials);
  g_free(data);
}

ep_exploded_data_t* ep_exploded_data_new() {
  ep_exploded_data_t* data;

  data = g_malloc(sizeof(ep_exploded_data_t));
  data->data = g_string_new("");

  return data;
}

/*
  Frees an exploded_data structure.

  data - expolded_data structure.
 */
void ep_exploded_data_free(ep_exploded_data_t* data) {
  g_string_free(data->data, TRUE);
  g_free(data);
}

/*
  Creates a new mail_info structure.

  from    - From:
  subject - Subject:

  return - New mail_info structure.
 */
ep_mail_info_t* ep_mail_info_new(gchar* from, gchar* subject,
  const gchar* date, const gchar* reply_to, const gchar* cc,
  const gchar* message_id) {
  ep_mail_info_t* mail_info;

  mail_info = g_malloc(sizeof(ep_mail_info_t));
  mail_info->from = g_string_new(from ? from : "");
  mail_info->subject = g_string_new(subject ? subject : "");
  mail_info->date = g_string_new(date ? date : "");
  mail_info->reply_to = g_string_new(reply_to ? reply_to : "");
  mail_info->cc = g_string_new(cc ? cc : "");
  mail_info->message_id = g_string_new(message_id ? message_id : "");

  return mail_info;
}

