/* This file is part of GDBM, the GNU data base manager.
   Copyright (C) 2016-2018 Free Software Foundation, Inc.

   GDBM is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   any later version.

   GDBM is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with GDBM. If not, see <http://www.gnu.org/licenses/>.    */

#include "gdbmtool.h"
#include <readline/readline.h>
#include <readline/history.h>

static char *pre_input_line;

static int
pre_input (void)
{
  if (pre_input_line)
    {
      rl_insert_text (pre_input_line);
      free (pre_input_line);
      pre_input_line = NULL;
      rl_redisplay ();
    }
  return 0;
}

static int
retrieve_history (char *str)
{
  char *out;
  int rc;

  rc = history_expand (str, &out);
  switch (rc)
    {
    case -1:
      yyerror (out);
      free (out);
      return 1;

    case 0:
      free (out);
      break;

    case 1:
      pre_input_line = out;
      return 1;

    case 2:
      printf ("%s\n", out);
      free (out);
      return 1;
    }
  return 0;
}

struct history_param
{
  int from;
  int count;
};
  
int
input_history_begin (struct handler_param *param, size_t *exp_count)
{
  struct history_param *p;
  int from = 0, count = history_length;

  switch (param->argc)
    {
    case 1:
      if (getnum (&count, param->argv[0]->v.string, NULL))
	return 1;
      if (count > history_length)
	count = history_length;
      else
	from = history_length - count;
      break;

    case 2:
      if (getnum (&from, param->argv[0]->v.string, NULL))
	return 1;
      if (from)
	--from;
      if (getnum (&count, param->argv[1]->v.string, NULL))
	return 1;

      if (count > history_length)
	count = history_length;
    }
  p = emalloc (sizeof *p);
  p->from = from;
  p->count = count;
  param->data = p;
  if (exp_count)
    *exp_count = count;
  return 0;
}

void
input_history_handler (struct handler_param *param)
{
  struct history_param *p = param->data;
  int i;
  HIST_ENTRY **hlist;
  FILE *fp = param->fp;
  
  hlist = history_list ();
  for (i = 0; i < p->count; i++)
    fprintf (fp, "%4d) %s\n", p->from + i + 1, hlist[p->from + i]->line);
}

#define HISTFILE_PREFIX "~/."
#define HISTFILE_SUFFIX "_history"

static char *
get_history_file_name (void)
{
  static char *filename = NULL;

  if (!filename)
    {
      char *hname;

      hname = emalloc (sizeof HISTFILE_PREFIX + strlen (rl_readline_name) +
		       sizeof HISTFILE_SUFFIX - 1);
      strcpy (hname, HISTFILE_PREFIX);
      strcat (hname, rl_readline_name);
      strcat (hname, HISTFILE_SUFFIX);
      filename = tildexpand (hname);
      free (hname);
    }
  return filename;
}
 
static char **
shell_completion (const char *text, int start, int end)
{
  char **matches;

  matches = (char **) NULL;

  /* If this word is at the start of the line, then it is a command
     to complete.  Otherwise it is the name of a file in the current
     directory. */
  if (start == 0)
    matches = rl_completion_matches (text, command_generator);

  return (matches);
}

void
input_init (void)
{
  /* Allow conditional parsing of the ~/.inputrc file. */
  rl_readline_name = (char *) progname;
  rl_attempted_completion_function = shell_completion;
  rl_pre_input_hook = pre_input;
  read_history (get_history_file_name ());
}

void
input_done (void)
{
  write_history (get_history_file_name ());
}

static void
instream_stdin_close (instream_t istr)
{
  free (istr);
}

static ssize_t
stdin_read_readline (instream_t istr, char *buf, size_t size)
{
  static char *input_line;
  static size_t input_length;
  static size_t input_off;
#define input_ptr() (input_line + input_off)
#define input_size() (input_length - input_off)
  size_t len = input_size ();
  if (!len)
    {
      if (input_line)
	{
	newline:
	  free (input_line);
	  input_line = NULL;
	  buf[0] = '\n';
	  return 1;
	}
      else
	{
	  char *prompt;
	again:
	  prompt = make_prompt ();
	  input_line = readline (prompt);
	  free (prompt);
	  if (!input_line)
	    return 0;
	  input_length = strlen (input_line);
	  input_off = 0;
	  if (input_length)
	    {
	      if (retrieve_history (input_line))
		{
		  free (input_line);
		  goto again;
		}
	    }
	  else
	    goto newline;
	  len = input_size ();
	  add_history (input_line);
	}
    }
  
  if (len > size)
    len = size;
  memcpy (buf, input_ptr (), len);
  input_off += len;

  return len;
} 

static ssize_t
instream_stdin_read (instream_t istr, char *buf, size_t size)
{
  if (istr->in_inter)
    return stdin_read_readline (istr, buf, size);
  return fread (buf, 1, size, stdin);
}

static int
instream_stdin_eq (instream_t a, instream_t b)
{
  return 0;
}

instream_t
instream_stdin_create (void)
{
  struct instream *istr;

  istr = emalloc (sizeof *istr);
  istr->in_name = "stdin";
  istr->in_inter = isatty (fileno (stdin));
  istr->in_read = instream_stdin_read;
  istr->in_close = instream_stdin_close;
  istr->in_eq = instream_stdin_eq;

  return istr;
}
