/* Auxillary functions for pptop

   Copyright (C) 2001, 2002 Paul Emsley.
   Copyright (C) 2002 James A. Morrison.

   Written by Paul Emsley <paule@chem.gla.ac.uk>

   This program 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 2, or (at
   your option) any later version.

   This program 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 this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA */

/* Create a ps_stream, connect to a file-analogue (via
   open_memstream), make a function
   proc_stat_list_fmt_analogue(ps_stream) which writes stuff to the
   ps_stream.

*/

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif /* _GNU_SOURCE */

#include <stdlib.h>		/* for getenv */
#include <unistd.h>
#include <error.h>
#include <termios.h>
#include <fcntl.h>
#include <pthread.h>
#include <libintl.h>
#include <locale.h>

#define _(STRING) gettext (STRING)
#define gettext_noop(STRING) STRING
#define N_(STRING) gettext_noop (STRING)

#include "aux.h"
#include "help.h"
#include "specs.h"
#include "filter.h"
#include "title.h"

int icount = 0;			/* global counter */

int adjust_headers (int);

/* This is a function used for testing the stream output -> string stuff */
int
proc_stat_list_fmt_analogue (struct ps_stream *strum)
{

  switch (icount)
    {

    case 0:
      fwrite ("0zero\n1zero", 1, 11, strum->stream);
      fwrite ("\n", 1, 1, strum->stream);
      icount = 1;
      break;

    case 1:
      fwrite ("0one\n1one\n2one\n", 1, 15, strum->stream);
      fwrite ("\n", 1, 1, strum->stream);
      icount = 2;
      break;

    case 2:
      fwrite ("0to be\n1or not to be", 1, 20, strum->stream);
      fwrite ("\n", 1, 1, strum->stream);
      icount = 3;
      break;

    case 3:
      fwrite ("0three\n1pea\n2oh", 1, 15, strum->stream);
      fwrite ("\n", 1, 1, strum->stream);
      icount = 0;
      break;

    default:
      printf ("Cannot happen\n");

    }
  return 0;
}

int
init_ps_stream (struct ps_stream *strum)
{
  char *bp;
  size_t size;

  /* This idea (open_memstream) is from James Morrison. You don't
     want to know how I was doing this before. */
  strum->stream = open_memstream (&bp, &size);
  return 0;
}

char *
get_strum_string (struct ps_stream *strum)
{
  /* get some chars from the FILE structure and then rewind */
  char *outstr;
  long current_end_pos, size;

  size = current_end_pos = ftell (strum->stream);

  rewind (strum->stream);
  outstr = (char *) calloc (size + 1, 1);
  fread (outstr, size, 1, strum->stream);
  rewind (strum->stream);

  return outstr;
}


/* Surely there must be a function somewhere that does this already?

   Return a list of strings, the last string in the list being NULL
   and signifying that there are no more strings.

   My puny c, skills I'm afraid.  I felt like I was fighting the
   language to achieve the result.  That is not a good sign, in my
   experience.
   
 */

/* Return NULL on no breaker */
char *
next_breaker (char *strum_string)
{

  char *ptr;
  size_t len;

  ptr = strpbrk (strum_string, "\n");
  if (ptr != NULL)
    {
      return ptr;
    }
  else
    {
      len = strlen (strum_string);
      return ptr + len;
    }
}


/* Use glib lists instead of this char** nonsense? */
char **
string_splitter (char *strum_string, int nlines)
{

  char *ptr;
  char **ptrptr, **start_ptrptr;
  char *new_str;
  size_t size;
  int line_counter = 0;

/* This is really rubbish and should be fixed.  We presume that 
   there are 400 or less lines output by proc_stat_list_fmt(). 
   If there are more, this code will break. */

  start_ptrptr = (char **) malloc ((nlines + 1) * (sizeof (char *)));
  if (!start_ptrptr)
    {
      undo_curses ();
      error (1, errno, "No memory available");
    }
  ptrptr = start_ptrptr;

  while (((ptr = next_breaker (strum_string)) != NULL) &&
	 (line_counter < nlines))
    {
      size = (ptr - strum_string + 1);
      new_str = (char *) malloc (size);

      /* An error occured. */
      if (new_str == NULL)
	{
	  undo_curses ();
	  printf ("ptr: %p, strum_string: %p, size: %zd\n",
		  ptr, strum_string, size);
	  printf ("Ooops: mallocing %zd in string_splitter failed\n",
		  (size_t) nlines * sizeof (char *) + 1);
	  exit (1);
	}
      strncpy (new_str, strum_string, size);
      new_str[size - 1] = '\0';

      *ptrptr = new_str;
      ptrptr++;
      line_counter++;
      strum_string = ptr + 1;	/* set up for next round */
    }
  /* Terminate the list */
  *ptrptr = NULL;

  return start_ptrptr;
}

void
print_strings (char **str_list)
{

  char *ptr;

  while ((ptr = *str_list) != NULL)
    {

      /* printf("got string: %s", ptr);  */
      str_list++;
    }
}

/* 'limit' is the number of ps_strings that will fit onto
   the screen - we don't have to use it - because curses can
   try to write off-screen and no bad things happen, we just
   don't see it. */
void
curses_print_strings (char **str_list, int limit, int offset)
{
  int line = offset;
  char *ptr;

  while ((ptr = *str_list) != NULL && (limit + offset) > line)
    {
      line++;
      mvaddstr (line, 0, ptr);
      clrtoeol ();

      /* now we have printed the string at ptr, we can free ptr */
      free (ptr);
      str_list++;
    }
}


/* A testing function, suffering from bit rot now!!! */
void
control (void)
{

  struct ps_stream *strum;
  int i;
  char *strum_string;
  int ii;
  long current_end_pos;
  char **ps_strings_list;

  i = init_ps_stream (strum);
  current_end_pos = ftell (strum->stream);
/*    printf("staring current_end_pos: %ld\n", current_end_pos); */

  /* repeat this a few time */

  for (ii = 0; ii < 5; ii++)
    {
      i = proc_stat_list_fmt_analogue (strum);
      strum_string = get_strum_string (strum);
/*       printf("strum_strings: %d :%s:\n", ii, strum_string); */
      printf ("ii: %d\n", ii);
      ps_strings_list = string_splitter (strum_string, LINES);
      curses_print_strings (ps_strings_list, LINES, 4);
    }

}

/* A function for debugging */
void
log_char (char *in_str)
{

  FILE *fd;

  fd = fopen ("pp.log", "w");
  if (in_str == NULL)
    {
      fwrite ("in_str NULL\n", 1, 12, fd);
    }
  else
    {
      fwrite (in_str, 1, strlen (in_str), fd);
    }
  fclose (fd);
}

/* return a string that is filled to length new_length
   with the filler char.  If the length of the 
   in_str string is longer than or equal in length to new_length, then 
   return a copy of in_str (so that in_str can be freed
   in all cases in the calling function).
*/
char *
extend_string (char **in_str, int new_length, char filler)
{

  char *out_str;

  out_str = (char *) malloc (new_length + 1);
  if (!out_str)
    {
      undo_curses ();
      error (1, errno, "No memory");
    }

  strncpy (out_str, *in_str, new_length + 1);
  {
    int i;
    for (i = (strlen (*in_str)); i < new_length; i++)
      {
	*(out_str + i) = filler;
      }
    *(out_str + new_length) = '\0';
  }

  free (*in_str);
  (*in_str) = out_str;
  return out_str;
}

void
bold_text (int line, int col, char *str)
{
  attron (A_BOLD);
  mvaddstr (line, col, str);
  attroff (A_BOLD);
}

void
rv_text (int line, char *str)
{
  attron (A_REVERSE);
  mvaddstr (line, 0, str);
  attroff (A_REVERSE);
}

void
do_curses_setup (void)
{
/*   struct termios info; */
  int in = fileno (stdin);

  initscr ();
  noecho ();

/*  nodelay (stdscr, TRUE); */ 	/* if FALSE, keyboard input works */
  clearok (stdscr, TRUE); 
  leaveok (stdscr, TRUE);   
  keypad (stdscr, TRUE); 	/* I don't know where this goes yet */
  cbreak ();  

  /* compensate for cbreak () not working */
/*  tcgetattr (in, &info);
  info.c_lflag &= ~ICANON;
  info.c_iflag &= ~ICRNL;
  info.c_lflag |= ISIG;
  info.c_cc[VMIN] = 1;
  info.c_cc[VTIME] = 0;
  tcsetattr (in, TCSANOW, &info); */ 
  fcntl (in, F_SETFL, fcntl (in, F_GETFL, 0) | O_NONBLOCK);

  /* nonl();  no difference */

}


  /* finish up */

void
undo_curses (void)
{
/*  int in = fileno (stdin); */

  curs_set (1);
  endwin ();   /* the next line doesn't seem neccesary */
/*  fcntl (in, F_SETFL, fcntl (in, F_GETFL, 0) & ~O_NONBLOCK); */

}


/* A testing routine */
void
print_lines_cols (void)
{

  char *q;

  q = getenv ("LINES");
  if (q == NULL)
    {
      printf ("No LINES ");
    }
  else
    {
      printf ("%s lines ", q);
    }
  q = getenv ("COLS");
  if (q == NULL)
    {
      printf ("No COLS\n");
    }
  else
    {
      printf ("%s cols\n", q);
    }
  printf ("int COLS: %d ", COLS);
  printf ("int LINES: %d\n", LINES);

  {
    int x, y;
    getmaxyx (curscr, y, x);

    printf ("max x and y: %d %d\n", x, y);

  }
}

				/* Argh.  Surely a library thing?  But
				   which one? */
int
max (int i, int j)
{
  return i > j ? i : j;
}
