#include "file_handler.h"

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

#define MAINDIR "/var/lib/gather/"

static const char* file_names[] =
{
  MAINDIR"metric_plugin",
  MAINDIR"repository_plugin",
  MAINDIR"metric_value_definition"
};

typedef int (*getter)(FILE*, void**);
typedef int (*writer)(FILE*, linked_list*);
typedef int (*clearer)(linked_list*);

FILE* open_file(const char* filename, const char* mode)
{
  return fopen(filename, mode);
}

int close_file(FILE* fd)
{
  return fclose(fd);
}

mp_list* create_metric_plugin(char* mdcn, char* mpn)
{
  mp_list* ml = malloc(sizeof(mp_list));
  ml->metric_definition_class_name = strdup(mdcn);
  ml->metric_plugin_name = strdup(mpn);
  ml->next = NULL;
  return ml;
}

rp_list* create_repository_plugin(char* mdcn, char* rpn, char* Ctpn)
{
  rp_list* rl = malloc(sizeof(rp_list));
  rl->metric_definition_class_name = strdup(mdcn);
  rl->repository_plugin_name = strdup(rpn);
  rl->cim_translation_plugin_name = strdup(Ctpn);
  rl->next = NULL;
  return rl;
}

mvd_list* create_metric_value_definition(char* mdcn, char* mvcn)
{
  mvd_list* ml = malloc(sizeof(mvd_list));
  ml->metric_definition_class_name = strdup(mdcn);
  ml->metric_value_class_name = strdup(mvcn);
  ml->next = NULL;
  return ml;
}

int get_next_mp(FILE* f, void** ml)
{
  char mdcn[256];
  char mpn[256];
  int ret = fscanf(f, "%s %s", mdcn, mpn);
  *ml = create_metric_plugin(mdcn, mpn);
  return ret;
}

int get_next_rp(FILE* f, void** rl)
{
  char mdcn[256];
  char rpn[256];
  char Ctpn[256];
  int ret = fscanf(f, "%s %s %s", mdcn, rpn, Ctpn);
  *rl = create_repository_plugin(mdcn, rpn, Ctpn);
  return ret;
}

int get_next_mvd(FILE* f, void** ml)
{
  char mdcn[256];
  char mvcn[256];
  int ret = fscanf(f, "%s %s", mdcn, mvcn);
  *ml = create_metric_value_definition(mdcn, mvcn);
  return ret;
}

static const getter getters[] =
{
  get_next_mp,
  get_next_rp,
  get_next_mvd
};

int fill(FILE* f, linked_list* ll, enum value_type t)
{
  void* current = NULL;
  void* prev = NULL;
  while(getters[t](f, &current) != EOF)
  {
    if (!ll->value_list.metric_plugin)
    {
      ll->value_list.metric_plugin = current;
    }

    if (prev)
      ((mp_list*)prev)->next = current;

    prev = current;
  }
  return 0;
}

int add_metric_plugin(
    linked_list* ll,
    char* metric_definition_class_name,
    char* metric_plugin_name)
{
  if (!ll->value_list.metric_plugin)
  {
    ll->value_list.metric_plugin = create_metric_plugin(
      metric_definition_class_name, metric_plugin_name);
  }
  else
  {
    mp_list* mln = ll->value_list.metric_plugin;
    mp_list* mlp = NULL;
    while(mln)
    {
      mlp = mln;
      if (strcmp(mlp->metric_definition_class_name,
        metric_definition_class_name) == 0)
      {
        mlp->metric_plugin_name = strdup(metric_plugin_name);
        return 0;
      }
      mln = mln->next;
    }
    mlp->next = create_metric_plugin(metric_definition_class_name,
      metric_plugin_name);
  }
  return 0;
}

int add_repository_plugin(
    linked_list* ll,
    char* metric_definition_class_name,
    char* repository_plugin_name,
    char* cim_translation_plugin_name)
{
  if (!ll->value_list.repository_plugin)
  {
    ll->value_list.repository_plugin = create_repository_plugin(
      metric_definition_class_name, repository_plugin_name,
      cim_translation_plugin_name);
  }
  else
  {
    rp_list* rln = ll->value_list.repository_plugin;
    rp_list* rlp = NULL;
    while(rln)
    {
      rlp = rln;
      if (strcmp(rlp->metric_definition_class_name,
        metric_definition_class_name) == 0)
      {
        rlp->repository_plugin_name = strdup(repository_plugin_name);
        rlp->cim_translation_plugin_name = strdup(cim_translation_plugin_name);
        return 0;
      }
      rln = rln->next;
    }
    rlp->next = create_repository_plugin(metric_definition_class_name,
      repository_plugin_name, cim_translation_plugin_name);
  }
  return 0;
}

int add_metric_value_definition(
    linked_list* ll,
    char* metric_definition_class_name,
    char* metric_value_class_name)
{
  if (!ll->value_list.metric_value_definition)
  {
    ll->value_list.metric_value_definition = create_metric_value_definition(
      metric_definition_class_name, metric_value_class_name);
  }
  else
  {
    mvd_list* mln = ll->value_list.metric_value_definition;
    mvd_list* mlp = NULL;
    while(mln)
    {
      mlp = mln;
      if (strcmp(mlp->metric_definition_class_name,
        metric_definition_class_name) == 0)
      {
        mlp->metric_value_class_name = strdup(metric_value_class_name);
        return 0;
      }
      mln = mln->next;
    }
    mlp->next = create_metric_value_definition(metric_definition_class_name,
      metric_value_class_name);
  }
  return 0;
}

int write_mp(FILE* f, linked_list* ll)
{
  mp_list* ml = ll->value_list.metric_plugin;
  while(ml)
  {
    fprintf(f, "%s %s\n", ml->metric_definition_class_name,
      ml->metric_plugin_name);
    ml = ml->next;
  }
  return 0;
}

int write_rp(FILE* f, linked_list* ll)
{
  rp_list* rl = ll->value_list.repository_plugin;
  while(rl)
  {
    fprintf(f, "%s %s %s\n", rl->metric_definition_class_name,
      rl->repository_plugin_name, rl->cim_translation_plugin_name);
    rl = rl->next;
  }
  return 0;
}

int write_mvd(FILE* f, linked_list* ll)
{
  mvd_list* ml = ll->value_list.metric_value_definition;
  while(ml)
  {
    fprintf(f, "%s %s\n", ml->metric_definition_class_name,
      ml->metric_value_class_name);
    ml = ml->next;
  }
  return 0;
}

int clear_mp(linked_list* ll)
{
  mp_list* ml = ll->value_list.metric_plugin;
  mp_list* mn = NULL;
  while(ml)
  {
    mn = ml->next;
    free(ml->metric_definition_class_name);
    free(ml->metric_plugin_name);
    free(ml);
    ml = mn;
  }
  free(ll);
  return 0;
}

int clear_rp(linked_list* ll)
{
  rp_list* rl = ll->value_list.repository_plugin;
  rp_list* rn = NULL;
  while(rl)
  {
    rn = rl->next;
    free(rl->metric_definition_class_name);
    free(rl->cim_translation_plugin_name);
    free(rl->repository_plugin_name);
    free(rl);
    rl = rn;
  }
  return 0;
  free(ll);
}

int clear_mvd(linked_list* ll)
{
  mvd_list* ml = ll->value_list.metric_value_definition;
  mvd_list* mn = NULL;
  while(ml)
  {
    mn = ml->next;
    free(ml->metric_definition_class_name);
    free(ml->metric_value_class_name);
    free(ml);
    ml = mn;
  }
  free(ll);
  return 0;
}

static const writer writers[] =
{
  write_mp,
  write_rp,
  write_mvd
};

static const clearer clearers[] =
{
  clear_mp,
  clear_rp,
  clear_mvd
};

linked_list* load_list(enum value_type t)
{
  linked_list* ll = malloc(sizeof(linked_list));
  ll->type = t;
  ll->value_list.metric_plugin = NULL;
  FILE* f = open_file(file_names[t], "r");
  if (f)
  {
    fill(f, ll, t);
    close_file(f);
  }
  return ll;
}

int save_list(linked_list* ll)
{
  FILE* f = open_file(file_names[ll->type], "w");
  if (!f)
    return -1;
  writers[ll->type](f, ll);
  close_file(f);
  return 0;
}

int clear_list(linked_list* ll)
{
  if (!ll)
    return 0;
  return clearers[ll->type](ll);
}

