/*
 * Copyright ©2026 Soham Pardeshi. All rights reserved.
 * Permission is hereby granted to students registered for University of
 * Washington CSE 333 for use solely during Summer Quarter 2026 for
 * purposes of the course. No other use, copying, distribution, or
 * modification is permitted without prior written consent. Copyrights
 * for third-party components of this work must be honored. Instructors
 * interested in reusing these course materials should contact the author.
 */

#ifndef HW2_MEMINDEX_H_
#define HW2_MEMINDEX_H_

#include <stdbool.h>

#include "libhw1/HashTable.h"
#include "libhw1/LinkedList.h"
#include "./DocTable.h"

// A word's byte offset from the start of a document.
typedef uint32_t DocPositionOffset_t;

// A MemIndex is an in-memory inverted index.
//
// An inverted index is a unidirectional mapping of word to a logical set
// of (document, document offset) tuples, indicating all the occurrences
// of that word within the document corpus.
//
// Although C is a procedural language, HW1 demonstrated that we could
// implement some aspects of object-oriented programming; namely, data
// encapsulation (ie, a struct), access restrictions (ie, hiding the struct
// definition in the .c), and information hiding (ie, hiding functionality
// in the .c).  MemIndex is a primitive example of how to do another OOP
// feature: inheritance.  Specifically, it is a simple example of
// implementation inheritance (ie, we reuse methods but add no new fields).
// More features, such as dynamic dispatch (also known as "late binding")
// and adding new fields would require the creation of a bookkeeping struct
// that contained function pointers (to enable dynamic dispatch) and field
// offsets (to enable adding fields) -- essentially, a hand-implemented
// vtable.  See also DocTable for an example of class composition.
typedef HashTable MemIndex;

// Allocate and return a new MemIndex.  The caller takes responsibility for
// eventually calling MemIndex_Free to free memory associated with the table.
//
// Arguments: none.
//
// Returns:
// - the newly-allocated table (never NULL).
MemIndex* MemIndex_Allocate(void);

// Frees a MemIndex that was previously allocated by MemIndex_Allocate,
// including all the structures stored within it.
//
// Arguments:
// - index: a previously-allocated MemIndex.
void MemIndex_Free(MemIndex* index);

// Returns the number of words contained within the index.
//
// Arguments:
// - index: the MemIndex
//
// Returns:
// - the number of unique words in the index.
int MemIndex_NumWords(MemIndex* index);

// Adds a "posting list" to the MemIndex.
//
// A "posting list" is the list of positions within a specific document where
// a particular word appears.  The postings (ie, positions) are specified as
// byte offsets from the beginning of the document (ie, DocPositionOffset_t),
// and the passed-in list must be in ascending order.  The passed-in word must
// not have been previously added to this document.
//
// Arguments:
// - index: the MemIndex to add these postings to
// - word: the word that these postings refer to.  MemIndex takes ownership
//   of this argument.
// - docid: the document containing these postings
// - postings: a non-empty list of byte offsets, in ascending order.
//   MemIndex takes ownership of this list.
void MemIndex_AddPostingList(MemIndex* index, char* word, DocID_t doc_id,
                             LinkedList* postings);

// A document that matches a search query.
typedef struct {
  DocID_t doc_id;  // a document that matches a search query
  int rank;        // an indicator of the quality of the match
} SearchResult;


// Processes a query against the MemIndex, returning a list SearchResults.
//
// The results are a list of SearchResult's, containing all the documents
// containing every query word at least once.  The results will be returned
// in decreasing quality (i.e. sorted by the rank field in decreasing order).
// The client is responsible for freeing the returned list of SearchResults;
// the appropriate deallocator to pass into LinkedList_Free is stdlib's free().
//
// Arguments:
// - index: the MemIndex to query
// - query: a non-empty array of strings; each string is a single
//          null-terminated query word, all lower-case.
// - query_len: the number of words in the query array.
//
// Returns:
// - NULL: if no matching documents were found
// - a non-NULL LinkedList of SearchResult's
LinkedList* MemIndex_Search(MemIndex* index, char* query[], int query_len);


//////////////////////////////////////////////////////////////////////////////

// The struct stored within our hash table as the HTValue_t.
//
// For HW3, clients will need to break the MemIndex abstraction by directly
// accessing the contents of the MemIndex HashTable.  This struct is how
// we implement the "logical set of (document, document offset) tuples"
// which is discussed at the top of the file; the 'word' field is the logical
// key to the inverted index and the 'postings' is its associated value,
// represented as a mapping from DocID_t -> LinkedList(DocPositionOffset_t).
//
// The WordPostings struct owns the memory allocated to both the word and
// postings fields.
typedef struct {
  char*        word;
  HashTable*   postings;
} WordPostings;

#endif  // HW2_MEMINDEX_H_