/*
 * Copyright Red Hat
 *
 * 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
 * of the License, 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., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA. 
 *
 * $Id: //eng/uds-releases/jasper/src/uds/indexComponent.h#5 $
 */

#ifndef INDEX_COMPONENT_H
#define INDEX_COMPONENT_H 1

#include "common.h"

#include "bufferedReader.h"
#include "bufferedWriter.h"
#include "compiler.h"
#include "regionIdentifiers.h"

typedef enum completionStatus {
  CS_NOT_COMPLETED,             // operation has not completed
  CS_JUST_COMPLETED,            // operation just completed
  CS_COMPLETED_PREVIOUSLY       // operation completed previously
} CompletionStatus;

typedef struct readPortal {
  struct indexComponent  *component;
  BufferedReader        **readers;
  unsigned int            zones;
} ReadPortal;

/**
 * Prototype for functions which can load an index component from its
 * saved state.
 *
 * @param portal        A component portal which can be used to load the
 *                        specified component.
 * @return UDS_SUCCESS or an error code
 **/
typedef int (*Loader)(ReadPortal *portal);

/**
 * Prototype for functions which can save an index component.
 *
 * @param component     The index component.
 * @param writer        A buffered writer.
 * @param zone          The zone number.
 *
 * @return UDS_SUCCESS or an error code
 **/
typedef int (*Saver)(struct indexComponent *component,
                     BufferedWriter        *writer,
                     unsigned int           zone);

/**
 * Command code used by IncrementalWriter function protocol.
 **/
typedef enum incrementalWriterCommand {
  IWC_START,    //< start an incremental save
  IWC_CONTINUE, //< continue an incremental save
  IWC_FINISH,   //< force finish of incremental save
  IWC_ABORT,    //< abort incremental save
  IWC_IDLE = -1,//< not a command, used internally to signify not in progress
  IWC_DONE = -2 //< not a command, used internally to signify async completion
} IncrementalWriterCommand;

typedef struct writeZone {
  struct indexComponent    *component;
  IncrementalWriterCommand  phase;
  BufferedWriter           *writer;
  unsigned int              zone;
} WriteZone;

/**
 * @param [in]  component       The index component.
 * @param [in]  writer          A buffered writer.
 * @param [in]  zone            The zone number (0 for non-multi-zone).
 * @param [in]  command         The incremental writer command.
 * @param [out] completed       If non-NULL, set to whether save is done.
 *
 * @return      UDS_SUCCESS or an error code
 **/
typedef int (*IncrementalWriter)(struct indexComponent    *component,
                                 BufferedWriter           *writer,
                                 unsigned int              zone,
                                 IncrementalWriterCommand  command,
                                 bool                     *completed);

/**
 * The structure describing how to load or save an index component.
 * At least one of saver or incremental must be specified.
 **/
typedef struct indexComponentInfo {
  RegionKind         kind;        // Region kind
  const char        *name;        // The name of the component (for logging)
  bool               saveOnly;    // Used for saves but not checkpoints
  bool               chapterSync; // Saved by the chapter writer
  bool               multiZone;   // Does this component have multiple zones?
  bool               ioStorage;   // Do we do I/O directly to storage?
  Loader             loader;      // The function load this component
  Saver              saver;       // The function to store this component
  IncrementalWriter  incremental; // The function for incremental writing
} IndexComponentInfo;

/**
 * The structure representing a savable (and loadable) part of an index.
 **/
typedef struct indexComponent {
  const IndexComponentInfo  *info;          // IndexComponentInfo specification
  void                      *componentData; // The object to load or save
  void                      *context;       // The context used to load or save
  struct indexState         *state;         // The index state
  unsigned int               numZones;      // Number of zones in write portal
  WriteZone               **writeZones;     // State for writing component
} IndexComponent;

/**
 * Make an index component
 *
 * @param state         The index state in which this component instance
 *                        shall reside.
 * @param info          The component info specification for this component.
 * @param zoneCount     How many active zones are in use.
 * @param data          Component-specific data.
 * @param context       Component-specific context.
 * @param componentPtr  Where to store the resulting component.
 *
 * @return UDS_SUCCESS or an error code
 **/
int makeIndexComponent(struct indexState         *state,
                       const IndexComponentInfo  *info,
                       unsigned int               zoneCount,
                       void                      *data,
                       void                      *context,
                       IndexComponent           **componentPtr)
  __attribute__((warn_unused_result));

/**
 * Destroy and index component.
 *
 * @param componentPtr  A pointer to the component to be freed.
 **/
void freeIndexComponent(IndexComponent **componentPtr);

/**
 * Return the index component name for this component.
 **/
static INLINE const char *indexComponentName(IndexComponent *component)
{
  return component->info->name;
}

/**
 * Return the index component data for this component.
 **/
static INLINE void *indexComponentData(IndexComponent *component)
{
  return component->componentData;
}

/**
 * Return the index component context for this component.
 **/
static INLINE void *indexComponentContext(IndexComponent *component)
{
  return component->context;
}

/**
 * Determine whether this component may be skipped for a checkpoint.
 *
 * @param component     the component,
 *
 * @return whether the component may be skipped
 **/
static INLINE bool skipIndexComponentOnCheckpoint(IndexComponent *component)
{
  return component->info->saveOnly;
}

/**
 * Determine whether actual saving during a checkpoint should be
 * invoked by the chapter writer thread.
 **/
static INLINE bool
deferIndexComponentCheckpointToChapterWriter(IndexComponent *component)
{
  return component->info->chapterSync;
}

/**
 * Determine whether a replay is required if component is missing.
 *
 * @param component     the component
 *
 * @return whether the component is final (that is, contains shutdown state)
 **/
static INLINE bool
missingIndexComponentRequiresReplay(IndexComponent *component)
{
  return component->info->saveOnly;
}

/**
 * Read a component's state.
 *
 * @param component  The component to read.
 *
 * @return UDS_SUCCESS, an error code from reading, or UDS_INVALID_ARGUMENT
 *         if the component is NULL.
 **/
int readIndexComponent(IndexComponent *component)
  __attribute__((warn_unused_result));

/**
 * Write a state file.
 *
 * @param component  The component to write
 *
 * @return UDS_SUCCESS, an error code from writing, or UDS_INVALID_ARGUMENT
 *         if the component is NULL.
 **/
int writeIndexComponent(IndexComponent *component)
  __attribute__((warn_unused_result));

/**
 * Start an incremental save for this component (all zones).
 *
 * @param [in] component        The index component.
 *
 * @return      UDS_SUCCESS or an error code.
 **/
int startIndexComponentIncrementalSave(IndexComponent *component)
  __attribute__((warn_unused_result));

/**
 * Perform an incremental save for a component in a particular zone.
 *
 * @param [in]  component       The index component.
 * @param [in]  zone            The zone number.
 * @param [out] completed       Pointer to hold completion status result.
 *
 * @return      UDS_SUCCESS or an error code.
 *
 * @note        If an incremental save is not supported, a regular
 *              save will be performed if this is the first call in zone 0.
 **/
 int performIndexComponentZoneSave(IndexComponent   *component,
                                   unsigned int      zone,
                                   CompletionStatus *completed)
  __attribute__((warn_unused_result));

/**
 * Perform an incremental save for a non-multizone component synchronized
 * with the chapter writer.
 *
 * @param component     The index component.
 **/
int performIndexComponentChapterWriterSave(IndexComponent *component)
  __attribute__((warn_unused_result));

/**
 * Force the completion of an incremental save currently in progress in
 * a particular zone.
 *
 * @param [in]  component       The index component.
 * @param [in]  zone            The zone number.
 * @param [out] completed       Pointer to hold completion status result.
 *
 * @return      UDS_SUCCESS or an error code.
 **/
int finishIndexComponentZoneSave(IndexComponent   *component,
                                 unsigned int      zone,
                                 CompletionStatus *completed)
  __attribute__((warn_unused_result));

/**
 * Force the completion of an incremental save in all zones and complete
 * the overal save.
 *
 * @param [in]  component       The index component.
 *
 * @return      UDS_SUCCESS or an error code.
 *
 * @note        If all zones call finishIndexComponentZoneSave first, only
 *              the common non-index-related completion code is required,
 *              which protects access to the index data structures from the
 *              invoking thread.
 **/
int finishIndexComponentIncrementalSave(IndexComponent *component)
  __attribute__((warn_unused_result));

/**
 * Abort the incremental save currently in progress in a particular zone.
 *
 * @param [in]  component       The index component.
 * @param [in]  zone            The zone number.
 * @param [out] completed       Pointer to hold completion status result.
 *
 * @return      UDS_SUCCESS or an error code.
 *
 * @note        "Completed" in this case means completed or aborted.
 *              Once any zone calls this function the entire save is
 *              useless unless every zone indicates CS_COMPLETED_PREVIOUSLY.
 **/
int abortIndexComponentZoneSave(IndexComponent   *component,
                                unsigned int      zone,
                                CompletionStatus *completed)
  __attribute__((warn_unused_result));

/**
 * Abort an incremental save currently in progress
 *
 * @param [in] component        The index component.
 *
 * @return      UDS_SUCCESS or an error code.
 *
 * @note        If all zones call abortIndexComponentZoneSave first, only
 *              the common non-index-related completion code is required,
 *              which protects access to the index data structures from the
 *              invoking thread.
 **/
int abortIndexComponentIncrementalSave(IndexComponent *component)
  __attribute__((warn_unused_result));

/**
 * Remove or invalidate component state.
 *
 * @param component  The component whose file is to be removed.  If NULL
 *                   no action is taken.
 **/
__attribute__((warn_unused_result))
int discardIndexComponent(IndexComponent *component);

/**
 * Get a buffered reader for the specified component part.
 *
 * @param [in]  portal          The component portal.
 * @param [in]  part            The component ordinal number.
 * @param [out] readerPtr       Where to put the buffered reader.
 *
 * @return UDS_SUCCESS or an error code.
 *
 * @note the reader is managed by the component portal
 **/
__attribute__((warn_unused_result))
int getBufferedReaderForPortal(ReadPortal      *portal,
                               unsigned int     part,
                               BufferedReader **readerPtr);

#endif /* INDEX_COMPONENT_H */
