#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ssdef.h>
#include <ctype.h>
#include "base_includes.h"
#include "list.h"
#include "level.h"
#include "mycompare.h"

/* ------------------------------------------------------------------------ */
/* typedef struct head_struct *head_ptr; */

typedef struct list_struct *list_ptr;
struct list_struct {
  char *text;
  int type;
  list_ptr older;
  list_ptr newer;
};

struct head_struct {
  list_ptr newest;
  list_ptr curr;
  list_ptr oldest;
  int count, max;
};
/* ------------------------------------------------------------------------ */
static head_ptr input = NULL;

head_ptr create_head(int max)
{
  head_ptr tmp;
  if (!(tmp = (head_ptr)malloc(sizeof(struct head_struct))))
    return NULL;
  tmp->newest = tmp->curr = tmp->oldest = NULL;
  tmp->count = 0;
  tmp->max = max;
  return tmp;
}

extern server_ptr gsrv;

void set_list_limit(head_ptr list, int limit)
{
  int change;
  list_ptr temp;
  int loop;

  if (limit<0) limit = 0;
  change = list->count - limit;
  list->max = limit;
  if (list->count > limit) list->count = limit;
  for (loop=0;loop<change;loop++)
  {
    temp = list->oldest;
    list->oldest = list->oldest->newer;
    list->oldest->older = NULL;
    if (list->curr == temp) list->curr = list->oldest;
    if (list->newest == temp) list->newest = list->oldest;
    myfree(temp);
  }
}

void set_hist_limit(int max)
{
  int old;
  old = sys$setast(0);
  yell("hist limit to %d", max);
  set_list_limit(input, max);
  if (old == SS$_WASSET) sys$setast(1);
}

void init_input_list(int max)
{
  input = create_head(max);
}

void add_list(list, str, type)
  head_ptr list;
  char *str;
  int type;
{
  list_ptr temp;
  int size;

  size = sizeof(struct list_struct) + strlen(str) + 1;
  if ((temp = (list_ptr)mymalloc(size)) == NULL) {
    say("Could not allocate space for malloc - add_list");
    return;
  }
  temp->text = (char *)((int)temp + sizeof(struct list_struct));
  (void)strcpy(temp->text, str);
  temp->type = type;
  temp->newer = NULL;
  temp->older = list->newest;
   
  if (list->newest) list->newest->newer = temp;
  if (!list->oldest) list->oldest = temp;
  list->newest = temp;
  list->curr = list->newest;
  (list->count)++;
  set_list_limit(list, list->max);
}

void add_input_list(str)
  char *str;
{
  int old;
  old = sys$setast(0);
  add_list(input, str, 0);
  if (input) input->curr = NULL;
  if (old == SS$_WASSET) sys$setast(1);
}

char *get_prev_list(head_ptr lst, int older, int allow_wrap)
{
  if (!lst) return NULL;
  if (older) {
    if (!lst->curr) lst->curr = lst->newest;
    else lst->curr = lst->curr->older;
    if (!lst->curr)
      if (allow_wrap) lst->curr = lst->newest;
      else return NULL;
  } else {
    if (!lst->curr) lst->curr = lst->oldest;
    else lst->curr = lst->curr->newer;
    if (!lst->curr)
      if (allow_wrap) lst->curr = lst->oldest;
      else return NULL;
  }
  if (lst->curr) return lst->curr->text;
  return NULL;
}

char *get_prev_input(int older, int allow_warp)
{
  return get_prev_list(input, older, allow_warp);
}

/* ------------------------------------------------------------------------- */
void user_lastlog(char *string, int varpar, char *subparams)
{
  char word[MAXLEN], match[MAXLEN], re_match[MAXLEN], *end;
  int flags = 0, num = 0, comm, dummy, oldlevel, max_d = 0, inv = 0;
  int around_d = 0, since_last, have_left, loop;
  win_ptr oldwin;
  list_ptr walk = NULL, twalk;
  head_ptr llog;
  extern win_ptr gwin;

  llog = get_window_lastlog(gwin);
  if (!llog) {
    say("*** No lastlog available.");
    return;
  }

  *match = 0;
  while (*string=='-') {
    string++;
    (void)grab_word(&string, ' ', word);
    if (!lookup_level(word, &flags, &inv)) {
      if (strncasecmp(word, "NUM", strlen(word)) == 0)
      {
        grab_word(&string, ' ', word);
        num = atoi(word);
      }
      else if (strncasecmp(word, "AROUND", strlen(word)) == 0)
      {
        grab_word(&string, ' ', word);
        around_d = atoi(word);
      }
      else if (strncasecmp(word, "MAX_DEPTH", strlen(word)) == 0)
      {
        grab_word(&string, ' ', word);
        max_d = atoi(word);
      }
      else if (strncasecmp(word, "PATTERN", strlen(word)) == 0)
      {
        char temp[MAXLEN], *w = temp;
        while (isspace(*string)) string++;
        if (*string == '\"') {
          w++;
          grab_word(&string, '\"',  match);
        } else grab_word(&string, ' ', match);
      } else {
        say("*** Flag \"%s\" unknown or ambiguous.", word);
        return;
      }
    }
  }
  end = string;
  while (isspace(*end)) end++;
  if (*end) {
    if (atoi(end) != 0) num = atoi(end);
    else strcpy(match, string);
  }
  if (!flags) flags = ALL;
  if (!strchr(match, '*') && !strchr(match, '%') && !strchr(match, '?'))
    sprintf(re_match, "*%s*", match);
  else
    strcpy(re_match, match);
  oldwin = set_outwin(gwin);
  oldlevel = set_loglevel(LASTLOG);
  say("*** Lastlog [%s] num: %d max_depth: %d:", re_match, num, max_d);
  if (max_d == 0)
    walk = llog->oldest;
  else {
    int count = 1;
    walk = llog->newest;
    while (walk && (count++ < max_d) && walk->older) walk = walk->older;
  }
  if (num != 0) {
    list_ptr w = llog->newest;
    int count = 0;
    while (w && (count<num) && (w!=walk))
    {
      if ((w->type & flags) && (wild_match(re_match, w->text) >= 0))
        count++;
      if (count<num) w = w->older;
    }
    walk = w;
  }
  since_last = 0;
  while (walk)
  {
    since_last++;
    if ((walk->type & flags) && (wild_match(re_match, walk->text) >= 0))
    {
      if (around_d > 0)
      {
        loop = 0;
        twalk = walk;
        while ((loop < since_last) && (loop < around_d) &&
               (twalk) && (twalk->older))
        {
          twalk = twalk->older;
          loop++;
        }
        while (twalk && (twalk != walk))
        {
          say("%s", twalk->text);
          twalk = twalk->newer;
        }
      }
      if (since_last >= 0) say("%s", walk->text);
      if (around_d > 0)
      {
        for (loop=0; loop<around_d; loop++)
        {
          walk = walk->newer;
          if (walk) say("%s", walk->text);
        }
      }
      since_last = 0;
    }
    walk = walk->newer;
  }
  say("*** End of lastlog");
  set_outwin(oldwin);
  set_loglevel(oldlevel);
}

