/* 'checkit_tiff' is a conformance checker for baseline TIFFs
 *
 * author: Andreas Romeyke, 2015-2022
 * licensed under conditions of libtiff
 * (see http://libtiff.maptools.org/misc.html)
 *
 */

#include <stdarg.h>
#include "check.h"
#include "check_helper.h"
#include "ctstring.h"
#include <sys/stat.h>
#include <assert.h>
#include <stdio.h>
#ifdef __WIN32__
#define bswap_16 _byteswap_ushort
#define bswap_32 _byteswap_ulong
#else
#include <byteswap.h>
#endif
/*
#define DEBUG
*/

void free_value_if_set(ret_t * pointer) {
    if (
        (!pointer->value_found.is_const)
        && (pointer->value_found.len > 0)
        && (pointer->value_found.string != NULL)
        ) {
        clean_str(&pointer->value_found);
    }
}

void free_expected_if_set(ret_t * pointer) {
    if (
        (!pointer->expected_value.is_const)
        && (pointer->expected_value.len > 0)
        && (pointer->expected_value.string != NULL)
        ) {
        clean_str(&pointer->expected_value);
    }
}


string_t float2str(float v) {
  char array[VALUESTRLEN]={0};
  snprintf(array, VALUESTRLEN-1, "%f", v);
  return str(array);
}

string_t uint2str(unsigned int v) {
    char array[VALUESTRLEN]={0};
    snprintf(array, VALUESTRLEN-1, "%u", v);
    return str(array);
}

string_t int2str(int v) {
  char array[VALUESTRLEN]={0};
  snprintf(array, VALUESTRLEN-1, "%i", v);
  return str(array);
}

string_t frac2str(int d, int n) {
  char array[VALUESTRLEN]={0};
  snprintf(array, VALUESTRLEN-1, "%i/%i", d, n);
  return str(array);
}

uint16 TIFFSwabShort(uint16 a) {
    return bswap_16( a );
}

uint32 TIFFSwabLong(uint32 a) {
    return  bswap_32( a );
}

long long fsize(int fd) {
  struct stat st;
  fstat(fd, &st);
  return st.st_size;
}

ret_t set_value_found_ret (ret_t * rp, const string_t msg) {
  assert( NULL != rp);
  free_value_if_set(rp);
  rp->value_found=msg;
  return *rp;
}

ret_t set_value_found_ret_with_returncode(ret_t *rp, const string_t msg, returncode_t rc) {
    assert(NULL != rp);
    ret_t r = set_value_found_ret(rp, msg);
    r.returncode = rc;
    return r;
}

ret_t set_value_found_ret_u32_value (ret_t *rp, uint32 value) {
    assert(NULL != rp);
    ret_t ret = set_value_found_ret(rp, uint2str(value));
    return ret;
}

ret_t set_value_found_ret_u16_value (ret_t *rp, uint16 value) {
    assert(NULL != rp);
    ret_t ret = set_value_found_ret(rp, uint2str(value));
    return ret;

}

ret_t set_value_found_ret_float_value (ret_t *rp, float value) {
    assert(NULL != rp);
    ret_t ret = set_value_found_ret(rp, float2str(value));
    return ret;
}



ret_t set_value_found_ret_formatted (ret_t * rp, const char * fmt, ...) {
    assert(NULL != rp);
    char msg[VALUESTRLEN]={0};
    va_list argptr;
    va_start(argptr, fmt);
    vsnprintf(msg, VALUESTRLEN-1, fmt, argptr);
    va_end(argptr);
    ret_t ret = set_value_found_ret(rp, str(msg));
    clean_ret(*rp);
    return ret;
}

ret_t set_value_found_ret_formatted_with_returncode(ret_t * rp, returncode_t rc, const char * fmt, ...) {
    assert(NULL != rp);
    char msg[VALUESTRLEN]={0};
    va_list argptr;
    va_start(argptr, fmt);
    vsnprintf(msg, VALUESTRLEN-1, fmt, argptr);
    va_end(argptr);
    ret_t ret = set_value_found_ret(rp, str(msg));
    clean_ret(*rp);
    ret.returncode = rc;
    return ret;

}

ret_t set_expected_value_formatted (ret_t * rp, const char * fmt, ...) {
    char msg[VALUESTRLEN]={0};
    va_list argptr;
    va_start(argptr, fmt);
    vsnprintf(msg, VALUESTRLEN, fmt, argptr);
    va_end(argptr);
    free_expected_if_set(rp);
    rp->expected_value = str(msg);
    return *rp;
}

uint32 get_streamlen_at_offset(const ifd_entry_t *ifd_entry) {
    uint32 streamlen;
    switch (ifd_entry->datatype) {
        case TIFF_ASCII:
        case TIFF_BYTE:
        case TIFF_SBYTE:
        case TIFF_UNDEFINED:
            streamlen = ifd_entry->count;
            break;
        case TIFF_SHORT:
        case TIFF_SSHORT:
            streamlen = ifd_entry->count * 2;
            break;
        case TIFF_LONG:
        case TIFF_SLONG:
        case TIFF_IFD:
        case TIFF_FLOAT:
            streamlen = ifd_entry->count * 4;
            break;
        case TIFF_LONG8:
        case TIFF_DOUBLE:
        case TIFF_SLONG8:
        case TIFF_RATIONAL:
        case TIFF_SRATIONAL:
            streamlen = ifd_entry->count * 8;
            break;
        default:
            streamlen = ifd_entry->count;
    }
    return streamlen;
}

bool tag_is_an_IFD(const ifd_entry_t *ifd_entry) {
    return (ifd_entry->datatype == TIFF_IFD) || (ifd_entry->datatype == TIFF_IFD8);
}

void clean_ret(ret_t ret) {
    clean_str(&ret.expected_value);
    clean_str(&ret.value_found);
}



/* vim: set tabstop=2 softtabstop=2 shiftwidth=2 smarttab expandtab :*/
