/*
 * Copyright (C) 2000-2022 the xine project
 *
 * This file is part of xine, a free video player.
 *
 * xine 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.
 *
 * xine 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, USA
 */

#ifndef HAVE_INPUT_PLUGIN_H
#define HAVE_INPUT_PLUGIN_H

#include <sys/types.h>  /* off_t */

#include <xine/attributes.h>
#include <xine/os_types.h>
#include <xine/xineutils.h>
#include <xine/buffer.h>

struct plugin_node_s;

#define INPUT_PLUGIN_IFACE_VERSION   18

typedef struct input_class_s input_class_t ;
typedef struct input_plugin_s input_plugin_t;

struct input_class_s {

  /*
   * create a new instance of this plugin class
   * return NULL if the plugin does'nt handle the given mrl
   */
  input_plugin_t* (*get_instance) (input_class_t *this_gen, xine_stream_t *stream, const char *mrl);

  /**
   * @brief short human readable identifier for this plugin class
   */
  const char *identifier;

  /**
   * @brief human readable (verbose = 1 line) description for this plugin class
   *
   * The description is passed to gettext() to internationalise.
   */
  const char *description;

  /**
   * @brief Optional non-standard catalog to use with dgettext() for description.
   */
  const char *text_domain;

  /*
   * ls function, optional: may be NULL
   * return value: NULL => filename is a file, **char=> filename is a dir
   */
  xine_mrl_t ** (*get_dir) (input_class_t *this_gen, const char *filename, int *nFiles);

  /*
   * generate autoplay list, optional: may be NULL
   * return value: list of MRLs
   */
  const char * const * (*get_autoplay_list) (input_class_t *this_gen, int *num_files);

  /*
   * close down, free all resources
   */
  void (*dispose) (input_class_t *this_gen);

  /*
   * eject/load the media (if possible), optional: may be NULL
   *
   * returns 0 for temporary failures
   */
  int (*eject_media) (input_class_t *this_gen);
};

#define default_input_class_dispose (void (*) (input_class_t *this_gen))free

struct input_plugin_s {

  /*
   * open the stream
   * return 0 if an error occured
   */
  int (*open) (input_plugin_t *this_gen);

  /*
   * return capabilities of the current playable entity. See
   * get_current_pos below for a description of a "playable entity"
   * Capabilities a created by "OR"ing a mask of constants listed
   * below which start "INPUT_CAP".
   *
   * depending on the values set, some of the functions below
   * will or will not get called or should (not) be able to
   * do certain tasks.
   *
   * for example if INPUT_CAP_SEEKABLE is set,
   * the seek() function is expected to work fully at any time.
   * however, if the flag is not set, the seek() function should
   * make a best-effort attempt to seek, e.g. at least
   * relative forward seeking should work.
   */
  uint32_t (*get_capabilities) (input_plugin_t *this_gen);

  /*
   * read nlen bytes, return number of bytes read
   * Should block until some bytes available for read;
   * a return value of 0 indicates no data available
   */
  off_t (*read) (input_plugin_t *this_gen, void *buf, off_t nlen) XINE_USED;


  /*
   * read one block, return newly allocated block (or NULL on failure)
   * for blocked input sources len must be == blocksize
   * the fifo parameter is only used to get access to the buffer_pool_alloc function
   */
  buf_element_t *(*read_block)(input_plugin_t *this_gen, fifo_buffer_t *fifo, off_t len);


  /*
   * seek position, return new position
   *
   * if seeking failed, -1 is returned
   */
  off_t (*seek) (input_plugin_t *this_gen, off_t offset, int origin);


  /*
   * seek to time position, return new position
   * time_offset is given in miliseconds
   *
   * if seeking failed, -1 is returned
   *
   * note: only SEEK_SET (0) is currently supported as origin,
   *       unless INPUT_CAP_TIME_SEEKABLE is set.
   * note: may be NULL is not supported
   */
  off_t (*seek_time) (input_plugin_t *this_gen, int time_offset, int origin);


  /*
   * get current position in stream.
   *
   */
  off_t (*get_current_pos) (input_plugin_t *this_gen);


  /*
   * get current time position in stream in miliseconds.
   *
   * note: may be NULL is not supported
   */
  int (*get_current_time) (input_plugin_t *this_gen);


  /*
   * return number of bytes in the next playable entity or -1 if the
   * input is unlimited, as would be the case in a network stream.
   *
   * A "playable entity" tends to be the entities listed in a playback
   * list or the units on which playback control generally works on.
   * It might be the number of bytes in a VCD "segment" or "track" (if
   * the track has no "entry" subdivisions), or the number of bytes in
   * a PS (Program Segment or "Chapter") of a DVD. If there are no
   * subdivisions of the input medium and it is considered one
   * indivisible entity, it would be the byte count of that entity;
   * for example, the length in bytes of an MPEG file.

   * This length information is used, for example when in setting the
   * absolute or relative play position or possibly calculating the
   * bit rate.
   */
  off_t (*get_length) (input_plugin_t *this_gen);


  /*
   * return block size in bytes of next complete playable entity (if
   * supported, 0 otherwise). See the description above under
   * get_length for a description of a "complete playable entity".
   *
   * this block size is only used for mpeg streams stored on
   * a block oriented storage media, e.g. DVDs and VCDs, to speed
   * up the demuxing process. only set this (and the INPUT_CAP_BLOCK
   * flag) if this is the case for your input plugin.
   *
   * make this function simply return 0 if unsure.
   */

  uint32_t (*get_blocksize) (input_plugin_t *this_gen);


  /*
   * return current MRL
   */
  const char * (*get_mrl) (input_plugin_t *this_gen);


  /*
   * request optional data from input plugin.
   */
  int (*get_optional_data) (input_plugin_t *this_gen, void *data, int data_type);


  /*
   * close stream, free instance resources
   */
  void (*dispose) (input_plugin_t *this_gen);

  /*
   * "backward" link to input plugin class struct
   */

  input_class_t *input_class;

  /**
   * @brief Pointer to the loaded plugin node.
   *
   * Used by the plugins loader. It's an opaque type when using the
   * structure outside of xine's build.
   */
  struct plugin_node_s *node XINE_PRIVATE_FIELD;
};

/*
 * possible capabilites an input plugin can have:
 */
#define INPUT_CAP_NOCAP                0x00000000

/*
 * INPUT_CAP_SEEKABLE:
 *   seek () works reliably.
 *   even for plugins that do not have this flag set
 *   it is a good idea to implement the seek() function
 *   in a "best effort" style anyway, so at least
 *   throw away data for network streams when seeking forward
 */

#define INPUT_CAP_SEEKABLE             0x00000001

/*
 * INPUT_CAP_BLOCK:
 *   means more or less that a block device sits behind
 *   this input plugin. get_blocksize must be implemented.
 *   will be used for fast and efficient demuxing of
 *   mpeg streams (demux_mpeg_block).
 */

#define INPUT_CAP_BLOCK                0x00000002

/*
 * INPUT_CAP_AUDIOLANG:
 * INPUT_CAP_SPULANG:
 *   input plugin knows something about audio/spu languages,
 *   e.g. knows that audio stream #0 is english,
 *   audio stream #1 is german, ...
 *   *((int *)data) will provide the requested channel number
 *   and awaits the language back in (char *)data
 */

#define INPUT_CAP_AUDIOLANG            0x00000008
#define INPUT_CAP_SPULANG              0x00000010

/*
 * INPUT_CAP_PREVIEW:
 *   get_optional_data can handle INPUT_OPTIONAL_DATA_PREVIEW
 *   so a non-seekable stream plugin can povide the first
 *   few bytes for demuxers to look at them and decide wheter
 *   they can handle the stream or not. the preview data must
 *   be buffered and delivered again through subsequent
 *   read() calls.
 *   caller must provide a buffer allocated with at least
 *   MAX_PREVIEW_SIZE bytes.
 */

#define INPUT_CAP_PREVIEW              0x00000040

/*
 * INPUT_CAP_CHAPTERS:
 *   The media streams provided by this plugin have an internal
 *   structure dividing it into segments usable for navigation.
 *   For those plugins, the behaviour of the skip button in UIs
 *   should be changed from "next MRL" to "next chapter" by
 *   sending XINE_EVENT_INPUT_NEXT.
 */

#define INPUT_CAP_CHAPTERS             0x00000080

/*
 * INPUT_CAP_RIP_FORBIDDEN:
 *   means that rip/disk saving must not be used.
 *   (probably at author's request)
 */

#define INPUT_CAP_RIP_FORBIDDEN        0x00000100

/*
 * INPUT_CAP_NO_CACHE:
 *   do not use input cache plugin
 */
#define INPUT_CAP_NO_CACHE             0x00000200

/*
 * INPUT_CAP_CLONE:
 *   open a second input plugin instance on the same source.
 */
#define INPUT_CAP_CLONE                0x00000400

/*
 * INPUT_CAP_SLOW_SEEKABLE:
 *   Like INPUT_CAP_SEEKABLE, but seeking may be expensive.
 *   Short forward seeks are fast (ex. implemented by skipping data).
 *   Seeking back or far forward is expensive.
 *
 *   This type of seeking should be used only when user issues seek request
 *   and the destination offset is known (no binary search etc. required).
 *   Another use case could be when header is located at the end of file.
 *
 */
#define INPUT_CAP_SLOW_SEEKABLE        0x00000800

#define INPUT_IS_SEEKABLE(input) (((input)->get_capabilities(input) & INPUT_CAP_SEEKABLE) != 0)
#define INPUT_IS_SLOW_SEEKABLE(input) (((input)->get_capabilities(input) & (INPUT_CAP_SEEKABLE | INPUT_CAP_SLOW_SEEKABLE)) != 0)

/*
 * INPUT_CAP_SIZED_PREVIEW:
 *   Like INPUT_CAP_PREVIEW, but the buffer shall contain an int telling
 *   the count of bytes to read before the call.
 */
#define INPUT_CAP_SIZED_PREVIEW        0x00001000

/*
 * INPUT_CAP_TIME_SEEKABLE:
 *   Time based seek works reliably and should be preferred over size based seek.
 */
#define INPUT_CAP_TIME_SEEKABLE        0x00002000

/*
 * INPUT_CAP_NEW_MRL:
 *   Plugin can try to switch to a new mrl on the fly.
 *   See INPUT_OPTIONAL_DATA_NEW_MRL below.
 */
#define INPUT_CAP_NEW_MRL              0x00004000

/*
 * INPUT_CAP_LIVE:
 *   Plugin knows it has a real live stream. It runs at a fixed speed that is
 *   _roughly_ the same as XINE_FINE_SPEED_NORMAL.
 */
#define INPUT_CAP_LIVE                  0x00008000

#define INPUT_OPTIONAL_UNSUPPORTED    0
#define INPUT_OPTIONAL_SUCCESS        1

#define INPUT_OPTIONAL_DATA_AUDIOLANG 2
#define INPUT_OPTIONAL_DATA_SPULANG   3
#define INPUT_OPTIONAL_DATA_PREVIEW   7

/* buffer is a const char **; the string is freed by the input plugin. */
#define INPUT_OPTIONAL_DATA_MIME_TYPE 8
/* buffer is unused; true if the demuxer should be determined by the MIME type */
#define INPUT_OPTIONAL_DATA_DEMUX_MIME_TYPE 9
/* buffer is a const char **; the string is static or freed by the input plugin. */
#define INPUT_OPTIONAL_DATA_DEMUXER   10
/* buffer is a struct input_plugin_s **; release by calling ptr->dispose (ptr). */
#define INPUT_OPTIONAL_DATA_CLONE     11
/* see INPUT_CAP_SIZED_PREVIEW above. */
#define INPUT_OPTIONAL_DATA_SIZED_PREVIEW 12
/* buffer is an int32_t * where input plugin will store media duration in milliseconds. */
#define INPUT_OPTIONAL_DATA_DURATION  13
/* buffer is a const char * holding the new mrl to try.
 * fragment streams can avoid input_plugin->dispose () followed by input_class->get_instance (),
 * or _x_free_input_plugin () followed by _x_find_input_plugin () on a lot of * similar mrls.
 * an empty mrl (buffer[0] == 0) means close connection now, and wait for a real new mrl later. */
#define INPUT_OPTIONAL_DATA_NEW_MRL   14
/* buffer is a xine_mfrag_list_t ** where input plugin will store a pointer to its media fragment
 * list, if any (see <xine/mfrag.h>). demux may update it if fragment index is part of the
 * container, like with some .mp4. */
#define INPUT_OPTIONAL_DATA_FRAGLIST  15
/* after a successful open () of a main input, this may supply possible side stream inputs.
 * on call, buffer is an int * telling the side stream index 1..3.
 * on return, buffer is a struct input_plugin_s ** where a pointer to a ready side input
 * bound to xine_get_side_stream (main_stream, stream_index) will be stored if available.
 * NOTE: this shall be an alternative get_instance (), not the result of an _x_find_input_plugin ().
 * release by _x_free_input_plugin () or xine_dispose () of the side stream. */
#define INPUT_OPTIONAL_DATA_SIDE      16
/* returns a pts value (1/90000s) to add to all demux media pts, or INPUT_OPTIONAL_UNSUPPORTED == 0.
 * some fragment streams need this to provide a/v sync over multiple side streams. */
#define INPUT_OPTIONAL_DATA_PTSOFFS   17
/* data is an int * telling how many seconds to go back into a live stream.
 * yes this _does_ work sometimes, up to 24h ;-) */
#define INPUT_OPTIONAL_DATA_REWIND    18
/* ask plugin to generate a new preview from the current position,
 * eg to skip a large ID3v2 tag. data is ignored. */
#define INPUT_OPTIONAL_DATA_NEW_PREVIEW 19

#define MAX_MRL_ENTRIES 255
#define MAX_PREVIEW_SIZE 4096

/* network buffering control */
typedef struct xine_nbc_st xine_nbc_t;
xine_nbc_t *xine_nbc_init (xine_stream_t *stream) XINE_PROTECTED;
/* returns a combinwd demux pts position, starting with 0 at stream start. */
int64_t xine_nbc_get_pos_pts (xine_nbc_t *nbc) XINE_PROTECTED;
void xine_nbc_close (xine_nbc_t *nbc) XINE_PROTECTED;


/* Types of mrls returned by get_dir() */
#define mrl_unknown        (0 << 0)
#define mrl_dvd            (1 << 0)
#define mrl_vcd            (1 << 1)
#define mrl_net            (1 << 2)
#define mrl_rtp            (1 << 3)
#define mrl_stdin          (1 << 4)
#define mrl_cda            (1 << 5)
#define mrl_file           (1 << 6)
#define mrl_file_fifo      (1 << 7)
#define mrl_file_chardev   (1 << 8)
#define mrl_file_directory (1 << 9)
#define mrl_file_blockdev  (1 << 10)
#define mrl_file_normal    (1 << 11)
#define mrl_file_symlink   (1 << 12)
#define mrl_file_sock      (1 << 13)
#define mrl_file_exec      (1 << 14)
#define mrl_file_backup    (1 << 15)
#define mrl_file_hidden    (1 << 16)

/*
 * Freeing/zeroing all of entries of given mrl.
 */
#define MRL_ZERO(m) {							\
    if((m)) {								\
    free((m)->origin);							\
    free((m)->mrl);							\
    free((m)->link);							\
    (m)->origin = NULL;							\
    (m)->mrl    = NULL;							\
    (m)->link   = NULL;							\
    (m)->type   = 0;							\
    (m)->size   = (off_t) 0;						\
    }									\
  }

/*
 * Duplicate two mrls entries (s = source, d = destination).
 */
#define MRL_DUPLICATE(s, d) {						\
    _x_assert((s) != NULL);						\
    _x_assert((d) != NULL);						\
									\
    free((d)->origin);							\
    (d)->origin = (s)->origin ? strdup((s)->origin) : NULL;		\
									\
    free((d)->mrl);							\
    (d)->mrl = (s)->mrl ? strdup((s)->mrl) : NULL;			\
									\
    free((d)->link);							\
    (d)->link = (s)->link ? strdup((s)->link) : NULL;			\
									\
    (d)->type = (s)->type;						\
    (d)->size = (s)->size;						\
  }

/*
 * Duplicate two arrays of mrls (s = source, d = destination).
 */
#define MRLS_DUPLICATE(s, d) {                                                \
  int i = 0;                                                                  \
                                                                              \
  _x_assert((s) != NULL);                                                     \
  _x_assert((d) != NULL);                                                     \
                                                                              \
  while((s) != NULL) {                                                        \
    d[i] = (xine_mrl_t *) malloc(sizeof(xine_mrl_t));                         \
    MRL_DUPLICATE(s[i], d[i]);                                                \
    i++;                                                                      \
  }                                                                           \
}


#endif
