/*
 * Copyright (c) 2004, Anthony Roberts
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright 
 * notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following
 * disclaimer in the documentation and/or other materials provided
 * with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */



#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include "arger.h"
#include "random.h"

char *myname;

/*
 * reads a line from the given input stream, stores the contents in buff,
 * possibly reallocates the buffer.
 */
size_t readline (FILE *incom, char **buff, size_t *buff_size) {
  size_t i = 0;
  int temp;

  temp = fgetc(incom);

  while (1) {
    /*
     * reallocates the buffer if necessary
     */
    if (i + 1 > *buff_size) {
      *buff_size *= 2;
      *buff = realloc(*buff, *buff_size);
      if (*buff == NULL) {
	fprintf(stderr, "%s: %s\n", myname, strerror(errno));
	exit(1);
      }
    }

    if (temp == '\n' || feof(incom)) {
      (*buff)[i++] = '\n';
      break;
    }
    else {
      (*buff)[i++] = (char) temp;
    }

    temp = fgetc(incom);
  }

  if (feof(incom)) {
    return -1;
  }
  else {
    return i;
  }
}

int main (int argc, char **argv) {
  program_state_t state = { (bool_t) true, RANDOM_SOURCE, NULL};
  char *buff, **lines, *temp;
  size_t buff_size = 1, ret_size;
  FILE *incom;
  int *lines_size, lines_count = 1, i = 0, count;
  double choice;

  myname=argv[0];

  getoption(argc, argv, 1, &state);

  if (state.incom != NULL) {
    if ((incom = fopen(state.incom, "r")) == NULL) {
      fprintf(stderr, "%s: couldn't open %s.\n", myname, state.incom);
      exit(1);
    }
  }
  else {
    incom = stdin;
  }

  if (init_rand48(state.source) != 0) {
    fprintf(stderr, "%s: couldn't read randomness.\n", myname);
    exit(1);
  }

  buff = malloc(buff_size);
  if (buff == NULL) {
    fprintf(stderr, "%s: %s\n", myname, strerror(errno));
    exit(1);
  }

  lines = malloc(sizeof(char*) * lines_count);
  lines_size = malloc(sizeof(int) * lines_count);
  
  
  /*
   * read all input lines into a buffer
   */
  i = 0;
  while((ret_size = readline(incom, &buff, &buff_size)) != -1) {
    /*
     * reallocate the buffer if necessary
     */
    if (i + 1 > lines_count) {
      lines_count *= 2;
      lines = realloc(lines, sizeof(char*) * lines_count);
      lines_size = realloc(lines_size, sizeof(int) * lines_count);
      if (lines == NULL) {
	fprintf(stderr, "%s: %s\n", myname, strerror(errno));
	exit(1);
      }
    }

    lines_size[i] = ret_size;
    lines[i++] = strdup(buff);
  }
  count = i;

  /* 
   * prints likes from the buffer at random, one at a time until they're all 
   * gone
   */
  while (count > 0) {
    choice = drand48();
    i = (int) (choice * (double) count);
    count--;

    write(STDOUT_FILENO, lines[i], lines_size[i]);

    /*
     * discard the line we just wrote
     */
    temp = lines[i];
    lines[i] = NULL;
    free(temp);
    lines_size[i] = -1;

    /*
     * lines[i] gets replaced with the last element. We don't care if
     * i == count because it wouldn't do anything.
     */
    lines[i] = lines[count];
    lines_size[i] = lines_size[count];
  }

  free(lines);
  free(lines_size);
  
  exit(0);
}

