/* ********* LineStorage module---implementation ********* */


#include <stdio.h>
#include <stdlib.h>
#include "kwic.h"
#include "LineStorage.h"

/* **** local constants **** */

/* **** local types **** */

/* Each line is a list of WordNodes. */
typedef struct WordNode {
char* word;
struct WordNode* nextWordPtr;
} WordNode;
typedef WordNode* WordNodePtr;

/* The LineStorage module stores a list of LineNodes */
typedef struct LineNode {
struct LineNode* nextLinePtr;
WordNodePtr headWordPtr;
WordNodePtr tailWordPtr;
int wordCount;
} LineNode;
typedef LineNode* LineNodePtr;

/* **** local variables **** */

static LineNodePtr headLinePtr, tailLinePtr;
static int lineCount;

/***** state invariant *****

1. if lineCount == 0 then
headlinePtr == NULL
taillinePtr == NULL
else
headLinePtr points to a null-terminated linked list of LineNodes.
tailLinePtr points to the last LineNode in this list.
There are lineCount LineNodes in this list.

2. for every LineNode allocated by LineStorage
if wordCount == 0 then
headWordPtr == NULL
tailWordPtr == NULL
else
headWordPtr points to a null-terminated linked list of WordNodes.
tailWordPtr points to the last WordNode in this list.
There are wordCount WordNodes in this list.

3. For every WordNode allocated word is a null-terminated array of characters.

4. All of the dynamic memory allocated by LineStorage (and not yet freed)
is in the list structure headed by headLinePtr.
*/

/* **** local functions **** */

/*
* if headLinePtr contains at least i+1 LineNodes then
* return the address of the ith LineNode
* else
* return NULL
* Assumed: the state invariant holds
*/
static LineNodePtr getLine(int i)
{
LineNodePtr tmpLinePtr;

if (i < 0)
return NULL;
for (tmpLinePtr = headLinePtr;
i-- > 0 && tmpLinePtr != NULL;
tmpLinePtr = tmpLinePtr->nextLinePtr)
;
return tmpLinePtr;
}

/*
* if wordNodePtr != NULL && the list headed by wordNodePtr has >= i words then
* return a pointer to the ith word in the list headed by wordNodePtr
* else
* return NULL
* Assumed: wordNodePtr is either NULL or a pointer to a list of WordNodes
*/
static WordNodePtr getWord(WordNodePtr wordNodePtr,int i)
{
if (i < 0)
return NULL;
while (i-- > 0 && wordNodePtr != NULL) {
wordNodePtr = wordNodePtr->nextWordPtr;
}
return wordNodePtr;
}

/* **** exported functions **** */

void LSInit(void)
{
headLinePtr = NULL;
tailLinePtr = NULL;
lineCount = 0;
}

void LSReset(void)
{
LineNodePtr tmpLinePtr;
WordNodePtr tmpWordPtr0,tmpWordPtr1;

while (headLinePtr != NULL) {
tmpWordPtr0 = headLinePtr->headWordPtr;
while (tmpWordPtr0 != NULL) {
tmpWordPtr1 = tmpWordPtr0->nextWordPtr;
free(tmpWordPtr0->word);
free(tmpWordPtr0);
tmpWordPtr0 = tmpWordPtr1;
}
tmpLinePtr = headLinePtr;
headLinePtr = headLinePtr->nextLinePtr;
free(tmpLinePtr);
}
lineCount = 0;
tailLinePtr = NULL;
}

KWStatus LSAddLine(void)
{
LineNodePtr newLinePtr;

/* create and fill a LineNode */
newLinePtr = malloc(sizeof(LineNode));
if (newLinePtr == NULL)
return KWMEMORYERROR;
lineCount++;
newLinePtr->nextLinePtr = NULL;
newLinePtr->headWordPtr = NULL;
newLinePtr->tailWordPtr = NULL;
newLinePtr->wordCount = 0;

/* link in the new LineNode */
if (tailLinePtr == NULL) {
headLinePtr = newLinePtr;
} else {
tailLinePtr->nextLinePtr = newLinePtr;
}
tailLinePtr = newLinePtr;
return KWSUCCESS;
}

KWStatus LSAddWord(char* word)
{
WordNodePtr newWordPtr;

if (tailLinePtr == NULL)
return KWRANGEERROR;

/* create and fill a WordNode */
newWordPtr = malloc(sizeof(WordNode));
if (newWordPtr == NULL)
return KWMEMORYERROR;
newWordPtr->word = malloc(strlen(word)+1);
if (newWordPtr->word == NULL) {
free(newWordPtr);
return KWMEMORYERROR;
}
tailLinePtr->wordCount++;
strcpy(newWordPtr->word,word);
newWordPtr->nextWordPtr = NULL;

/* link in the new WordNode */
if (tailLinePtr->tailWordPtr == NULL) { /* empty line */
tailLinePtr->headWordPtr = newWordPtr;
} else {
tailLinePtr->tailWordPtr->nextWordPtr = newWordPtr;
}
tailLinePtr->tailWordPtr = newWordPtr;
return KWSUCCESS;
}

const char* LSGetWord(int lineNum,int wordNum)
{
LineNodePtr tmpLinePtr;
WordNodePtr tmpWordPtr;

if (lineNum >= lineCount)
return NULL;

/* find line LineNum */
tmpLinePtr = getLine(lineNum);
if (tmpLinePtr == NULL)
return NULL;

/* find word wordNum */
tmpWordPtr = getWord(tmpLinePtr->headWordPtr,wordNum);
if (tmpWordPtr == NULL)
return NULL;
return tmpWordPtr->word;
}

int LSNumWords(int lineNum)
{
LineNodePtr tmpLinePtr;

/* find line lineNum */
tmpLinePtr = getLine(lineNum);
if (tmpLinePtr == NULL) {
return KWRANGEERROR;
}
/* count the words in line lineNum */
return tmpLinePtr->wordCount;
}

int LSNumLines(void)
{
return lineCount;
}

void LSPrintState(void)
{
LineNodePtr tmpLinePtr;
WordNodePtr tmpWordPtr;

printf("lineCount:%d\n",lineCount);
for (tmpLinePtr = headLinePtr;
tmpLinePtr != NULL;
tmpLinePtr = tmpLinePtr->nextLinePtr) {
printf("\twordCount:%d\n\t",tmpLinePtr->wordCount);
for (tmpWordPtr = tmpLinePtr->headWordPtr;
tmpWordPtr != NULL;
tmpWordPtr = tmpWordPtr->nextWordPtr) {
printf("!%s",tmpWordPtr->word);
}
printf("!\n");
}
}