#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <iconv.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include "utfchar.h"
#include "hhentry.h"
#include "tree.h"
#include "hhdict.h"

static int _n_trees = 0;
static Tree *_p_trees = NULL;
static int n_dictionary_creation_counter = 0;

static Bool dictionary_get_header_info (FILE *fp, DictionaryHeader *hdr);
static void dictionary_build_tree_from_file (FILE *fp, Tree *tree);

extern void get_int24_from_file (int *val, FILE *fp);

void
get_inmemory_dictionary (int *n_trees, Tree **p_trees)
{
  if (!n_trees || !p_trees)
    return;
  *n_trees = _n_trees;
  *p_trees = _p_trees;
}

Bool
construct_binary_tree_from_file
(char *dic_path, Tree **trees_return, int *n_trees_return )
{
  FILE *fp;
  struct stat buf;
  int ret;
  int i;
  DictionaryHeader hdr;

  assert (dic_path != NULL);
  n_dictionary_creation_counter += 1;
  
  if (n_dictionary_creation_counter > 1){
    *trees_return = _p_trees;
    *n_trees_return = _n_trees;
  }
    
  
  if (dic_path == NULL){
    fprintf (stderr, "NULL path for dictionary was passed\n");
    return False;
  }
  
  ret = stat (dic_path, &buf);
  
  if (ret){
    /* in most case, the file isn't likely to exist */
    perror ("dictionary_build_btree_from_file error");
    return False;
  }
  
  fp = fopen (dic_path, "r");
    
  /* how many tree do I need to build */
  dictionary_get_header_info (fp, &hdr);
  
  _n_trees = hdr.table_size;
  _p_trees = (Tree *) tree_n_new (hdr.table_size);
  
  for (i = 0; i < _n_trees; i++)
    dictionary_build_tree_from_file (fp, &_p_trees[i]);

  fclose (fp);
  *n_trees_return = _n_trees;
  *trees_return = _p_trees;
  
  return True;
}

static void
dictionary_free ()
{
  /*
  static int _n_trees = 0;
  static Tree *_p_trees = NULL;
  */
  int i;
  Tree *p_tree;
  for (i = 0 ; i < _n_trees; i++){
    p_tree =  &_p_trees[i];
    
    
  }
}

static Bool
dictionary_get_header_info (FILE *fp, DictionaryHeader *hdr)
{
  int version_signature;
  
  assert (fp != NULL);
  assert (hdr != NULL);
  
  if (!hdr || !fp){
    fprintf (stderr, "dictionary_get_header_info error: ptr or hdr is null\n");
    return False;
  }
  
  fread (hdr->dict_name, strlen(IIIM_KO_LE_DIC) + 1, 1,  fp);
  if (strcmp (hdr->dict_name, IIIM_KO_LE_DIC)){
    /* this is not ko-le dictionary, cancelling... */
    fprintf (stderr, "dictionary_get_header_info error: dict_name is wrong\n");
    return False;
  }
  /* read version signature from the file.
     but, I don't care about version for now.. */  
  get_int24_from_file (&version_signature, fp);

  /* read info for number of tables */
  get_int24_from_file (&hdr->table_size, fp);

  return True;
}

/* read  each bucket */
static void
dictionary_build_tree_from_file (FILE *fp, Tree *tree)
{
  int i_total;
  int i;
  HHItem *hhitem;
  TreeNode *node;
  
  /* number of hangul-hanja pairs in this bucket */
  get_int24_from_file (&i_total, fp);

  for (i = 0 ; i < i_total; i++){
    hhitem = hhitem_new ();
    hhitem_read_from_file (fp, hhitem);
    
    node = (TreeNode *) tree_node_new_with_hhitem (hhitem);
    tree_insert (tree, node);
    
    hhitem_free (hhitem);
    hhitem = NULL;
  }
}


Bool
dictionary_search_hanja_candidates_in_utf8
(char *u8_hangul, int *n_return, unsigned char***u8_hanja_return )
{
  UTFCHAR *p16char = NULL;
  int hash_val;
  int i;
  TreeNode *search_result;
    
  assert (u8_hangul != NULL);
  assert (n_return != NULL);
  assert (u8_hanja_return != NULL);

  if (u8_hangul == NULL || !strlen (u8_hangul)){
    fprintf (stdout,
	     "dictionary_search_hanja_candidates_in_utf8 error: "
	     "u8_hangul is null or zero length");
    return False;
  }
  if (n_return == NULL || u8_hanja_return == NULL){
    fprintf (stdout,
	     "dictionary_search_hanja_candidates_in_utf8 error: "
	     "n_return or u8_hanja_return is NULL");
    return False;
  }
  p16char = _utfchar_convert_u8_to_u16 (u8_hangul);
  if (!p16char){
    fprintf (stdout, "dictionary_search_hanja_candidates_in_utf8 error: "
	     "_utfchar_convert_u8_to_u16 failed\n");
    return False;
  }

  hash_val = hash (p16char);
  search_result = tree_search_hangul (_p_trees + hash_val, u8_hangul);
  if (!search_result){
    fprintf (stdout, "dictionary_search_hanja_candidates_in_utf8 error"
	     "no candidates found\n");
    if (p16char)
      free (p16char);

    return False;
  }
  *n_return = search_result->data->n_hanja;
  *u8_hanja_return =
    (unsigned char **) calloc (*n_return, sizeof (unsigned char *));

  for (i = 0 ; i < *n_return; i++)
    (*u8_hanja_return)[i]= strdup (search_result->data->hanja_list[i]);
  if (p16char)
    free (p16char);
  
  return True;
}



Bool
dictionary_search_hanja_candidates_in_utf16
(UTFCHAR *u16_hangul, int *n_return, UTFCHAR ***u16_hanja_return )
{
  int hash_val;
  int i;
  unsigned char *p8hangul = NULL;
  UTFCHAR *conv_return;
  TreeNode *search_result;
    
  assert (u16_hangul != NULL);
  assert (n_return != NULL);
  assert (u16_hanja_return != NULL);

  if (u16_hangul == NULL || !_utfchar_length (u16_hangul)){
    fprintf (stdout,
	     "dictionary_search_hanja_candidates_in_utf16 error: "
	     "u16_hangul is null or zero length");
    return False;
  }
  if (n_return == NULL || u16_hanja_return == NULL){
    fprintf (stdout,
	     "dictionary_search_hanja_candidates_in_utf16 error: "
	     "n_return or u16_hanja_return is NULL");
    return False;
  }

  hash_val = hash (u16_hangul);
  p8hangul = (unsigned char *) _utfchar_convert_u16_to_u8 (u16_hangul);
  
  search_result = tree_search_hangul (_p_trees + hash_val, p8hangul);
  if (!search_result){
    fprintf (stdout, "dictionary_search_hanja_candidates_in_utf16 error"
	     "no candidates found\n");
    if (p8hangul)
      free (p8hangul);

    return False;
  }
  
  *n_return = search_result->data->n_hanja;
  *u16_hanja_return =
    (UTFCHAR **) calloc (*n_return, sizeof (UTFCHAR *));

  for (i = 0 ; i < *n_return; i++){
    conv_return = 
      _utfchar_convert_u8_to_u16 (search_result->data->hanja_list[i]);
    /*
    if (conv_return == NULL)
          (*u16_hanja_return)[i] = NULL;
    */
    (*u16_hanja_return)[i] = conv_return;
  }

  if (p8hangul)
    free (p8hangul);
  return True;
}



