/*-
 * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer,
 *    without modification.
 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
 *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
 *    redistribution must be conditioned upon including a substantially
 *    similar Disclaimer requirement for further binary redistribution.
 *
 * NO WARRANTY
 * 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 NONINFRINGEMENT, MERCHANTIBILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
 * 
 */

#ifndef _BHND_NVRAM_BHND_NVRAM_VALUE_H_
#define _BHND_NVRAM_BHND_NVRAM_VALUE_H_

#include <sys/refcount.h>

#ifdef _KERNEL
#include <sys/stdarg.h>
#else /* !_KERNEL */
#include <stdarg.h>
#endif /* _KERNEL */

#include "bhnd_nvram.h"

typedef struct bhnd_nvram_val_fmt	bhnd_nvram_val_fmt;
typedef struct bhnd_nvram_val		bhnd_nvram_val;

const char			*bhnd_nvram_val_fmt_name(
				     const bhnd_nvram_val_fmt *fmt);

const bhnd_nvram_val_fmt	*bhnd_nvram_val_default_fmt(
				      bhnd_nvram_type type);

int				 bhnd_nvram_val_init(bhnd_nvram_val *value,
				     const bhnd_nvram_val_fmt *fmt,
				     const void *inp, size_t ilen,
				     bhnd_nvram_type itype, uint32_t flags);

int				 bhnd_nvram_val_convert_init(
				     bhnd_nvram_val *value,
				     const bhnd_nvram_val_fmt *fmt,
				     bhnd_nvram_val *src, uint32_t flags);

int				 bhnd_nvram_val_new(bhnd_nvram_val **value,
				     const bhnd_nvram_val_fmt *fmt,
				     const void *inp, size_t ilen,
				     bhnd_nvram_type itype, uint32_t flags);

int				 bhnd_nvram_val_convert_new(
				     bhnd_nvram_val **value,
				     const bhnd_nvram_val_fmt *fmt,
				     bhnd_nvram_val *src, uint32_t flags);

bhnd_nvram_val			*bhnd_nvram_val_copy(bhnd_nvram_val *value);

void				 bhnd_nvram_val_release(
				     bhnd_nvram_val *value);

int				 bhnd_nvram_val_encode(bhnd_nvram_val *value,
				     void *outp, size_t *olen,
				     bhnd_nvram_type otype);

int				 bhnd_nvram_val_encode_elem(
				     bhnd_nvram_val *value, const void *inp,
				     size_t ilen, void *outp, size_t *olen,
				     bhnd_nvram_type otype);

int				 bhnd_nvram_val_printf(bhnd_nvram_val *value,
				     const char *fmt, char *outp, size_t *olen,
				     ...);
int				 bhnd_nvram_val_vprintf(bhnd_nvram_val *value,
				     const char *fmt, char *outp, size_t *olen,
				     va_list ap);

const void			*bhnd_nvram_val_bytes(bhnd_nvram_val *value,
				     size_t *olen, bhnd_nvram_type *otype);

bhnd_nvram_type			 bhnd_nvram_val_type(bhnd_nvram_val *value);
bhnd_nvram_type			 bhnd_nvram_val_elem_type(
				     bhnd_nvram_val *value);

const void			*bhnd_nvram_val_next(bhnd_nvram_val *value,
				     const void *prev, size_t *olen);

size_t				 bhnd_nvram_val_nelem(bhnd_nvram_val *value);

/**
 * NVRAM value flags
 */
enum {
	/**
	 * Do not allocate additional space for value data; all data must be
	 * represented inline within the value structure (default).
	 */
	BHND_NVRAM_VAL_FIXED		= (0<<0),

	/**
	 * Automatically allocate additional space for value data if it cannot
	 * be represented within the value structure.
	 */
	BHND_NVRAM_VAL_DYNAMIC		= (1<<0),

	/** 
	 * Copy the value data upon initialization. (default).
	 */
	BHND_NVRAM_VAL_COPY_DATA	= (0<<1),

	/**
	 * Do not perform an initial copy of the value data; the data must
	 * remain valid for the lifetime of the NVRAM value.
	 * 
	 * Value data will still be copied if the value itself is copied to the
	 * heap.
	 */
	BHND_NVRAM_VAL_BORROW_DATA	= (1<<1),

	/**
	 * Do not copy the value data when copying the value to the heap; the
	 * vlaue data is assumed to be statically allocated and must remain
	 * valid for the lifetime of the process.
	 * 
	 * Implies BHND_NVRAM_VAL_BORROW_DATA.
	 */
	BHND_NVRAM_VAL_STATIC_DATA	= (1<<2),
};

/**
 * @internal
 *
 * NVRAM value storage types.
 */
typedef enum {
	/**
	 * The value structure has an automatic storage duration
	 * (e.g. it is stack allocated, or is otherwise externally managed),
	 * and no destructors will be run prior to deallocation of the value.
	 *
	 * When performing copy/retain, the existing structure must be copied
	 * to a new heap allocation.
	 */
	BHND_NVRAM_VAL_STORAGE_AUTO	= 0,

	/**
	 * The value structure was heap allocated and is fully managed by the
	 * the NVRAM value API.
	 *
	 * When performing copy/retain, the existing structure may be retained
	 * as-is.
	 */
	BHND_NVRAM_VAL_STORAGE_DYNAMIC	= 2,

	/**
	 * The value structure has a static storage duration, and will never
	 * be deallocated.
	 *
	 * When performing copy/retain, the existing structure may be referenced
	 * without modification.
	 */
	BHND_NVRAM_VAL_STORAGE_STATIC	= 3,
} bhnd_nvram_val_storage;

/**
 * @internal
 *
 * NVRAM data storage types.
 */
typedef enum {
	/** Value has no active representation. This is the default for
	*  zero-initialized value structures. */
	BHND_NVRAM_VAL_DATA_NONE	= 0,

	/** Value data is represented inline */
	BHND_NVRAM_VAL_DATA_INLINE	= 1,

	/**
	 * Value represented by an external reference to data with a static
	 * storage location. The data need not be copied if copying the value.
	 */
	BHND_NVRAM_VAL_DATA_EXT_STATIC	= 2,

	/**
	 * Value represented by weak external reference, which must be copied
	 * if copying the value.
	 */
	BHND_NVRAM_VAL_DATA_EXT_WEAK	= 3,

	/**
	 * Value represented by an external reference that must be deallocated
	 * when deallocating the value.
	 */
	BHND_NVRAM_VAL_DATA_EXT_ALLOC	= 4,
} bhnd_nvram_val_data_storage;

/**
 * NVRAM value
 */
struct bhnd_nvram_val {
	volatile u_int			 refs;		/**< reference count */
	bhnd_nvram_val_storage		 val_storage;	/**< value structure storage */
	const bhnd_nvram_val_fmt	*fmt;		/**< value format */
	bhnd_nvram_val_data_storage	 data_storage;	/**< data storage */
	bhnd_nvram_type			 data_type;	/**< data type */
	size_t				 data_len;	/**< data size */

	/** data representation */
	union {
		uint8_t			 u8[8];		/**< 8-bit unsigned data */
		uint16_t		 u16[4];	/**< 16-bit unsigned data */
		uint32_t		 u32[2];	/**< 32-bit unsigned data */
		uint32_t		 u64[1];	/**< 64-bit unsigned data */
		int8_t			 i8[8];		/**< 8-bit signed data */
		int16_t			 i16[4];	/**< 16-bit signed data */
		int32_t			 i32[2];	/**< 32-bit signed data */
		int64_t			 i64[1];	/**< 64-bit signed data */
		unsigned char		 ch[8];		/**< 8-bit character data */
		bhnd_nvram_bool_t	 b[8];		/**< 8-bit boolean data */
		const void		*ptr;		/**< external data */
	} data;
};

/** Declare a bhnd_nvram_val_fmt with name @p _n */
#define	BHND_NVRAM_VAL_FMT_DECL(_n)	\
	extern const bhnd_nvram_val_fmt bhnd_nvram_val_ ## _n ## _fmt;

BHND_NVRAM_VAL_FMT_DECL(bcm_decimal);
BHND_NVRAM_VAL_FMT_DECL(bcm_hex);
BHND_NVRAM_VAL_FMT_DECL(bcm_leddc);
BHND_NVRAM_VAL_FMT_DECL(bcm_macaddr);
BHND_NVRAM_VAL_FMT_DECL(bcm_string);

BHND_NVRAM_VAL_FMT_DECL(uint8);
BHND_NVRAM_VAL_FMT_DECL(uint16);
BHND_NVRAM_VAL_FMT_DECL(uint32);
BHND_NVRAM_VAL_FMT_DECL(uint64);
BHND_NVRAM_VAL_FMT_DECL(int8);
BHND_NVRAM_VAL_FMT_DECL(int16);
BHND_NVRAM_VAL_FMT_DECL(int32);
BHND_NVRAM_VAL_FMT_DECL(int64);
BHND_NVRAM_VAL_FMT_DECL(char);
BHND_NVRAM_VAL_FMT_DECL(bool);
BHND_NVRAM_VAL_FMT_DECL(string);
BHND_NVRAM_VAL_FMT_DECL(data);
BHND_NVRAM_VAL_FMT_DECL(null);

BHND_NVRAM_VAL_FMT_DECL(uint8_array);
BHND_NVRAM_VAL_FMT_DECL(uint16_array);
BHND_NVRAM_VAL_FMT_DECL(uint32_array);
BHND_NVRAM_VAL_FMT_DECL(uint64_array);
BHND_NVRAM_VAL_FMT_DECL(int8_array);
BHND_NVRAM_VAL_FMT_DECL(int16_array);
BHND_NVRAM_VAL_FMT_DECL(int32_array);
BHND_NVRAM_VAL_FMT_DECL(int64_array);
BHND_NVRAM_VAL_FMT_DECL(char_array);
BHND_NVRAM_VAL_FMT_DECL(bool_array);
BHND_NVRAM_VAL_FMT_DECL(string_array);

/** Shared NULL value instance */
#define	BHND_NVRAM_VAL_NULL	(&bhnd_nvram_val_null)
extern bhnd_nvram_val bhnd_nvram_val_null;

#endif /* _BHND_NVRAM_BHND_NVRAM_VALUE_H_ */
