/*
  Copyright 2011-2020 David Robillard <http://drobilla.net>

  Permission to use, copy, modify, and/or distribute this software for any
  purpose with or without fee is hereby granted, provided that the above
  copyright notice and this permission notice appear in all copies.

  THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef SERD_ITER_H
#define SERD_ITER_H

#include "statement.h"

#include "serd/serd.h"
#include "zix/btree.h"

#include <stdbool.h>
#include <stdint.h>

/** Triple ordering */
typedef enum {
	SPO,   ///<         Subject,   Predicate, Object
	SOP,   ///<         Subject,   Object,    Predicate
	OPS,   ///<         Object,    Predicate, Subject
	OSP,   ///<         Object,    Subject,   Predicate
	PSO,   ///<         Predicate, Subject,   Object
	POS,   ///<         Predicate, Object,    Subject
	GSPO,  ///< Graph,  Subject,   Predicate, Object
	GSOP,  ///< Graph,  Subject,   Object,    Predicate
	GOPS,  ///< Graph,  Object,    Predicate, Subject
	GOSP,  ///< Graph,  Object,    Subject,   Predicate
	GPSO,  ///< Graph,  Predicate, Subject,   Object
	GPOS   ///< Graph,  Predicate, Object,    Subject
} SerdOrder;

/** Mode for searching or iteration */
typedef enum {
	ALL,           ///< Iterate over entire store
	RANGE,         ///< Iterate over range with equal prefix
	FILTER_RANGE,  ///< Iterate over range with equal prefix, filtering
	FILTER_ALL     ///< Iterate to end of store, filtering
} SearchMode;

struct SerdIterImpl {
	const SerdModel* model;     ///< Model being iterated over
	uint64_t         version;   ///< Model version when iterator was created
	ZixBTreeIter*    cur;       ///< Current DB cursor
	SerdQuad         pat;       ///< Pattern (in ordering order)
	SerdOrder        order;     ///< Store order (which index)
	SearchMode       mode;      ///< Iteration mode
	int              n_prefix;  ///< Prefix for RANGE and FILTER_RANGE
};

#define NUM_ORDERS 12
#define TUP_LEN 4

/**
   Quads of indices for each order, from most to least significant
   (array indexed by SordOrder)
*/
static const int orderings[NUM_ORDERS][TUP_LEN] = {
	{ 0, 1, 2, 3 }, { 0, 2, 1, 3 },  // SPO, SOP
	{ 2, 1, 0, 3 }, { 2, 0, 1, 3 },  // OPS, OSP
	{ 1, 0, 2, 3 }, { 1, 2, 0, 3 },  // PSO, POS
	{ 3, 0, 1, 2 }, { 3, 0, 2, 1 },  // GSPO, GSOP
	{ 3, 2, 1, 0 }, { 3, 2, 0, 1 },  // GOPS, GOSP
	{ 3, 1, 0, 2 }, { 3, 1, 2, 0 }   // GPSO, GPOS
};

SerdIter* serd_iter_new(const SerdModel* model,
                        ZixBTreeIter*    cur,
                        const SerdQuad   pat,
                        SerdOrder        order,
                        SearchMode       mode,
                        int              n_prefix);

bool serd_iter_scan_next(SerdIter* iter);

bool serd_quad_match(const SerdQuad x, const SerdQuad y);

#endif  // SERD_ITER_H