/*
 * Copyright (c) 2020 Red Hat, Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * 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 General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA. 
 *
 * $Id: //eng/vdo-releases/aluminum/src/c++/vdo/base/header.c#5 $
 */

#include "header.h"

#include "logger.h"
#include "permassert.h"
#include "statusCodes.h"

/**********************************************************************/
int validateVersion(VersionNumber  expectedVersion,
                    VersionNumber  actualVersion,
                    const char    *componentName)
{
  if (!areSameVersion(expectedVersion, actualVersion)) {
    return logErrorWithStringError(VDO_UNSUPPORTED_VERSION,
                                   "%s version mismatch,"
                                   " expected %d.%d, got %d.%d",
                                   componentName,
                                   expectedVersion.majorVersion,
                                   expectedVersion.minorVersion,
                                   actualVersion.majorVersion,
                                   actualVersion.minorVersion);
  }
  return VDO_SUCCESS;
}

/**********************************************************************/
int validateHeader(const Header *expectedHeader,
                   const Header *actualHeader,
                   bool          exactSize,
                   const char   *componentName)
{
  if (expectedHeader->id != actualHeader->id) {
    return logErrorWithStringError(VDO_INCORRECT_COMPONENT,
                                   "%s ID mismatch, expected %d, got %d",
                                   componentName,
                                   expectedHeader->id,
                                   actualHeader->id);
  }

  int result = validateVersion(expectedHeader->version,
                               actualHeader->version,
                               componentName);
  if (result != VDO_SUCCESS) {
    return result;
  }

  if ((expectedHeader->size > actualHeader->size)
      || (exactSize && (expectedHeader->size < actualHeader->size))) {
    return logErrorWithStringError(VDO_UNSUPPORTED_VERSION,
                                   "%s size mismatch, expected %zu, got %zu",
                                   componentName,
                                   expectedHeader->size,
                                   actualHeader->size);
  }

  return VDO_SUCCESS;
}

/**********************************************************************/
int encodeHeader(const Header *header, Buffer *buffer)
{
  if (!ensureAvailableSpace(buffer, ENCODED_HEADER_SIZE)) {
    return UDS_BUFFER_ERROR;
  }

  int result = putUInt32LEIntoBuffer(buffer, header->id);
  if (result != UDS_SUCCESS) {
    return result;
  }

  result = encodeVersionNumber(header->version, buffer);
  if (result != UDS_SUCCESS) {
    return result;
  }

  return putUInt64LEIntoBuffer(buffer, header->size);
}

/**********************************************************************/
int encodeVersionNumber(VersionNumber version, Buffer *buffer)
{
  PackedVersionNumber packed = packVersionNumber(version);
  return putBytes(buffer, sizeof(packed), &packed);
}

/**********************************************************************/
int decodeHeader(Buffer *buffer, Header *header)
{
  ComponentID id;
  int result = getUInt32LEFromBuffer(buffer, &id);
  if (result != UDS_SUCCESS) {
    return result;
  }

  VersionNumber version;
  result = decodeVersionNumber(buffer, &version);
  if (result != UDS_SUCCESS) {
    return result;
  }

  uint64_t size;
  result = getUInt64LEFromBuffer(buffer, &size);
  if (result != UDS_SUCCESS) {
    return result;
  }

  *header = (Header) {
    .id      = id,
    .version = version,
    .size    = size,
  };
  return UDS_SUCCESS;
}

/**********************************************************************/
int decodeVersionNumber(Buffer *buffer, VersionNumber *version)
{
  PackedVersionNumber packed;
  int result = getBytesFromBuffer(buffer, sizeof(packed), &packed);
  if (result != UDS_SUCCESS) {
    return result;
  }

  *version = unpackVersionNumber(packed);
  return UDS_SUCCESS;
}
