#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "lookupbuf.h"
#include "utfchar.h"

static LookupBufferPage *hangul_lookupbuf_page_new (int , UTFCHAR **);
static void hangul_lookupbuf_append_page (LookupBufferStruct *, LookupBufferPage *);
static UTFCHAR *hangul_lookupbuf_page_get_candidate_string (LookupBufferPage *, int);
static void hangul_lookupbuf_page_free (LookupBufferPage *);
static void
hangul_lookupbuf_page_next_candidate (LookupBufferPage *page,
				      Bool *goto_next_page_return);
static void hangul_lookupbuf_page_reset (LookupBufferPage *);
static int hangul_lookupbuf_page_get_current_selection_index (LookupBufferPage *);
static void hangul_lookupbuf_page_set_focus (LookupBufferPage *page);
static void hangul_lookupbuf_page_unset_focus (LookupBufferPage *page);

LookupBufferStruct *
hangul_lookupbuf_new (int n_candidates, int count_per_page, UTFCHAR **string_list)
{
  LookupBufferStruct *lub = NULL;
  LookupBufferPage *lup = NULL;
  
  int i;
  int n_pages;
  int n_last_page_size = 0;
  
  assert (n_candidates > 0);
  assert (string_list != NULL);

  if ((n_candidates <= 0) || (string_list == NULL)){
    fprintf (stderr, "hangul_lookupbuf_new error: "
	     "n_candidates or string_list is not valid\n");
    return NULL;
  }

  lub = (LookupBufferStruct *) calloc (1, sizeof (LookupBufferStruct));

  lub->n_candidates = n_candidates;
  lub->n_items_per_page = count_per_page;
  lub->p_current_page = NULL;
  lub->page_list = NULL;

  n_last_page_size = n_candidates % lub->n_items_per_page;
  if (!n_last_page_size)
    n_pages = n_candidates / lub->n_items_per_page;
  else
    n_pages = n_candidates / lub->n_items_per_page +1;

  for (i = 0 ; i < n_pages; i++){
    if ( i < n_pages - 1){
	lup =
	  hangul_lookupbuf_page_new (lub->n_items_per_page,
				     string_list + (i * count_per_page));
    } else {
      if (n_last_page_size){
	lup =
	  hangul_lookupbuf_page_new ( n_last_page_size,
				      string_list + (i * count_per_page));
      } else {
	lup =
	  hangul_lookupbuf_page_new (lub->n_items_per_page,
				     string_list + (i *count_per_page));
      }
    }
    hangul_lookupbuf_append_page (lub, lup);
    lup = NULL;
  }
  lub->p_current_page = lub->page_list;
  hangul_lookupbuf_page_set_focus (lub->p_current_page);
  return lub;
}

Bool
hangul_lookupbuf_free (LookupBufferStruct *lub)
{
  LookupBufferPage *p, *pnext;
  
  if (lub == NULL){
    fprintf (stderr, "hangul_lookupbuf_free error: lub is already NULL\n");
    return True;
  }
  p = lub->page_list;
  while (p){
    pnext = p->next;
    hangul_lookupbuf_page_free (p);
    p = pnext;
  }
  return True;
}

LookupBufferPage *
hangul_lookupbuf_get_current_page (LookupBufferStruct *lub)
{
  assert (lub != NULL);
  if (lub == NULL){
    fprintf (stderr, "hangul_lookupbuf_get_current_page error :"
	     "lub is null\n");
    return NULL;
  }
  if (!lub->p_current_page){
    if (lub->page_list)
      lub->p_current_page = lub->page_list;
    else
      return NULL;
  }
  return lub->p_current_page;
}

int
hangul_lookupbuf_get_current_selection_index (LookupBufferStruct *lub)
{
  int i;
  assert (lub != NULL);
  if (lub == NULL){
    fprintf (stderr, "hangul_lookupbuf_get_current_selection_index error :"
	     "lub is null\n");
    return -1;
  }
  if (!lub->p_current_page)
    return -1;

  i = hangul_lookupbuf_page_get_current_selection_index (lub->p_current_page);
  return i;
}

void
hangul_lookupbuf_get_current_page_candidates (LookupBufferStruct *lub,
				   int *n_candidates_return,
				   int *n_current_candidate,
				   UTFCHAR ***candidates_list_return)
{
  int i;
  LookupBufferPage *p_page;
  
  assert (lub != NULL);
  if (lub == NULL){
    fprintf (stderr, "hangul_lookupbuf_get_current_page error :"
	     "lub is null\n");
    return;
  }
  if (!lub->p_current_page){
    if (lub->page_list)
      lub->p_current_page = lub->page_list;
    else {
      *n_candidates_return = 0;
      *n_current_candidate = -1;
      *candidates_list_return = NULL;
      return ;
    }
  }
  p_page = lub->p_current_page;

  *n_candidates_return = p_page->n_strings;
  *n_current_candidate = p_page->i_candidate;
  *candidates_list_return = (UTFCHAR **) calloc (*n_candidates_return,
						 sizeof (UTFCHAR *));
  for (i = 0 ; i < *n_candidates_return; i++)
    (*candidates_list_return)[i] = _utfchar_dup (p_page->string_list[i]);
  return;
}


static void
hangul_lookupbuf_append_page (LookupBufferStruct *lub, LookupBufferPage *lup)
{
  LookupBufferPage *p, *tail;
  p = lub->page_list;
  if (!p){
    lub->page_list = lup;
    lup->next = NULL;
    lup->prev = NULL;
  } else {
    while (p){
      tail = p;
      p = p->next;
    }
    p = tail;
    p->next = lup;
    lup->next = NULL;
    lup->prev = p;
  }
}
UTFCHAR *
hangul_lookupbuf_get_candidate_string (LookupBufferStruct *lub, int i)
{
  UTFCHAR *str_return;
  LookupBufferPage *page;
  assert (lub != NULL);
  if (lub == NULL){
    fprintf (stderr, "hangul_lookupbuf_get_candidate_string error :"
	     "lub is null \n");
    return NULL;
  }
  page = lub->p_current_page;
  if (!page){
    fprintf (stderr, "hangul_lookupbuf_get_candidate_string error :"
	     "lub->p_current_page is null \n");
    return NULL;
  }
  str_return = hangul_lookupbuf_page_get_candidate_string (page, i);
  return str_return;
} 

void
hangul_lookupbuf_next_page (LookupBufferStruct *lub)
{
  assert (lub != NULL);
  if (lub == NULL){
    fprintf (stderr, "hangul_lookupbuf_next_page error: "
	     "lub is NULL\n");
    return;
  }
  
  if (!lub->p_current_page){
    lub->p_current_page = lub->page_list;
    hangul_lookupbuf_page_set_focus (lub->p_current_page);
  } else {
    if (lub->p_current_page->next){
      hangul_lookupbuf_page_unset_focus (lub->p_current_page);
      lub->p_current_page = lub->p_current_page->next;
      hangul_lookupbuf_page_set_focus (lub->p_current_page);
    }
    else {
      hangul_lookupbuf_page_unset_focus (lub->p_current_page);
      lub->p_current_page = lub->page_list;
      hangul_lookupbuf_page_set_focus (lub->p_current_page);
    }
  }
}


void
hangul_lookupbuf_previous_page (LookupBufferStruct *lub)
{
  assert (lub != NULL);
  if (lub == NULL){
    fprintf (stderr, "hangul_lookupbuf_next_page error: "
	     "lub is NULL\n");
    return;
  }
  
  if (!lub->p_current_page){
    lub->p_current_page = lub->page_list;
    hangul_lookupbuf_page_set_focus (lub->p_current_page);
  } else {
    if (lub->p_current_page->prev){
      hangul_lookupbuf_page_unset_focus (lub->p_current_page);
      lub->p_current_page = lub->p_current_page->prev;
      hangul_lookupbuf_page_set_focus (lub->p_current_page);
    } else {
      /* This is not efficient... */
      LookupBufferPage *p = lub->page_list;
      LookupBufferPage *tail = NULL;
      hangul_lookupbuf_page_unset_focus (lub->p_current_page);
      while (p){
	tail = p;
	p = p->next;
      }
      p = tail;
      /* p is the last page */
      lub->p_current_page = p;
      hangul_lookupbuf_page_set_focus (lub->p_current_page);
    }
  }
}

int
hangul_lookupbuf_get_count_of_candidates_in_current_page (LookupBufferStruct *lub)
{
  LookupBufferPage *page;
  assert (lub != NULL);
  if (lub == NULL){
    fprintf (stderr,
	     "hangul_lookupbuf_get_count_of_candidates_in_current_page error :"
	     "lub is NULL\n");
    return -1;
  }
  page = lub->p_current_page;
  if (page == NULL){
    fprintf (stderr, 
	     "hangul_lookupbuf_get_count_of_candidates_in_current_page error :"
	     "lub->p_current_page is NULL\n");
    return -1;    
  }
  return page->n_strings;
}
static LookupBufferPage *
hangul_lookupbuf_page_new (int n_candidates, UTFCHAR **string_list)
{
  LookupBufferPage *page;
  int i;
  assert (n_candidates > 0);
  assert (string_list != NULL);

  if ((n_candidates <= 0) || (string_list == NULL)){
    fprintf (stderr, "hangul_lookupbuf_page_new error :"
	     "n_candidates  or string_list is NULL\n");
    return NULL;
  }

  if (n_candidates == 0)
    return NULL;

  page = (LookupBufferPage *) calloc (1, sizeof (LookupBufferPage));
  
  if (!page){
    fprintf (stderr, "hangul_lookupbuf_page_new error :"
	     "memory allocation failure\n");
    return NULL;
  }
  page->next = NULL;
  page->prev = NULL;
  page->n_strings = n_candidates;
  page->i_candidate = -1;
  page->string_list = (UTFCHAR **) calloc (n_candidates + 1, sizeof (UTFCHAR *));

  for (i = 0 ; i < n_candidates; i++){
    page->string_list[i] = _utfchar_dup (string_list[i]);
  }
  page->string_list[i] = NULL;
  return page;
}

void
hangul_lookupbuf_next_candidate (LookupBufferStruct *lub)
{
  Bool move_to_next_page = False;
  LookupBufferPage *p;

  assert (lub != NULL);
  if (lub == NULL){
    fprintf (stderr, "hangul_lookupbuf_next_candidate error :"
	     "lub is NULL\n");
    return;
  }

  if (!lub->p_current_page){
    if (!lub->page_list){
      fprintf (stderr, "hangul_lookupbuf_next_candidate error :"
	       "fatal error, not lookup page\n");
      return;
    }
    lub->p_current_page = lub->page_list;
  } 

  p = lub->p_current_page;
  
  hangul_lookupbuf_page_next_candidate (p, &move_to_next_page);
  if (move_to_next_page){
    hangul_lookupbuf_page_unset_focus (lub->p_current_page);
    if (p->next)
      lub->p_current_page = p->next;
    else
      lub->p_current_page = lub->page_list;
    hangul_lookupbuf_page_set_focus (lub->p_current_page);
  }
}



static void
hangul_lookupbuf_page_free (LookupBufferPage *page)
{
  int i, n_candidates;
  if (!page){
    fprintf (stderr, "hangul_lookupbuf_page_free error :"
	     "page is already NULL, can't free\n");
    return;
  }
  n_candidates = page->n_strings;
  for (i = 0 ; i < n_candidates; i++){
    if (page->string_list[i])
      free (page->string_list[i]);
  }
  
  free (page->string_list);
  free (page);
  return;
}

static void
hangul_lookupbuf_page_next_candidate (LookupBufferPage *page,
				   Bool *goto_next_page_return)
{
  assert (page != NULL);
  if (page == NULL){
    fprintf (stderr, "hangul_lookup_page_next_candidate error :"
	     "page is NULL\n");
    /* is this reasonable correctional way to do? */
    page->i_candidate = -1;
    if (goto_next_page_return)
      *goto_next_page_return = False;
  }

  page->i_candidate++;
  if (page->string_list[page->i_candidate] == NULL){
    if (goto_next_page_return)
      *goto_next_page_return = True;
    page->i_candidate = -1;
  } else {
    if (goto_next_page_return)
      *goto_next_page_return = False;
  }
  
}

static UTFCHAR *
hangul_lookupbuf_page_get_candidate_string (LookupBufferPage *page, int i)
{
  UTFCHAR *str_candidate;
  
  assert (page != NULL);
  if (page == NULL){
    fprintf (stderr, "hangul_lookupbuf_page_get_candidate_string error :"
	     "page is NULL\n");
    return NULL;
  }
  
  if ( (i < 0) || (i >= page->n_strings)){
    fprintf (stderr, "hangul_lookupbuf_page_get_candidate_string error :"
	     "index is outof boundary\n");
    return NULL;
  }
  
  str_candidate = _utfchar_dup (page->string_list [i]);
  return str_candidate;
}

static void
hangul_lookupbuf_page_reset (LookupBufferPage *page)
{
  assert (page != NULL);
  if (page == NULL){
    fprintf (stderr, "hangul_lookupbuf_page_reset error :"
	     "page is NULL\n");
    return;
  }
  page->i_candidate = -1;

}

static int
hangul_lookupbuf_page_get_current_selection_index (LookupBufferPage *page)
{
  assert (page != NULL);
  if (page == NULL){
    fprintf (stderr, "hangul_lookupbuf_page_get_current_selection_index error: "
	     "page is null\n");
    return -1;
  }
  return page->i_candidate;
}
      

static void
hangul_lookupbuf_page_set_focus (LookupBufferPage *page)
{
  assert (page != NULL);
  if (page == NULL){
    fprintf (stderr, "hangul_lookupbuf_page_set_focus error :"
	     "page is NULL\n");
    return;
  }
  page->i_candidate = 0;

}

static void
hangul_lookupbuf_page_unset_focus (LookupBufferPage *page)
{
  assert (page != NULL);
  if (page == NULL){
    fprintf (stderr, "hangul_lookupbuf_page_unset_focus error :"
	     "page is NULL\n");
    return;
  }
  page->i_candidate = -1;

}

#ifdef TESTRUN
void
hangul_lookupbuf_page_print (LookupBufferPage *lup, Bool got_focus, FILE *fp)
{
  int i;
  if (!fp)
    fp = stdout;
  
  fprintf (fp, "Page (%d candidates, candidate index %d)\n",
	  lup->n_strings, lup->i_candidate);
  fprintf (fp, "\t");
  for (i = 0 ; i < lup->n_strings; i++){
    if (got_focus &&(i == lup->i_candidate)){
      fprintf (fp, ">>%s<< ", _utfchar_convert_u16_to_u8 (lup->string_list[i]));
    } else
      fprintf (fp, "%s ", _utfchar_convert_u16_to_u8 (lup->string_list[i]));
  }
  fprintf (fp, "\n");
}

void
hangul_lookupbuf_print (LookupBufferStruct *lub, FILE *fp)
{
  LookupBufferPage *p;
  if (!fp)
    fp = stdout;
  p = lub->page_list;
  fprintf (fp, "Buffer info\n");
  fprintf (fp, "total : %d candidates\n", lub->n_candidates);
  while (p){
    if (p == lub->p_current_page)
      hangul_lookupbuf_page_print (p, True, fp);
    else
      hangul_lookupbuf_page_print (p, False, fp);

    p = p->next;
  }
}




#include "hhdict.h"
Bool
construct_binary_tree_from_file
(char *dic_path, Tree **trees_return, int *n_trees_return );

int
main (int argc, char **argv)
{
  Tree *trees;
  int n_trees;
  Bool mthd_return = False;
  UTFCHAR ga[] = { 0xac00 };
  UTFCHAR **cand = NULL;
  int n_candidates;
  int i;
  int ch;
  LookupBufferStruct *lub;
  FILE *fp;

  if (argc != 2){
    fprintf (stdout, "usage\n%s output_file\n", argv[1]);
    exit (-1);
  }
  fp = fopen (argv[1], "w");
  
  construct_binary_tree_from_file ("/usr/lib/im/locale/ko_KR/common/data/hhdict",
				   &trees, &n_trees);
  mthd_return = dictionary_search_hanja_candidates_in_utf16
    (ga, &n_candidates, &cand );
  if (!mthd_return){
    fprintf (stderr, "fail to search\n");
    exit (-1);
  }
#if 0  
  for (i = 0 ; i < n_candidates; i++){
    printf ("%s", _utfchar_convert_u16_to_u8 (cand[i]));
    printf ("\n");
  }
#endif
  lub = hangul_lookupbuf_new (n_candidates, 10, cand);
  hangul_lookupbuf_print (lub, fp);

  while (ch = fgetc (stdin)){
    if (ch == ' '){
      hangul_lookupbuf_next_candidate (lub);
      hangul_lookupbuf_print (lub, fp);
    } else if (ch == 'q')
      break;
  }
  fclose (fp);
  return 0;
				   
  
}
#endif
