/*
 *   connector.h
 *   Copyright (C) 2019 David García Goñi <dagargo@gmail.com>
 *
 *   This file is part of Elektroid.
 *
 *   Elektroid 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 3 of the License, or
 *   (at your option) any later version.
 *
 *   Elektroid 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 Elektroid. If not, see <http://www.gnu.org/licenses/>.
 */

#include "backend.h"

#ifndef CONNECTOR_H
#define CONNECTOR_H

#define FS_ICON_CHIP "elektroid-chip-symbolic"
#define FS_ICON_FILE "elektroid-file-symbolic"
#define FS_ICON_FX_DELAY "elektroid-fx-delay-symbolic"
#define FS_ICON_FX_MODULATION "elektroid-fx-modulation-symbolic"
#define FS_ICON_FX_REVERB "elektroid-fx-reverb-symbolic"
#define FS_ICON_KEYS "elektroid-keys-symbolic"
#define FS_ICON_OSCILLATOR "elektroid-oscillator-symbolic"
#define FS_ICON_PRESET "elektroid-preset-symbolic"
#define FS_ICON_PROJECT "elektroid-project-symbolic"
#define FS_ICON_SEQUENCE "elektroid-sequence-symbolic"
#define FS_ICON_SETTINGS "elektroid-settings-symbolic"
#define FS_ICON_SLICE "elektroid-slice-symbolic"
#define FS_ICON_TRACK "elektroid-track-symbolic"
#define FS_ICON_TRACK_LOOP "elektroid-track-loop-symbolic"
#define FS_ICON_WAVE "elektroid-wave-symbolic"
#define FS_ICON_WAVE_LOOP "elektroid-wave-loop-symbolic"
#define FS_ICON_WAVETABLE "elektroid-wavetable-symbolic"

#define GET_SAVE_EXT(ops,backend) (ops->get_exts(backend, ops)[0])

enum item_type
{
  ITEM_TYPE_NONE = 0,
  ITEM_TYPE_FILE = 'F',
  ITEM_TYPE_DIR = 'D'
};

//name must be filled up always. If no name is available, this can be a string representation of the id without padding. See set_item_name_from_id function.
//In slot mode, id needs to be filled up. A typical values for this would be the preset number. It can start at 0 or 1 but it is recommended to mimmic the particular device.
//As the id is used to address items, it is recommended to follow the same user naming convention as the device regardless of its internal meaning or representation.
//In default mode (not slot mode), id can be used for any or no purpose. It's still possible to use the id as the filename by using the FS_OPTION_ID_AS_FILENAME option.
//A -1 size (unknown) will show no size in either the GUI or the CLI.
//A 0 size item will be skipped by the `backup` command for efficiency purposes. It depends on the connector if this can be determined.
//A download on an item of size 0 is still possible and the actual size might be different than 0 as an empty slot might contain an initialized item.
//However, the GUI must list all the items always as the slot destination is always needed and the `ls` command will list them as well.
//If the size column is not used at all, do not use FS_OPTION_SHOW_SIZE_COLUMN.

#define ITEM_NAME_MAX PATH_MAX
#define ITEM_SLOT_MAX 32
#define ITEM_OBJECT_INFO_MAX PATH_MAX

#define DEFAULT_PRINT_COL_SIZE_LEN 10
#define DEFAULT_PRINT_COL_ID_LEN 10
#define DEFAULT_PRINT_COL_SLOT_LEN 10
#define DEFAULT_PRINT_COL_NAME_LEN 32

struct item
{
  enum item_type type;
  gchar name[ITEM_NAME_MAX];
  // Used only by slot filesystems
  gint32 id;
  // A FS_OPTION_SLOT_STORAGE might fill the slot.
  // The slot will be shown on the GUI if FS_OPTION_SHOW_SLOT_COLUMN is used.
  // If the filesystem uses the default print_item, it will be used by the CLI.
  // In these both cases, the slot must be filled.
  // If a filesystem has a custom print_item and is not using FS_OPTION_SHOW_SLOT_COLUMN, it might be left unfilled.
  // A slot must have the same length and format of the hardware. A63 or 031 are valid values.
  gchar slot[ITEM_SLOT_MAX];
  gint64 size;
  //Optionally filled up structs by filesystems.
  //Filesystem options must indicate if these are in use with FS_OPTION_SHOW_SAMPLE_COLUMNS and FS_OPTION_SHOW_INFO_COLUMN.
  struct sample_info sample_info;
  gchar object_info[ITEM_OBJECT_INFO_MAX];
};

struct item_iterator;

typedef gint (*iterator_next) (struct item_iterator *);

typedef void (*iterator_free) (void *);

struct item_iterator
{
  gchar *dir;
  iterator_next next;
  iterator_free free;
  void *data;
  struct item item;
};

struct fs_operations;

typedef void (*fs_print_item) (struct item_iterator *, struct backend *,
			       const struct fs_operations *);

typedef gint (*fs_init_iter_func) (struct backend * backend,
				   struct item_iterator * iter,
				   const gchar * dir, const gchar ** exts);

typedef gint (*fs_path_func) (struct backend *, const gchar *);

typedef gint (*fs_src_dst_func) (struct backend *, const gchar *,
				 const gchar *);

typedef gint (*fs_remote_file_op) (struct backend *, const gchar *,
				   struct idata *, struct task_control *);

typedef gchar *(*fs_get_item_slot) (struct item *, struct backend *);

typedef const gchar **(*fs_get_exts) (struct backend *,
				      const struct fs_operations *);

typedef gchar *(*fs_get_path) (struct backend * backend,
			       const struct fs_operations * ops,
			       const gchar * dst_dir, const gchar * src_path,
			       struct idata * idata);

typedef void (*fs_select_item) (struct backend *, const gchar *,
				struct item *);

typedef gboolean (*fs_file_exists) (struct backend *, const gchar *);

// All the function members that return gint should return 0 if no error and a negative number in case of error.
// errno values are recommended as will provide the user with a meaningful message. In particular,
// ENOSYS could be used when a particular device does not support a feature that other devices implementing the same filesystem do.

// rename and move are different operations. If move is implemented, rename must behave the same way. However, it's perfectly
// possible to implement rename without implementing move. This is the case in slot mode filesystems.

struct fs_operations
{
  guint32 id;
  guint32 options;
  const gchar *name;		// This needs to be unique among all the filesystems of a given connector. Used to address filesystems. Using spaces is discouraged and hyphen is the suggested replacement.
  const gchar *gui_name;
  const gchar *gui_icon;
  const gchar *file_icon;
  guint32 max_name_len;
  fs_init_iter_func readdir;	//This function runs on its own thread so it can take as long as needed in order to make calls to item_iterator_next not to wait for IO.
  fs_file_exists file_exists;
  fs_print_item print_item;	// Optional. If not used, the default printing will be used.
  fs_path_func mkdir;
  fs_path_func delete;
  fs_src_dst_func rename;
  fs_src_dst_func move;
  fs_src_dst_func copy;
  fs_src_dst_func swap;
  fs_remote_file_op download;	//Donload a resource from the filesystem to memory.
  fs_remote_file_op upload;	//Upload a resource from memory to the filesystem.
  fs_remote_file_op save;	//Write a file from memory to the OS storage. Typically used after download.
  fs_remote_file_op load;	//Load a file from the OS storage into memory. Typically used before upload.
  fs_get_exts get_exts;		//Length must be one at least. First element will be used as file extension and all will be used as loading extensions.
  fs_get_path get_upload_path;
  fs_get_path get_download_path;
  fs_select_item select_item;
};

enum fs_options
{
  //Show the audio player.
  FS_OPTION_SAMPLE_EDITOR = 1,
  //Allow mono samples. Only useful if used together with FS_OPTION_SAMPLE_EDITOR
  FS_OPTION_MONO = (1 << 1),
  //Allow stereo samples. Only useful if used together with FS_OPTION_SAMPLE_EDITOR
  FS_OPTION_STEREO = (1 << 2),
  //Every operation will block the remote browser.
  FS_OPTION_SINGLE_OP = (1 << 3),
  //In slot storage mode, the item name in dst_path passed to get_upload_path is the ID.
  //DND is only possible over a concrete slot.
  //A DND operation of several items over a slot will behave as dropping the first item over the destination slot and the rest over the following ones.
  FS_OPTION_SLOT_STORAGE = (1 << 4),
  //Show column options. Name column is always showed.
  FS_OPTION_SHOW_ID_COLUMN = (1 << 5),	// Numeric value; not 0 padded. For a custom string value use FS_OPTION_SHOW_SLOT_COLUMN and set slot accordingly.
  FS_OPTION_SHOW_SIZE_COLUMN = (1 << 6),
  FS_OPTION_SHOW_SLOT_COLUMN = (1 << 7),
  FS_OPTION_SHOW_INFO_COLUMN = (1 << 8),
  FS_OPTION_SHOW_SAMPLE_COLUMNS = (1 << 9),	// Filesystems indicating this must fill the sample_info struct in the item.
  //This requires the function readdir to be relatively fast because canceling the search will block the GUI. Requires item.sample_info to be initialized if used with FS_OPTION_SAMPLE_EDITOR.
  FS_OPTION_ALLOW_SEARCH = (1 << 10),
  //This blocks the editor as the transfers are done with audio.
  FS_OPTION_AUDIO_LINK = (1 << 11)
};

typedef gint (*connector_handshake) (struct backend * backend);

// This mimics enum backend_type.
enum connector_type
{
  CONNECTOR_TYPE_SYSTEM = 1,
  CONNECTOR_TYPE_MIDI,
  CONNECTOR_TYPE_NO_MIDI
};

struct connector
{
  const gchar *name;		// This needs to be unique among all the connectors. Using spaces is discouraged and hyphen is the suggested replacement.
  connector_handshake handshake;
  enum connector_type type;
  const gchar *device_name;	//Only used for non MIDI devices when a virtual device is created.
  //If the backend device name matches this regex, the handshake will be run before than the connectors that didn't match.
  const gchar *regex;
};

void item_iterator_init (struct item_iterator *iter, const gchar * dir,
			 void *data, iterator_next next, iterator_free free);

gint item_iterator_next (struct item_iterator *iter);

void item_iterator_free (struct item_iterator *iter);

gboolean item_iterator_is_dir_or_matches_exts (struct item_iterator
					       *iter,
					       const gchar ** extensions);

/**
 * Returns the filename for an item, which is a string that uniquely idenfifies an item.
 * In a PC, filenames are typically strings but in embedded devices this could be just a number (in string format).
 * Typically, in these systems, several slots can have the same name but the id is an address to a memory slot.
 * @param item
 * @param options The options member in the fs_operations struct.
 */
gchar *item_get_filename (struct item *item, guint32 options);

void item_set_name (struct item *item, const gchar * format, ...);

void item_set_object_info (struct item *item, const gchar * format, ...);

#endif
