// sorted_list_solution1.c - solution 1 for the sorted
//                           list example

#include <stdio.h>
#include <stdlib.h>

// function declarations

void insertNumber(int number);
void deleteNumber(unsigned index);
void showList(unsigned char *input_line);
void showReverseList(unsigned char *input_line);
void showHelp(unsigned char *input_line);
void calculateSum(unsigned char *input_line);
void quitProgram(unsigned char *input_line);

void run();
int readLine(unsigned char *str,unsigned max_length);
void addToDispatcher(unsigned char input,
                     void (*handler)(unsigned char *input_line));
void removeFromDispatcher(unsigned char input);
void dispatch(unsigned char *input_line);

void callInsertNumber(unsigned char *input_line);
void callDeleteNumber(unsigned char *input_line);
void elementWithGivenIndexNotFound(unsigned index);
void commandNotRecognized(unsigned char *command_line);
void exitOnFatalErrorOutOfMemory();
void exitOnFatalErrorIllegalArgument(unsigned char* func_name);
unsigned char *cloneString(unsigned char* str);

// some constants

#define MAX_LINE_LENGTH 256
#define ILLEGAL_ARGUMENTS -1
#define FALSE 0
#define TRUE  1

// structure needed for our list and access functions for it

struct ListNode
{
  int number;
  struct ListNode *previous;
  struct ListNode *next;
};

struct ListNode *newListNode(int number);
void deleteListNode(struct ListNode *node);
void setNextInListNode(struct ListNode *node,
                       struct ListNode *next);
struct ListNode *getNextFromListNode(struct ListNode *node);
void setPreviousInListNode(struct ListNode *node,
                           struct ListNode *next);
struct ListNode *getPreviousFromListNode(struct ListNode *node);
int getNumberFromListNode(struct ListNode *node);

// our first and last nodes of the list

struct ListNode *first_node_ = NULL;
struct ListNode *last_node_ = NULL;
int num_elements_ = 0;
void (*handler_[256])(unsigned char *input_line);

//------------------------------------------------------------
int main()
{
  addToDispatcher('i',callInsertNumber);
  addToDispatcher('d',callDeleteNumber);
  addToDispatcher('s',showList);
  addToDispatcher('r',showReverseList);
  addToDispatcher('h',showHelp);
  addToDispatcher('c',calculateSum);
  addToDispatcher('q',quitProgram);
  run();
  return(0);
}

//------------------------------------------------------------
// creates a new node and inserts it in the correct position
// according to the given priority
void insertNumber(int number)
{
  struct ListNode *node = newListNode(number);
  struct ListNode *current = NULL;
  struct ListNode *previous = NULL;

  if (!first_node_) // this is the first node in the list
  {
    first_node_ = last_node_ = node;
    num_elements_++;
    return;
  }

  // find the correct position
  current = first_node_;
  while(current && (getNumberFromListNode(current) <= number))
  {
    previous = current;
    current = getNextFromListNode(current);
  }
  setPreviousInListNode(node,previous);
  setNextInListNode(node,current);
  if (!previous) // first node
    first_node_ = node;
  else
    setNextInListNode(previous,node);
  if (!current) // last node
    last_node_ = node;
  else
    setPreviousInListNode(current,node);
  num_elements_++;
}

//------------------------------------------------------------
void deleteNumber(unsigned index)
{
  unsigned help = index;
  struct ListNode *current = first_node_;
  struct ListNode *previous = NULL;
  struct ListNode *next = NULL;

  while(current && help--)
  {
    previous = current;
    current = getNextFromListNode(current);
  }
  if (!current)
  {
    elementWithGivenIndexNotFound(index);
    return;
  }
  next = getNextFromListNode(current);
  if (!previous) // first node is deleted
    first_node_ = next;
  else
    setNextInListNode(previous,next);
  if (!next) // last node is deleted
    last_node_ = previous;
  else
    setPreviousInListNode(next,previous);
  deleteListNode(current);
  num_elements_--;
}

//------------------------------------------------------------
void showList(unsigned char *input_line)
{
  struct ListNode *current = first_node_;
  unsigned index = 0;

  while(current)
  {
    printf("(%u: %d)",index++,getNumberFromListNode(current));
    current = getNextFromListNode(current);
  }
  printf("\n");
}

//------------------------------------------------------------
void showReverseList(unsigned char *input_line)
{
  struct ListNode *current = last_node_;
  unsigned index = num_elements_;

  while(current)
  {
    printf("(%u: %d)",--index,getNumberFromListNode(current));
    current = getPreviousFromListNode(current);
  }
  printf("\n");
}

//------------------------------------------------------------
void showHelp(unsigned char *input_line)
{
  printf("accepted commands:\n");
  printf("i <number>: insert <number>\n");
  printf("d <index>: delete number with index <index>\n");
  printf("s: show list\n");
  printf("r: show reverse list\n");
  printf("c: calculate sum\n");
  printf("h: show this help\n");
  printf("q: quit\n");
}

//------------------------------------------------------------
void calculateSum(unsigned char *input_line)
{
  struct ListNode *current = first_node_;
  int sum = 0;

  while(current)
  {
    sum += getNumberFromListNode(current);
    current = getNextFromListNode(current);
  }
  printf("%d\n",sum);
}

//------------------------------------------------------------
void quitProgram(unsigned char *input_line)
{
  exit(0);
}

//============================================================
// all ListNode access functions

//------------------------------------------------------------
struct ListNode *newListNode(int number)
{
  struct ListNode *node = malloc(sizeof(struct ListNode));
  if (!node)
    exitOnFatalErrorOutOfMemory();
  node->number = number;
  node->previous = node->next = NULL;
  return(node);
}


//------------------------------------------------------------
void deleteListNode(struct ListNode *node)
{
  if (!node)
    exitOnFatalErrorIllegalArgument("deleteListNode");
  free(node);
  return;
}

//------------------------------------------------------------
void setNextInListNode(struct ListNode *node,
                       struct ListNode *next)
{
  if (!node)
    exitOnFatalErrorIllegalArgument("setNextInListNode");
  node->next = next;
}

//------------------------------------------------------------
struct ListNode *getNextFromListNode(struct ListNode *node)
{
  if (!node)
    exitOnFatalErrorIllegalArgument("getNextFromListNode");
  return(node->next);
}

//------------------------------------------------------------
void setPreviousInListNode(struct ListNode *node,
                           struct ListNode *previous)
{
  if (!node)
    exitOnFatalErrorIllegalArgument("setPreviousInListNode");
  node->previous = previous;
}

//------------------------------------------------------------
struct ListNode *getPreviousFromListNode(struct ListNode *node)
{
  if (!node)
    exitOnFatalErrorIllegalArgument("getPreviousFromListNode");
  return(node->previous);
}

//------------------------------------------------------------
int getNumberFromListNode(struct ListNode *node)
{
  if (!node)
    exitOnFatalErrorIllegalArgument("getNumberFromListNode");
  return(node->number);
}


//------------------------------------------------------------
// The run-loop of the program that accepts the keyboard input

void run()
{
  unsigned char *input_line = malloc(sizeof(unsigned char) * 
                                     (MAX_LINE_LENGTH + 1));
  int chars_read = 0;

  for(;;)
  {
    printf("list control> ");
    if ((chars_read = readLine(input_line,MAX_LINE_LENGTH) == 
         ILLEGAL_ARGUMENTS))
    {
      printf("Error: (internal) readLine: ILLEGAL_ARGUMENTS\n");
      exit(-1);
    }
    dispatch(input_line);
  }
}

//------------------------------------------------------------
// reads one line from the keyboard
int readLine(unsigned char *str,unsigned max_length)
{
  unsigned char character_read = '\0';
  unsigned num_chars = 0;

  if ((max_length <= 1) || (str == NULL))
    return(ILLEGAL_ARGUMENTS);

  while((character_read = getchar()) != '\n')
  {
    *str++ = character_read;
    if (++num_chars >= max_length)
      break;
  }
  *str = '\0';
  return(num_chars);
}

//============================================================
// keyboard event dispatcher functions

//------------------------------------------------------------
void addToDispatcher(unsigned char input,
                     void (*handler)(unsigned char *input_line))
{
  handler_[input] = handler;
}

//------------------------------------------------------------
void removeFromDispatcher(unsigned char input)
{
  handler_[input] = NULL;
}

//------------------------------------------------------------
// dispatches the keyboard input

void dispatch(unsigned char *input_line)
{
  if (handler_[*input_line] != NULL)
    handler_[*input_line](input_line);
  else
    commandNotRecognized(input_line);
}

//------------------------------------------------------------
void callInsertNumber(unsigned char *input_line)
{
  // for demonstration purposes we do everything by hand here
  // and to make things easier we don't interpret negative numbers
  unsigned char *help = input_line;
  int number = 0;

  while(*++help) // look for the start of the number
  {
    if ((*help >= '0') && (*help <= '9'))
      break;
    if (*help != ' ') // nothing else than spaces allowed here
    {
      commandNotRecognized(input_line);
      return;
    }
  }
  while((*help >= '0') && (*help <= '9')) // extract the number
    number = (number * 10) + (*help++ - '0');
      // the remainder of the line is the task description
  insertNumber(number);
}


//------------------------------------------------------------
void callDeleteNumber(unsigned char *input_line)
{
  // for demonstration purposes we do everything by hand here
  unsigned char *help = input_line;
  unsigned index = 0;

  while(*++help) // look for the start of the number
  {
    if ((*help >= '0') && (*help <= '9'))
      break;
    if (*help != ' ') // nothing else than spaces allowed here
    {
      commandNotRecognized(input_line);
      return;
    }
  }
  while((*help >= '0') && (*help <= '9')) // extract the number
    index = (index * 10) + (*help++ - '0');
  // here we should check, whether something unallowed follows,
  // I leave this up to you
  deleteNumber(index);
}


//------------------------------------------------------------
void elementWithGivenIndexNotFound(unsigned index)
{
  printf("Error: element with index %d does not exist\n",index);
}

//------------------------------------------------------------
void commandNotRecognized(unsigned char *command_line)
{
  printf("Error: command not recognized... %s\n",command_line);
}

//------------------------------------------------------------
// helper function if the program runs out of memory
void exitOnFatalErrorOutOfMemory()
{
  printf("Fatal Error: Out of Memory\n");
  exit(-1);
}

//------------------------------------------------------------
// helper function if a function gets an illegal argument
void exitOnFatalErrorIllegalArgument(unsigned char* func_name)
{
  printf("Fatal Error: IllegalArgument in function %s\n",
         func_name);
  exit(-2);
}
