/*
 * Copyright 2016-2018, Intel Corporation
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in
 *       the documentation and/or other materials provided with the
 *       distribution.
 *
 *     * Neither the name of the copyright holder nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * check_util.h -- internal definitions check util
 */
#ifndef CHECK_UTIL_H
#define CHECK_UTIL_H

#include <time.h>
#include <limits.h>
#include <sys/param.h>

#ifdef __cplusplus
extern "C" {
#endif

#define CHECK_STEP_COMPLETE	UINT_MAX
#define CHECK_INVALID_QUESTION	UINT_MAX

#define REQUIRE_ADVANCED	"the following error can be fixed using " \
				"PMEMPOOL_CHECK_ADVANCED flag"

#ifndef min
#define min(a, b) ((a) < (b) ? (a) : (b))
#endif

/* check control context */
struct check_data;
struct arena;

/* queue of check statuses */
struct check_status;

/* container storing state of all check steps */
#define PREFIX_MAX_SIZE 30
typedef struct {
	unsigned init_done;
	unsigned step;

	unsigned replica;
	unsigned part;

	int single_repl;
	int single_part;

	struct pool_set *set;
	int is_dev_dax;

	struct pool_hdr *hdrp;
	/* copy of the pool header in host byte order */
	struct pool_hdr hdr;
	int hdr_valid;
	/*
	 * If pool header has been modified this field indicates that
	 * the pool parameters structure requires refresh.
	 */
	int pool_hdr_modified;

	unsigned healthy_replicas;

	struct pool_hdr *next_part_hdrp;
	struct pool_hdr *prev_part_hdrp;
	struct pool_hdr *next_repl_hdrp;
	struct pool_hdr *prev_repl_hdrp;

	int next_part_hdr_valid;
	int prev_part_hdr_valid;
	int next_repl_hdr_valid;
	int prev_repl_hdr_valid;

	/* valid poolset uuid */
	uuid_t *valid_puuid;
	/* valid part uuid */
	uuid_t *valid_uuid;

	/* valid part pool header */
	struct pool_hdr *valid_part_hdrp;
	int valid_part_done;
	unsigned valid_part_replica;

	char prefix[PREFIX_MAX_SIZE];

	struct arena *arenap;
	uint64_t offset;
	uint32_t narena;

	uint8_t *bitmap;
	uint8_t *dup_bitmap;
	uint8_t *fbitmap;

	struct list *list_inval;
	struct list *list_flog_inval;
	struct list *list_unmap;

	struct {
		int btti_header;
		int btti_backup;
	} valid;

	struct {
		struct btt_info btti;
		uint64_t btti_offset;
	} pool_valid;
} location;

/* check steps */
void check_bad_blocks(PMEMpoolcheck *ppc);
void check_backup(PMEMpoolcheck *ppc);
void check_pool_hdr(PMEMpoolcheck *ppc);
void check_pool_hdr_uuids(PMEMpoolcheck *ppc);
void check_sds(PMEMpoolcheck *ppc);
void check_log(PMEMpoolcheck *ppc);
void check_blk(PMEMpoolcheck *ppc);
void check_btt_info(PMEMpoolcheck *ppc);
void check_btt_map_flog(PMEMpoolcheck *ppc);
void check_write(PMEMpoolcheck *ppc);

struct check_data *check_data_alloc(void);
void check_data_free(struct check_data *data);

uint32_t check_step_get(struct check_data *data);
void check_step_inc(struct check_data *data);
location *check_get_step_data(struct check_data *data);

void check_end(struct check_data *data);
int check_is_end_util(struct check_data *data);

int check_status_create(PMEMpoolcheck *ppc, enum pmempool_check_msg_type type,
		uint32_t arg, const char *fmt, ...) FORMAT_PRINTF(4, 5);
void check_status_release(PMEMpoolcheck *ppc, struct check_status *status);
void check_clear_status_cache(struct check_data *data);
struct check_status *check_pop_question(struct check_data *data);
struct check_status *check_pop_error(struct check_data *data);
struct check_status *check_pop_info(struct check_data *data);
bool check_has_error(struct check_data *data);
bool check_has_answer(struct check_data *data);
int check_push_answer(PMEMpoolcheck *ppc);

struct pmempool_check_status *check_status_get_util(
	struct check_status *status);
int check_status_is(struct check_status *status,
	enum pmempool_check_msg_type type);

/* create info status */
#define CHECK_INFO(ppc, ...)\
	check_status_create(ppc, PMEMPOOL_CHECK_MSG_TYPE_INFO, 0, __VA_ARGS__)

/* create info status and append error message based on errno */
#define CHECK_INFO_ERRNO(ppc, ...)\
	check_status_create(ppc, PMEMPOOL_CHECK_MSG_TYPE_INFO,\
			(uint32_t)errno, __VA_ARGS__)

/* create error status */
#define CHECK_ERR(ppc, ...)\
	check_status_create(ppc, PMEMPOOL_CHECK_MSG_TYPE_ERROR, 0, __VA_ARGS__)

/* create question status */
#define CHECK_ASK(ppc, question, ...)\
	check_status_create(ppc, PMEMPOOL_CHECK_MSG_TYPE_QUESTION, question,\
		__VA_ARGS__)

#define CHECK_NOT_COMPLETE(loc, steps)\
	((loc)->step != CHECK_STEP_COMPLETE &&\
		((steps)[(loc)->step].check != NULL ||\
		(steps)[(loc)->step].fix != NULL))

int check_answer_loop(PMEMpoolcheck *ppc, location *data,
	void *ctx, int fail_on_no,
	int (*callback)(PMEMpoolcheck *, location *, uint32_t, void *ctx));
int check_questions_sequence_validate(PMEMpoolcheck *ppc);

const char *check_get_time_str(time_t time);
const char *check_get_uuid_str(uuid_t uuid);
const char *check_get_pool_type_str(enum pool_type type);

void check_insert_arena(PMEMpoolcheck *ppc, struct arena *arenap);

#ifdef _WIN32
void cache_to_utf8(struct check_data *data, char *buf, size_t size);
#endif

#define CHECK_IS(ppc, flag)\
	util_flag_isset((ppc)->args.flags, PMEMPOOL_CHECK_ ## flag)

#define CHECK_IS_NOT(ppc, flag)\
	util_flag_isclr((ppc)->args.flags, PMEMPOOL_CHECK_ ## flag)

#define CHECK_WITHOUT_FIXING(ppc)\
	CHECK_IS_NOT(ppc, REPAIR) || CHECK_IS(ppc, DRY_RUN)

#ifdef __cplusplus
}
#endif

#endif
