/**
@page page-tutorial Tutorial

@tableofcontents

@section common-usage Common usage

This page contains a description of basic library usage for the most
common usage cases.

Additionally, the @ref page-examples page contains full examples for
common use cases, which are also included in the 'examples' directory
of the source code distribution.

@subsection reading Reading miniSEED data

A common task is to read a file of miniSEED records and perform some
action based on the header values or apply some process to the data
samples.  There are two patterns to support this in the library, one
to iterate over the file record-by-record and another to read all data
into a collection of continuous series.

@subsubsection reading-records Reading miniSEED records from files

The ms3_readmsr() routine will open a specified file and return
MS3Record structures for each miniSEED record it reads from the file.
Called iteratively in a loop, all records are returned in the order
discovered.

A code snippet for reading miniSEED from a file record-by-record:
@anchor msr3-readmsr-snippet
@code
int main ()
{
  char *filename = "datafile.mseed";
  MS3Record *msr = NULL;
  uint32_t flags = MSF_SKIPNOTDATA & MSF_UNPACKDATA;
  int verbose    = 0;
  int retcode;

  while ((retcode = ms3_readmsr (&msr, filename, flags, verbose)) == MS_NOERROR)
  {
    /* Do something with the record here, e.g. print */
    msr3_print (msr, verbose);
  }

  if (retcode != MS_ENDOFFILE)
    ms_log (2, "Cannot read %s: %s\n", filename, ms_errorstr (retcode));

  /* Cleanup memory and close file */
  ms3_readmsr (&msr, NULL, flags, verbose);

  return 0;
}
@endcode

It is important to call ms3_readmsr() for cleanup as illustrated to
free memory and close files.

There are more advanced versions of this routine.  The ms3_readmsr_r()
function is a thread-safe version that can be used in threaded
programs to read files in parallel.  The ms3_readmsr_selection()
function is a thread-safe version that can additionally accept @ref
data-selections to efficiently return only selected data.

@subsubsection reading-tracelists Reading miniSEED from files into traces lists

Another option is to read all miniSEED into a @ref trace-list without
any handling of records.  This is done with ms3_readtraces(), which is
very useful for quickly reading data for processing.  The data are
returned in ordered, continuous segments ready for processing.

A code snippet for reading miniSEED from a file into a @ref trace-list :
@anchor msr3-readtracelist-snippet
@code
int main ()
{
  char *filename1    = "datafile1.mseed";
  char *filename2    = "datafile2.mseed";
  MS3TraceList *mstl = NULL;
  MS3Tolerance tol   = MS3Tolerance_INITIALIZER;
  uint32_t flags     = MSF_SKIPNOTDATA & MSF_UNPACKDATA;
  int verbose        = 0;
  int retcode;

  retcode = ms3_readtracelist (&mstl, filename1, &tol, 0, flags, verbose);

  if (retcode != MS_NOERROR)
    ms_log (2, "Cannot read %s: %s\n", filename1, ms_errorstr (retcode));

  retcode = ms3_readtracelist (&mstl, filename2, &tol, 0, flags, verbose);

  if (retcode != MS_NOERROR)
    ms_log (2, "Cannot read %s: %s\n", filename2, ms_errorstr (retcode));

  if (!mstl)
  {
    fprintf (stderr, "Error reading file\\n");
    return -1;
  }

  /* Do something with the traces here, e.g. print */
  mstl3_printtracelist (mstl, 0, verbose, 0, 0);

  mstl3_free (&mstl, 1);

  return 0;
}
@endcode

There are more advanced versions of this routine.  The
ms3_readtracelist_timewin() function allows data to be limited to a
specific time range while reading.  The ms3_readtracelist_selection()
can additionally accept @ref data-selections to efficiently return
only selected data.

@subsubsection reading-buffers Reading miniSEED from memory

If an application is not designed to read miniSEED from files the
library provides functions functionality to read data from memory.
The mstl3_readbuffer() function will read all data from a buffer into
a @ref trace-list.  The low-level functions ms3_detect() and
msr3_parse() can be used to detect and parse miniSEED records in
memory buffers.

@subsection writing Writing miniSEED records

Another common task is to create miniSEED records.  There are two
patterns to support this in the library: one is to write data from
library containers to files and another to create records and provide
them back to the caller in a memory buffer via a callback function.

@subsubsection callback Callback design

If not writing directly to files, the packing interfaces use a
callback design, where the caller specifies a function that will be
called for every record that is created.  It is the responsibility of
the callback function to handle the record before returning.  This
design allows a program to do whatever is needed with generated
miniSEED, i.e. write to files, send over the network, or even
injection of more information.

@subsubsection writing-files Writing miniSEED data to files

The library supports writing miniSEED directly to files from either
MS3Record or MS3TraceList containers using msr3_writemseed() or
mstl3_writemseed() respectively.

A code snippet for writing miniSEED to a file from an MS3Record using msr3_writemseed():
@anchor msr3-writemseed-snippet
@code
int main()
{
  int packedrecords;
  int verbose = 0;
  char *msfile = "output.mseed";
  int32_t datasamples[] = {0,1,2,3,4,5,6,7,8,9,10};
  MS3Record *msr;

  msr = msr3_init (NULL);

  /* Populate MS3Record values */
  strcpy (msr->sid, "XX_TEXT__B_H_E");
  msr->formatversion = 3;
  msr->reclen = 512;
  msr->encoding = DE_STEIM2;
  msr->starttime = ms_timestr2nstime ("2018-12-01T00:00:00.000000000");
  msr->samprate = 20.0;

  msr->datasamples = datasamples;  /* pointer to 32-bit integer data samples */
  msr->numsamples = 11;
  msr->sampletype = 'i';  /* declare data type to be 32-bit integers */

  /* Write all data in MS3Record to output file, using MSF_FLUSHDATA flag */
  packedrecords = msr3_writemseed (msr, msfile, 1, MSF_FLUSHDATA, verbose);

  ms_log (0, "Wrote %d records to %s\n", packedrecords, msfile);

  /* Disconnect datasamples pointer, otherwise msr3_free() will attempt to free() it */
  msr->datasamples = NULL;

  msr3_free (&msr);
}
@endcode

@subsubsection creating-records Creating miniSEED records

Records can be generated from data in either MS3Record or MS3TraceList
containers using, respectively, msr3_pack() or and mstl3_pack().

*/
