/* Copyright (c) 2010, 2011
 * The Regents of the University of Michigan
 * All Rights Reserved
 * 
 * Olga Kornievskaia <aglo@umich.edu>
 * Casey Bodley <cbodley@umich.edu>
 *
 * Permission is granted to use, copy and redistribute this software
 * for noncommercial education and research purposes, so long as no
 * fee is charged, and so long as the name of the University of Michigan
 * is not used in any advertising or publicity pertaining to the use
 * or distribution of this software without specific, written prior
 * authorization.  Permission to modify or otherwise create derivative
 * works of this software is not granted.
 *
 * This software is provided as is, without representation or warranty
 * of any kind either express or implied, including without limitation
 * the implied warranties of merchantability, fitness for a particular
 * purpose, or noninfringement.  The Regents of the University of
 * Michigan shall not be liable for any damages, including special,
 * indirect, incidental, or consequential damages, with respect to any
 * claim arising out of or in connection with the use of the software,
 * even if it has been or is hereafter advised of the possibility of
 * such damages.
 */

#ifndef __NFS41_DAEMON_UTIL_H__
#define __NFS41_DAEMON_UTIL_H__

#include "nfs41_types.h"

extern DWORD NFS41D_VERSION;
struct __nfs41_session;
struct __nfs41_write_verf;
enum stable_how4;

int safe_read(unsigned char **pos, uint32_t *remaining, void *dest, uint32_t dest_len);
int safe_write(unsigned char **pos, uint32_t *remaining, void *dest, uint32_t dest_len);
int get_name(unsigned char **pos, uint32_t *remaining, const char **out_name);

const char* strip_path(
    IN const char *path,
    OUT uint32_t *len_out OPTIONAL);

uint32_t max_read_size(
    IN const struct __nfs41_session *session,
    IN const nfs41_fh *fh);
uint32_t max_write_size(
    IN const struct __nfs41_session *session,
    IN const nfs41_fh *fh);

bool_t verify_write(
    IN nfs41_write_verf *verf,
    IN OUT enum stable_how4 *stable);
bool_t verify_commit(
    IN nfs41_write_verf *verf);

/* bitmap4 */
static __inline bool_t bitmap_isset(
    IN const bitmap4 *mask,
    IN uint32_t word,
    IN uint32_t flag)
{
    return mask->count > word && mask->arr[word] & flag;
}
static __inline void bitmap_set(
    IN bitmap4 *mask,
    IN uint32_t word,
    IN uint32_t flag)
{
    if (mask->count > word)
        mask->arr[word] |= flag;
    else {
        mask->count = word + 1;
        mask->arr[word] = flag;
    }
}
static __inline void bitmap_unset(
    IN bitmap4 *mask,
    IN uint32_t word,
    IN uint32_t flag)
{
    if (mask->count > word) {
        mask->arr[word] &= ~flag;
        while (mask->count && mask->arr[mask->count-1] == 0)
            mask->count--;
    }
}

ULONG nfs_file_info_to_attributes(
    IN const nfs41_file_info *info);
void nfs_to_basic_info(
    IN const nfs41_file_info *info,
    OUT PFILE_BASIC_INFO basic_out);
void nfs_to_standard_info(
    IN const nfs41_file_info *info,
    OUT PFILE_STANDARD_INFO std_out);

/* http://msdn.microsoft.com/en-us/library/ms724290%28VS.85%29.aspx:
 * A file time is a 64-bit value that represents the number of
 * 100-nanosecond intervals that have elapsed since 12:00 A.M.
 * January 1, 1601 Coordinated Universal Time (UTC). */
#define FILETIME_EPOCH 116444736000000000LL

static __inline void file_time_to_nfs_time(
    IN const PLARGE_INTEGER file_time,
    OUT nfstime4 *nfs_time)
{
    LONGLONG diff = file_time->QuadPart - FILETIME_EPOCH;
    nfs_time->seconds = diff / 10000000;
    nfs_time->nseconds = (uint32_t)((diff % 10000000)*100);
}

static __inline void nfs_time_to_file_time(
    IN const nfstime4 *nfs_time,
    OUT PLARGE_INTEGER file_time)
{
    file_time->QuadPart = FILETIME_EPOCH +
        nfs_time->seconds * 10000000 +
        nfs_time->nseconds / 100;
}

void get_file_time(
    OUT PLARGE_INTEGER file_time);
void get_nfs_time(
    OUT nfstime4 *nfs_time);

static __inline void nfstime_normalize(
    IN OUT nfstime4 *nfstime)
{
    /* return time in normalized form (0 <= nsec < 1s) */
    while ((int32_t)nfstime->nseconds < 0) {
        nfstime->nseconds += 1000000000;
        nfstime->seconds--;
    }
}
static __inline void nfstime_diff(
    IN const nfstime4 *lhs,
    IN const nfstime4 *rhs,
    OUT nfstime4 *result)
{
    /* result = lhs - rhs */
    result->seconds = lhs->seconds - rhs->seconds;
    result->nseconds = lhs->nseconds - rhs->nseconds;
    nfstime_normalize(result);
}
static __inline void nfstime_abs(
    IN const nfstime4 *nt,
    OUT nfstime4 *result)
{
    if (nt->seconds < 0) {
        const nfstime4 zero = { 0, 0 };
        nfstime_diff(&zero, nt, result); /* result = 0 - nt */
    } else if (result != nt)
        memcpy(result, nt, sizeof(nfstime4));
}


int create_silly_rename(
    IN nfs41_abs_path *path,
    IN const nfs41_fh *fh,
    OUT nfs41_component *silly);

bool_t multi_addr_find(
    IN const multi_addr4 *addrs,
    IN const netaddr4 *addr,
    OUT OPTIONAL uint32_t *index_out);

/* nfs_to_windows_error
 *   Returns a windows ERROR_ code corresponding to the given NFS4ERR_ status.
 * If the status is outside the range of valid NFS4ERR_ values, it is returned
 * unchanged.  Otherwise, if the status does not match a value in the mapping,
 * a debug warning is generated and the default_error value is returned.
 */
int nfs_to_windows_error(int status, int default_error);


__inline uint32_t align8(uint32_t offset) {
    return 8 + ((offset - 1) & ~7);
}
__inline uint32_t align4(uint32_t offset) {
    return 4 + ((offset - 1) & ~3);
}

/* path parsing */
__inline int is_delimiter(char c) {
    return c == '\\' || c == '/' || c == '\0';
}
__inline const char* next_delimiter(const char *pos, const char *end) {
    while (pos < end && !is_delimiter(*pos))
        pos++;
    return pos;
}
__inline const char* prev_delimiter(const char *pos, const char *start) {
    while (pos > start && !is_delimiter(*pos))
        pos--;
    return pos;
}
__inline const char* next_non_delimiter(const char *pos, const char *end) {
    while (pos < end && is_delimiter(*pos))
        pos++;
    return pos;
}
__inline const char* prev_non_delimiter(const char *pos, const char *start) {
    while (pos > start && is_delimiter(*pos))
        pos--;
    return pos;
}

bool_t next_component(
    IN const char *path,
    IN const char *path_end,
    OUT nfs41_component *component);

bool_t last_component(
    IN const char *path,
    IN const char *path_end,
    OUT nfs41_component *component);

bool_t is_last_component(
    IN const char *path,
    IN const char *path_end);

void abs_path_copy(
    OUT nfs41_abs_path *dst,
    IN const nfs41_abs_path *src);

void path_fh_init(
    OUT nfs41_path_fh *file,
    IN nfs41_abs_path *path);

void fh_copy(
    OUT nfs41_fh *dst,
    IN const nfs41_fh *src);

void path_fh_copy(
    OUT nfs41_path_fh *dst,
    IN const nfs41_path_fh *src);

__inline int valid_handle(HANDLE handle) {
    return handle != INVALID_HANDLE_VALUE && handle != 0;
}

#endif /* !__NFS41_DAEMON_UTIL_H__ */
