/*
 * (C) Copyright 2005- ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 *
 * In applying this licence, ECMWF does not waive the privileges and immunities granted to it by
 * virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction.
 */

#include "Values.h"

eccodes::AccessorBuilder<eccodes::accessor::Values> _grib_accessor_values_builder{};

namespace eccodes::accessor
{

long Values::init_length()
{
    int ret = 0;
    long seclen        = 0;
    long offsetsection = 0;
    long offsetdata    = 0;

    if ((ret = grib_get_long_internal(get_enclosing_handle(), seclen_, &seclen)))
        return ret;

    if (seclen == 0) {
        /* printf("init_length seclen=0\n"); */
        return 0;
    }

    if ((ret = grib_get_long_internal(get_enclosing_handle(), offsetsection_, &offsetsection)))
        return ret;

    if ((ret = grib_get_long_internal(get_enclosing_handle(), offsetdata_, &offsetdata)))
        return ret;

    /* When reparsing */
    if (offsetdata < offsetsection) {
        /* printf("init_length offsetdata < offsetsection=0\n"); */
        ECCODES_ASSERT(this->get_enclosing_handle()->loader);
        return 0;
    }

    return seclen - (offsetdata - offsetsection);
}

void Values::init(const long v, grib_arguments* params)
{
    Gen::init(v, params);
    carg_ = 0;

    seclen_        = params->get_name(get_enclosing_handle(), carg_++);
    offsetdata_    = params->get_name(get_enclosing_handle(), carg_++);
    offsetsection_ = params->get_name(get_enclosing_handle(), carg_++);
    values_dirty_  = 1;

    length_ = init_length();
    /* ECCODES_ASSERT(length_ >=0); */
}

long Values::get_native_type()
{
    return GRIB_TYPE_DOUBLE;
}

void Values::dump(eccodes::Dumper* dumper)
{
    dumper->dump_values(this);
}

long Values::byte_count()
{
    grib_context_log(context_, GRIB_LOG_DEBUG, "byte_count of %s = %ld", name_, length_);
    return length_;
}

long Values::byte_offset()
{
    return offset_;
}

long Values::next_offset()
{
    return offset_ + length_;
}

void Values::update_size(size_t s)
{
    grib_context_log(context_, GRIB_LOG_DEBUG, "updating size of %s old %ld new %ld", name_, length_, s);
    length_ = s;
    ECCODES_ASSERT(length_ >= 0);
}

int Values::compare(grib_accessor* b)
{
    int retval   = 0;
    double* aval = 0;
    double* bval = 0;

    size_t alen = 0;
    size_t blen = 0;
    int err     = 0;
    long count  = 0;

    err = value_count(&count);
    if (err)
        return err;
    alen = count;

    err = b->value_count(&count);
    if (err)
        return err;
    blen = count;

    if (alen != blen)
        return GRIB_COUNT_MISMATCH;

    aval = (double*)grib_context_malloc(context_, alen * sizeof(double));
    bval = (double*)grib_context_malloc(b->context_, blen * sizeof(double));

    unpack_double(aval, &alen);
    b->unpack_double(bval, &blen);
    retval = GRIB_SUCCESS;
    for (size_t i = 0; i < alen && retval == GRIB_SUCCESS; ++i) {
        if (aval[i] != bval[i]) retval = GRIB_DOUBLE_VALUE_MISMATCH;
    }

    grib_context_free(context_, aval);
    grib_context_free(b->context_, bval);

    return retval;
}

int Values::pack_long(const long* val, size_t* len)
{
    double* dval = (double*)grib_context_malloc(context_, *len * sizeof(double));

    for (size_t i = 0; i < *len; i++)
        dval[i] = (double)val[i];

    int ret = pack_double(dval, len);
    grib_context_free(context_, dval);

    values_dirty_ = 1;

    return ret;
}

}  // namespace eccodes::accessor
