/*******************************************************************************
 * This file is part of SWIFT.
 * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 ******************************************************************************/
#ifndef LOGGER_LOGGER_HEADER_H
#define LOGGER_LOGGER_HEADER_H

#include "logger_tools.h"

#include <stdio.h>
#include <stdlib.h>

/* Number of character for the version information */
#define LOGGER_VERSION_SIZE 20
/* Number of bytes for the mask information in the headers
   (need to be large enough for the larges mask) */
#define LOGGER_MASK_SIZE 2
/* Number of bytes for the offset size in the headers */
#define LOGGER_OFFSET_SIZE (8 - LOGGER_MASK_SIZE)

enum logger_offset_direction {
  logger_offset_backward = 0,
  logger_offset_forward,
  logger_offset_corrupted,
  /* Number of offset type. */
  logger_offset_count,
};

/**
 * @brief Names of the offset directions.
 */
extern const char *logger_offset_name[];

struct logger_logfile;

/**
 * @brief This structure contains everything from the file header.
 *
 * This structure is initialized by #header_read and need to be freed
 * with #header_free.
 *
 * The information contained by the header can be easily access with
 * the functions #header_get_record_size_from_mask and #header_get_field_index.
 *
 * The only function that modify the file is #header_change_offset_direction.
 */
struct header {
  /* Dump's major version. */
  int major_version;

  /* Dump's minor version. */
  int minor_version;

  /* Offset of the first record. */
  size_t offset_first_record;

  /* Number of bytes for strings. */
  unsigned int string_length;

  /* Number of masks. */
  int masks_count;

  /* List of masks. */
  struct mask_data *masks;

  /* Direction of the offset in the records. */
  enum logger_offset_direction offset_direction;

  /* The corresponding log. */
  struct logger_logfile *log;

  /* Timestamp mask */
  size_t timestamp_mask;
};

void header_print(const struct header *h);
void header_free(struct header *h);
int header_get_field_index(const struct header *h, const char *field);
void header_read(struct header *h, struct logger_logfile *log);
size_t header_get_record_size_from_mask(const struct header *h,
                                        const size_t mask);
void header_change_offset_direction(struct header *h,
                                    enum logger_offset_direction new_value);

/**
 * @brief Check if the offset are forward.
 * @param h The #header.
 */
__attribute__((always_inline)) INLINE static int header_is_forward(
    const struct header *h) {
  return h->offset_direction == logger_offset_forward;
}

/**
 * @brief Check if the offset are backward.
 * @param h The #header.
 */
__attribute__((always_inline)) INLINE static int header_is_backward(
    const struct header *h) {
  return h->offset_direction == logger_offset_backward;
}

/**
 * @brief Check if the offset are corrupted.
 * @param h The #header.
 */
__attribute__((always_inline)) INLINE static int header_is_corrupted(
    const struct header *h) {
  return h->offset_direction == logger_offset_corrupted;
}

/**
 * @brief Set the first derivative of a field.
 *
 * @param h The #header.
 * @param field The index of the field.
 * @param field_first The index of the first derivative of the field.
 */
__attribute__((always_inline)) INLINE static void header_set_first_derivative(
    struct header *h, const int field, const int field_first) {
  if (h->masks[field].reader.first_deriv != -1 &&
      h->masks[field].reader.first_deriv != field_first) {
    error(
        "The first derivative is not consistent with the one from a different "
        "module.");
  }
  h->masks[field].reader.first_deriv = field_first;
}

/**
 * @brief Set the second derivative of a field.
 *
 * @param h The #header.
 * @param field The index of the field.
 * @param field_second The index of the second derivative of the field.
 */
__attribute__((always_inline)) INLINE static void header_set_second_derivative(
    struct header *h, const int field, const int field_second) {
  if (h->masks[field].reader.second_deriv != -1 &&
      h->masks[field].reader.second_deriv != field_second) {
    error(
        "The second derivative is not consistent with the one from a different "
        "module.");
  }
  h->masks[field].reader.second_deriv = field_second;
}

#endif  // LOGGER_LOGGER_HEADER_H
