/*
 Copyright (C) 2018-2024 Fredrik Öhrström (gpl-3.0-or-later)

 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 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 General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include"dvparser.h"
#include"wmbus.h"
#include"util.h"

#include<assert.h>
#include<cmath>
#include<math.h>
#include<memory.h>
#include<limits>

// The parser should not crash on invalid data, but yeah, when I
// need to debug it because it crashes on invalid data, then
// I enable the following define...
//#define DEBUG_PARSER(...) fprintf(stdout, __VA_ARGS__)
#define DEBUG_PARSER(...)

using namespace std;

union RealConversion
{
    uint32_t i;
    float f;
};

const char *toString(VIFRange v)
{
    switch (v) {
        case VIFRange::None: return "None";
        case VIFRange::Any: return "Any";
#define X(name,from,to,quantity,unit) case VIFRange::name: return #name;
LIST_OF_VIF_RANGES
#undef X
    }
    assert(0);
}

VIFRange toVIFRange(const char *s)
{
    if (!strcmp(s, "None")) return VIFRange::None;
    if (!strcmp(s, "Any")) return VIFRange::Any;
#define X(name,from,to,quantity,unit) if (!strcmp(s, #name)) return VIFRange::name;
LIST_OF_VIF_RANGES
#undef X

    return VIFRange::None;
}

VIFCombinable toVIFCombinable(const char *s)
{
    if (!strcmp(s, "None")) return VIFCombinable::None;
    if (!strcmp(s, "Any")) return VIFCombinable::Any;
#define X(name,from,to) if (!strcmp(s, #name)) return VIFCombinable::name;
LIST_OF_VIF_COMBINABLES
#undef X

    return VIFCombinable::None;
}

const char *toString(VIFCombinable v)
{
    switch (v) {
        case VIFCombinable::None: return "None";
        case VIFCombinable::Any: return "Any";
#define X(name,from,to) case VIFCombinable::name: return #name;
LIST_OF_VIF_COMBINABLES
#undef X
    }
    assert(0);
}

VIFCombinable toVIFCombinable(int i)
{
#define X(name,from,to) if (from <= i && i <= to) return VIFCombinable::name;
LIST_OF_VIF_COMBINABLES
#undef X
    return VIFCombinable::None;
}

Unit toDefaultUnit(Vif v)
{
#define X(name,from,to,quantity,unit) { if (from <= v.intValue() && v.intValue() <= to) return unit; }
LIST_OF_VIF_RANGES
#undef X
    return Unit::Unknown;
}

Unit toDefaultUnit(VIFRange v)
{
    switch (v) {
    case VIFRange::Any:
    case VIFRange::None:
        assert(0);
        break;
#define X(name,from,to,quantity,unit) case VIFRange::name: return unit;
LIST_OF_VIF_RANGES
#undef X
    }
    assert(0);
}

VIFRange toVIFRange(int i)
{
#define X(name,from,to,quantity,unit) if (from <= i && i <= to) return VIFRange::name;
LIST_OF_VIF_RANGES
#undef X
    return VIFRange::None;
}

bool isInsideVIFRange(Vif vif, VIFRange vif_range)
{
    if (vif_range == VIFRange::AnyVolumeVIF)
    {
        // There are more volume units in the standard that will be added here.
        return isInsideVIFRange(vif, VIFRange::Volume);
    }
    if (vif_range == VIFRange::AnyEnergyVIF)
    {
        return
            isInsideVIFRange(vif, VIFRange::EnergyWh) ||
            isInsideVIFRange(vif, VIFRange::EnergyMJ) ||
            isInsideVIFRange(vif, VIFRange::EnergyMWh) ||
            isInsideVIFRange(vif, VIFRange::EnergyGJ);
    }
    if (vif_range == VIFRange::AnyPowerVIF)
    {
        // There are more power units in the standard that will be added here.
        return isInsideVIFRange(vif, VIFRange::PowerW) ||
               isInsideVIFRange(vif, VIFRange::PowerJh);
    }

#define X(name,from,to,quantity,unit) if (VIFRange::name == vif_range) { return from <= vif.intValue() && vif.intValue() <= to; }
LIST_OF_VIF_RANGES
#undef X
    return false;
}

map<uint16_t,string> hash_to_format_;

bool loadFormatBytesFromSignature(uint16_t format_signature, vector<uchar> *format_bytes)
{
    if (hash_to_format_.count(format_signature) > 0) {
        debug("(dvparser) found remembered format for hash %x\n", format_signature);
        // Return the proper hash!
        hex2bin(hash_to_format_[format_signature], format_bytes);
        return true;
    }
    // Unknown format signature.
    return false;
}

bool parseDV(Telegram *t,
             vector<uchar> &databytes,
             vector<uchar>::iterator data,
             size_t data_len,
             map<string,pair<int,DVEntry>> *dv_entries,
             vector<uchar>::iterator *format,
             size_t format_len,
             uint16_t *format_hash)
{
    map<string,int> dv_count;
    vector<uchar> format_bytes;
    vector<uchar> id_bytes;
    vector<uchar> data_bytes;
    string dv, key;
    size_t start_parse_here = t->parsed.size();
    vector<uchar>::iterator data_start = data;
    vector<uchar>::iterator data_end = data+data_len;
    vector<uchar>::iterator format_end;
    bool data_has_difvifs = true;
    bool variable_length = false;
    int force_mfct_index = t->force_mfct_index;

    if (format == NULL) {
        // No format string was supplied, we therefore assume
        // that the difvifs necessary to parse the data is
        // part of the data! This is the default.
        format = &data;
        format_end = data_end;
    } else {
        // A format string has been supplied. The data is compressed,
        // and can only be decoded using the supplied difvifs.
        // Since the data does not have the difvifs.
        data_has_difvifs = false;
        format_end = *format+format_len;
        string s = bin2hex(*format, format_end, format_len);
        debug("(dvparser) using format \"%s\"\n", s.c_str());
    }

    dv_entries->clear();

    // Data format is:

    // DIF byte (defines how the binary data bits should be decoded and howy man data bytes there are)
    // Sometimes followed by one or more dife bytes, if the 0x80 high bit is set.
    // The last dife byte does not have the 0x80 bit set.

    // VIF byte (defines what the decoded value means, water,energy,power,etc.)
    // Sometimes followed by one or more vife bytes, if the 0x80 high bit is set.
    // The last vife byte does not have the 0x80 bit set.

    // Data bytes, the number of data bytes are defined by the dif format.
    // Or if the dif says variable length, then the first data byte specifies the number of data bytes.

    // DIF again...

    // A Dif(Difes)Vif(Vifes) identifier can be for example be the 02FF20 for the Multical21
    // vendor specific status bits. The parser then uses this identifier as a key to store the
    // data bytes in a map. The same identifier could occur several times in a telegram,
    // even though it often don't. Since the first occurence is stored under 02FF20,
    // the second identical identifier stores its data under the key "02FF20_2" etc for 3 and forth...
    // A proper meter would use storagenr etc to differentiate between different measurements of
    // the same value.

    format_bytes.clear();
    id_bytes.clear();
    for (;;)
    {
        id_bytes.clear();
        DEBUG_PARSER("(dvparser debug) Remaining format data %ju\n", std::distance(*format,format_end));
        if (*format == format_end) break;

        if (force_mfct_index != -1)
        {
            // This is an old meter without a proper 0f or other hear start manufacturer data marker.
            int index = std::distance(data_start, data);

            if (index >= force_mfct_index)
            {
                DEBUG_PARSER("(dvparser) manufacturer specific data, parsing is done.\n", dif);
                size_t datalen = std::distance(data, data_end);
                string value = bin2hex(data, data_end, datalen);
                t->addExplanationAndIncrementPos(data, datalen, KindOfData::CONTENT, Understanding::NONE, "manufacturer specific data %s", value.c_str());
                break;
            }
        }

        uchar dif = **format;

        MeasurementType mt = difMeasurementType(dif);
        int datalen = difLenBytes(dif);
        DEBUG_PARSER("(dvparser debug) dif=%02x datalen=%d \"%s\" type=%s\n", dif, datalen, difType(dif).c_str(),
                     measurementTypeName(mt).c_str());

        if (datalen == -2)
        {
            if (dif == 0x0f)
            {
                DEBUG_PARSER("(dvparser) reached dif %02x manufacturer specific data, parsing is done.\n", dif);
                datalen = std::distance(data,data_end);
                string value = bin2hex(data+1, data_end, datalen-1);
                t->mfct_0f_index = 1+std::distance(data_start, data);
                assert(t->mfct_0f_index >= 0);
                t->addExplanationAndIncrementPos(data, datalen, KindOfData::CONTENT, Understanding::NONE, "%02X manufacturer specific data %s", dif, value.c_str());
                break;
            }
            if (dif == 0x1f)
            {
                DEBUG_PARSER("(dvparser) reached dif %02x more records in next telegram.\n", dif);
                datalen = std::distance(data,data_end);
                string value = bin2hex(data+1, data_end, datalen-1);
                t->mfct_0f_index = 1+std::distance(data_start, data);
                t->mfct_1f_index = 1+std::distance(data_start, data);
                assert(t->mfct_0f_index >= 0);
                t->addExplanationAndIncrementPos(data, datalen, KindOfData::CONTENT, Understanding::FULL, "%02X more data in next telegram %s", dif, value.c_str());
                break;
            }
            DEBUG_PARSER("(dvparser) reached unknown dif %02x treating remaining data as manufacturer specific, parsing is done.\n", dif);
            datalen = std::distance(data,data_end);
            string value = bin2hex(data+1, data_end, datalen-1);
            t->mfct_0f_index = 1+std::distance(data_start, data);
            assert(t->mfct_0f_index >= 0);
            t->addExplanationAndIncrementPos(data, datalen, KindOfData::CONTENT, Understanding::NONE, "%02X unknown dif treating remaining data as mfct specific %s", dif, value.c_str());
            break;
        }
        if (dif == 0x2f) {
            t->addExplanationAndIncrementPos(*format, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02X skip", dif);
            DEBUG_PARSER("\n");
            continue;
        }
        if (datalen == -1) {
            variable_length = true;
        } else {
            variable_length = false;
        }
        if (data_has_difvifs) {
            format_bytes.push_back(dif);
            id_bytes.push_back(dif);
            t->addExplanationAndIncrementPos(*format, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02X dif (%s)", dif, difType(dif).c_str());
        } else {
            id_bytes.push_back(**format);
            (*format)++;
        }


        int difenr = 0;
        int subunit = 0;
        int tariff = 0;
        int lsb_of_storage_nr = (dif & 0x40) >> 6;
        int storage_nr = lsb_of_storage_nr;

        bool has_another_dife = (dif & 0x80) == 0x80;
        int num_dife = 0;

        while (has_another_dife)
        {
            num_dife++;
            if (num_dife > 10) { debug("(dvparser) warning: too many dife found!\n"); break; }
            if (*format == format_end) { debug("(dvparser) warning: unexpected end of data (dife expected)\n"); break; }

            uchar dife = **format;
            int subunit_bit = (dife & 0x40) >> 6;
            subunit |= subunit_bit << difenr;
            int tariff_bits = (dife & 0x30) >> 4;
            tariff |= tariff_bits << (difenr*2);
            int storage_nr_bits = (dife & 0x0f);
            storage_nr |= storage_nr_bits << (1+difenr*4);

            DEBUG_PARSER("(dvparser debug) dife=%02x (subunit=%d tariff=%d storagenr=%d)\n", dife, subunit, tariff, storage_nr);

            if (data_has_difvifs)
            {
                format_bytes.push_back(dife);
                id_bytes.push_back(dife);
                t->addExplanationAndIncrementPos(*format, 1, KindOfData::PROTOCOL, Understanding::FULL,
                                                 "%02X dife (subunit=%d tariff=%d storagenr=%d)",
                                                 dife, subunit, tariff, storage_nr);
            }
            else
            {
                id_bytes.push_back(**format);
                (*format)++;
            }

            has_another_dife = (dife & 0x80) == 0x80;
            difenr++;
        }

        if (*format == format_end) { debug("(dvparser) warning: unexpected end of data (vif expected)\n"); break; }

        uchar vif = **format;
        int full_vif = vif & 0x7f;
        bool extension_vif = false;
        int combinable_full_vif = 0;
        bool combinable_extension_vif = false;
        set<VIFCombinable> found_combinable_vifs;
        set<uint16_t> found_combinable_vifs_raw;

        DEBUG_PARSER("(dvparser debug) vif=%04x \"%s\"\n", vif, vifType(vif).c_str());

        if (data_has_difvifs)
        {
            format_bytes.push_back(vif);
            id_bytes.push_back(vif);
            t->addExplanationAndIncrementPos(*format, 1, KindOfData::PROTOCOL, Understanding::FULL,
                                             "%02X vif (%s)", vif, vifType(vif).c_str());
        } else
        {
            id_bytes.push_back(**format);
            (*format)++;
        }

        // Check if this is marker for one of the extended sets of vifs: first, second and third or manufacturer.
        if (vif == 0xfb || vif == 0xfd || vif == 0xef || vif == 0xff)
        {
            // Extension vifs.
            full_vif <<= 8;
            extension_vif = true;
        }

        // Grabbing a variable length vif. This does not currently work
        // with the compact format. Should the high bit be set here?
        // One meter (qsmoke) does not set the high bit, since the next byte is not strictly a vife, it is a length byte.
        // Another meter does set the hight bit....
        // We accept both hear, I do not yet know what the standard says.
        if (vif == 0x7c || vif == 0xfc)
        {
            DEBUG_PARSER("(dvparser debug) variable length vif found\n");
            if (*format == format_end) { debug("(dvparser) warning: unexpected end of data (vif varlen expected)\n"); break; }
            uchar viflen = **format;
            id_bytes.push_back(viflen);
            t->addExplanationAndIncrementPos(*format, 1, KindOfData::PROTOCOL, Understanding::FULL,
                                             "%02X viflen (%d)", viflen, viflen);
            for (uchar i = 0; i < viflen; ++i)
            {
                if (*format == format_end) { debug("(dvparser) warning: unexpected end of data (vif varlen byte %d/%d expected)\n",
                                                   i+1, viflen); break; }
                uchar v = **format;
                t->addExplanationAndIncrementPos(*format, 1, KindOfData::PROTOCOL, Understanding::FULL,
                                                 "%02X vif (%c)", v, v);
                id_bytes.push_back(v);
            }
        }

        // Do we have another vife byte? We better have one, if extension_vif is true.
        bool has_another_vife = (vif & 0x80) == 0x80;
        int num_vife = 0;

        while (has_another_vife)
        {
            num_vife++;
            if (num_vife > 10) { debug("(dvparser) warning: too many vife found!\n"); break; }

            if (*format == format_end) { debug("(dvparser) warning: unexpected end of data (vife expected)\n"); break; }

            uchar vife = **format;
            DEBUG_PARSER("(dvparser debug) vife=%02x (%s)\n", vife, vifeType(dif, vif, vife).c_str());

            if (data_has_difvifs)
            {
                // Collect the difvifs to create signature for future use.
                format_bytes.push_back(vife);
                id_bytes.push_back(vife);
            }
            else
            {
                // Reuse the existing
                id_bytes.push_back(**format);
                (*format)++;
            }

            has_another_vife = (vife & 0x80) == 0x80;

            if (extension_vif)
            {
                // First vife after the extension marker is the real vif.
                full_vif |= (vife & 0x7f);
                extension_vif = false;
                if (data_has_difvifs)
                {
                    t->addExplanationAndIncrementPos(*format, 1, KindOfData::PROTOCOL, Understanding::FULL,
                                                     "%02X vife (%s)", vife, vifeType(dif, vif, vife).c_str());
                }
            }
            else
            {
                if (combinable_extension_vif)
                {
                    // First vife after the combinable extension marker is the real vif.
                    combinable_full_vif |= (vife & 0x7f);
                    combinable_extension_vif = false;
                    VIFCombinable vc = toVIFCombinable(combinable_full_vif);
                    found_combinable_vifs.insert(vc);
                    found_combinable_vifs_raw.insert(combinable_full_vif);

                    if (data_has_difvifs)
                    {
                        t->addExplanationAndIncrementPos(*format, 1, KindOfData::PROTOCOL, Understanding::FULL,
                                                         "%02X combinable extension vife (%s)", vife, toString(vc));
                    }
                }
                else
                {
                    combinable_full_vif = vife & 0x7f;
                    // Check if this is marker for one of the extended combinable vifs
                    if (combinable_full_vif == 0x7c || combinable_full_vif == 0x7f)
                    {
                        combinable_full_vif <<= 8;
                        combinable_extension_vif = true;
                        VIFCombinable vc = toVIFCombinable(vife & 0x7f);
                        if (data_has_difvifs)
                        {
                            t->addExplanationAndIncrementPos(*format, 1, KindOfData::PROTOCOL, Understanding::FULL,
                                                             "%02X combinable vif (%s)", vife, toString(vc));
                        }
                    }
                    else
                    {
                        VIFCombinable vc = toVIFCombinable(combinable_full_vif);
                        found_combinable_vifs.insert(vc);
                        found_combinable_vifs_raw.insert(combinable_full_vif);

                        if (data_has_difvifs)
                        {
                            t->addExplanationAndIncrementPos(*format, 1, KindOfData::PROTOCOL, Understanding::FULL,
                                                             "%02X combinable vif (%s)", vife, toString(vc));
                        }
                    }
                }
            }
        }

        dv = "";
        for (uchar c : id_bytes) {
            char hex[3];
            hex[2] = 0;
            snprintf(hex, 3, "%02X", c);
            dv.append(hex);
        }
        DEBUG_PARSER("(dvparser debug) key \"%s\"\n", dv.c_str());

        int count = ++dv_count[dv];
        if (count > 1) {
            strprintf(&key, "%s_%d", dv.c_str(), count);
        } else {
            strprintf(&key, "%s", dv.c_str());
        }
        DEBUG_PARSER("(dvparser debug) DifVif key is %s\n", key.c_str());

        int remaining = std::distance(data, data_end);
        if (remaining < 1)
        {
            debug("(dvparser) warning: unexpected end of data\n");
            break;
        }

        if (variable_length) {
            DEBUG_PARSER("(dvparser debug) varlen %02x\n", *(data+0));
            datalen = *(data);
            t->addExplanationAndIncrementPos(data, 1, KindOfData::PROTOCOL, Understanding::FULL, "%02X varlen=%d", *(data+0), datalen);
            remaining--; // Drop the length byte.
        }
        DEBUG_PARSER("(dvparser debug) remaining data %d len=%d\n", remaining, datalen);
        if (remaining < datalen)
        {
            debug("(dvparser) warning: unexpected end of data\n");
            datalen = remaining-1;
        }

        string value = bin2hex(data, data_end, datalen);
        int offset = start_parse_here+data-data_start;

        (*dv_entries)[key] = { offset, DVEntry(offset,
                                               key,
                                               mt,
                                               Vif(full_vif),
                                               found_combinable_vifs,
                                               found_combinable_vifs_raw,
                                               StorageNr(storage_nr),
                                               TariffNr(tariff),
                                               SubUnitNr(subunit),
                                               value) };

        DVEntry *dve = &(*dv_entries)[key].second;

        if (isTraceEnabled())
        {
            trace("[DVPARSER] entry %s\n", dve->str().c_str());
        }

        assert(key == dve->dif_vif_key.str());

        if (value.length() > 0) {
            // This call increments data with datalen.
            t->addExplanationAndIncrementPos(data, datalen, KindOfData::CONTENT, Understanding::NONE, "%s", value.c_str());
            DEBUG_PARSER("(dvparser debug) data \"%s\"\n\n", value.c_str());
        }
        if (remaining == datalen || data == databytes.end()) {
            // We are done here!
            break;
        }
    }

    string format_string = bin2hex(format_bytes);
    uint16_t hash = crc16_EN13757(safeButUnsafeVectorPtr(format_bytes), format_bytes.size());

    if (data_has_difvifs) {
        if (hash_to_format_.count(hash) == 0) {
            hash_to_format_[hash] = format_string;
            debug("(dvparser) found new format \"%s\" with hash %x, remembering!\n", format_string.c_str(), hash);
        }
    }

    return true;
}

bool hasKey(std::map<std::string,std::pair<int,DVEntry>> *dv_entries, std::string key)
{
    return dv_entries->count(key) > 0;
}

bool findKey(MeasurementType mit, VIFRange vif_range, StorageNr storagenr, TariffNr tariffnr,
             std::string *key, std::map<std::string,std::pair<int,DVEntry>> *dv_entries)
{
    return findKeyWithNr(mit, vif_range, storagenr, tariffnr, 1, key, dv_entries);
}

bool findKeyWithNr(MeasurementType mit, VIFRange vif_range, StorageNr storagenr, TariffNr tariffnr, int nr,
                   std::string *key, std::map<std::string,std::pair<int,DVEntry>> *dv_entries)
{
    /*debug("(dvparser) looking for type=%s vifrange=%s storagenr=%d tariffnr=%d\n",
      measurementTypeName(mit).c_str(), toString(vif_range), storagenr.intValue(), tariffnr.intValue());*/

    for (auto& v : *dv_entries)
    {
        MeasurementType ty = v.second.second.measurement_type;
        Vif vi = v.second.second.vif;
        StorageNr sn = v.second.second.storage_nr;
        TariffNr tn = v.second.second.tariff_nr;

        /* debug("(dvparser) match? %s type=%s vife=%x (%s) and storagenr=%d\n",
              v.first.c_str(),
              measurementTypeName(ty).c_str(), vi.intValue(), storagenr, sn);*/

        if (isInsideVIFRange(vi, vif_range) &&
            (mit == MeasurementType::Instantaneous || mit == ty) &&
            (storagenr == AnyStorageNr || storagenr == sn) &&
            (tariffnr == AnyTariffNr || tariffnr == tn))
        {
            *key = v.first;
            nr--;
            if (nr <= 0) return true;
            debug("(dvparser) found key %s for type=%s vif=%x storagenr=%d\n",
                  v.first.c_str(), measurementTypeName(ty).c_str(),
                  vi.intValue(), storagenr.intValue());
        }
    }
    return false;
}

void extractDV(DifVifKey &dvk, uchar *dif, int *vif, bool *has_difes, bool *has_vifes)
{
    string tmp = dvk.str();
    extractDV(tmp, dif, vif, has_difes, has_vifes);
}

void extractDV(string &s, uchar *dif, int *vif, bool *has_difes, bool *has_vifes)
{
    vector<uchar> bytes;
    hex2bin(s, &bytes);
    size_t i = 0;
    *has_difes = false;
    *has_vifes = false;
    if (bytes.size() == 0)
    {
        *dif = 0;
        *vif = 0;
        return;
    }

    *dif = bytes[i];
    while (i < bytes.size() && (bytes[i] & 0x80))
    {
        i++;
        *has_difes = true;
    }
    i++;

    if (i >= bytes.size())
    {
        *vif = 0;
        return;
    }

    *vif = bytes[i];
    if (*vif == 0xfb || // first extension
        *vif == 0xfd || // second extensio
        *vif == 0xef || // third extension
        *vif == 0xff)   // vendor extension
    {
        if (i+1 < bytes.size())
        {
            // Create an extended vif, like 0xfd31 for example.
            *vif = bytes[i] << 8 | bytes[i+1];
            i++;
        }
    }

    while (i < bytes.size() && (bytes[i] & 0x80))
    {
        i++;
        *has_vifes = true;
    }
}

bool extractDVuint8(map<string,pair<int,DVEntry>> *dv_entries,
                    string key,
                    int *offset,
                    uchar *value)
{
    if ((*dv_entries).count(key) == 0) {
        verbose("(dvparser) warning: cannot extract uint8 from non-existant key \"%s\"\n", key.c_str());
        *offset = -1;
        *value = 0;
        return false;
    }

    pair<int,DVEntry>&  p = (*dv_entries)[key];
    *offset = p.first;
    vector<uchar> v;
    hex2bin(p.second.value, &v);

    *value = v[0];
    return true;
}

bool extractDVuint16(map<string,pair<int,DVEntry>> *dv_entries,
                     string key,
                     int *offset,
                     uint16_t *value)
{
    if ((*dv_entries).count(key) == 0) {
        verbose("(dvparser) warning: cannot extract uint16 from non-existant key \"%s\"\n", key.c_str());
        *offset = -1;
        *value = 0;
        return false;
    }

    pair<int,DVEntry>&  p = (*dv_entries)[key];
    *offset = p.first;
    vector<uchar> v;
    hex2bin(p.second.value, &v);

    *value = v[1]<<8 | v[0];
    return true;
}

bool extractDVuint24(map<string,pair<int,DVEntry>> *dv_entries,
                     string key,
                     int *offset,
                     uint32_t *value)
{
    if ((*dv_entries).count(key) == 0) {
        verbose("(dvparser) warning: cannot extract uint24 from non-existant key \"%s\"\n", key.c_str());
        *offset = -1;
        *value = 0;
        return false;
    }

    pair<int,DVEntry>&  p = (*dv_entries)[key];
    *offset = p.first;
    vector<uchar> v;
    hex2bin(p.second.value, &v);

    *value = v[2] << 16 | v[1]<<8 | v[0];
    return true;
}

bool extractDVuint32(map<string,pair<int,DVEntry>> *dv_entries,
                     string key,
                     int *offset,
                     uint32_t *value)
{
    if ((*dv_entries).count(key) == 0) {
        verbose("(dvparser) warning: cannot extract uint32 from non-existant key \"%s\"\n", key.c_str());
        *offset = -1;
        *value = 0;
        return false;
    }

    pair<int,DVEntry>&  p = (*dv_entries)[key];
    *offset = p.first;
    vector<uchar> v;
    hex2bin(p.second.value, &v);

    *value = (uint32_t(v[3]) << 24) |  (uint32_t(v[2]) << 16) | (uint32_t(v[1])<<8) | uint32_t(v[0]);
    return true;
}

bool extractDVdouble(map<string,pair<int,DVEntry>> *dv_entries,
                     string key,
                     int *offset,
                     double *value,
                     bool auto_scale,
                     bool force_unsigned)
{
    if ((*dv_entries).count(key) == 0) {
        verbose("(dvparser) warning: cannot extract double from non-existant key \"%s\"\n", key.c_str());
        *offset = 0;
        *value = 0;
        return false;
    }
    pair<int,DVEntry>&  p = (*dv_entries)[key];
    *offset = p.first;

    if (p.second.value.length() == 0) {
        verbose("(dvparser) warning: key found but no data  \"%s\"\n", key.c_str());
        *offset = 0;
        *value = 0;
        return false;
    }

    return p.second.extractDouble(value, auto_scale, force_unsigned);
}

bool checkSizeHex(size_t expected_len, DifVifKey &dvk, string &v)
{
    if (v.length() == expected_len) return true;

    warning("(dvparser) bad decode since difvif %s expected %d hex chars but got \"%s\"\n",
            dvk.str().c_str(), expected_len, v.c_str());
    return false;
}

bool is_all_F(string &v)
{
    for (size_t i = 0; i < v.length(); ++i)
    {
        if (v[i] != 'F') return false;
    }
    return true;
}

bool DVEntry::extractDouble(double *out, bool auto_scale, bool force_unsigned)
{
    int t = dif_vif_key.dif() & 0xf;
    if (t == 0x0 ||
        t == 0x8 ||
        t == 0xd ||
        t == 0xf)
    {
        // Cannot extract from nothing, selection for readout, variable length or special.
        // Variable length is used for compact varlen history. Should be added in the future.
        return false;
    }
    else
    if (t == 0x1 || // 8 Bit Integer/Binary
        t == 0x2 || // 16 Bit Integer/Binary
        t == 0x3 || // 24 Bit Integer/Binary
        t == 0x4 || // 32 Bit Integer/Binary
        t == 0x6 || // 48 Bit Integer/Binary
        t == 0x7)   // 64 Bit Integer/Binary
    {
        vector<uchar> v;
        hex2bin(value, &v);
        uint64_t raw = 0;
        bool negate = false;
        uint64_t negate_mask = 0;
        if (t == 0x1) {
            if (!checkSizeHex(2, dif_vif_key, value)) return false;
            assert(v.size() == 1);
            raw = v[0];
            if (!force_unsigned && (raw & (uint64_t)0x80UL) != 0) { negate = true; negate_mask = ~((uint64_t)0)<<8; }
        } else if (t == 0x2) {
            if (!checkSizeHex(4, dif_vif_key, value)) return false;
            assert(v.size() == 2);
            raw = v[1]*256 + v[0];
            if (!force_unsigned && (raw & (uint64_t)0x8000UL) != 0) { negate = true; negate_mask = ~((uint64_t)0)<<16; }
        } else if (t == 0x3) {
            if (!checkSizeHex(6, dif_vif_key, value)) return false;
            assert(v.size() == 3);
            raw = v[2]*256*256 + v[1]*256 + v[0];
            if (!force_unsigned && (raw & (uint64_t)0x800000UL) != 0) { negate = true; negate_mask = ~((uint64_t)0)<<24; }
        } else if (t == 0x4) {
            if (!checkSizeHex(8, dif_vif_key, value)) return false;
            assert(v.size() == 4);
            raw = ((unsigned int)v[3])*256*256*256
                + ((unsigned int)v[2])*256*256
                + ((unsigned int)v[1])*256
                + ((unsigned int)v[0]);
            if (!force_unsigned && (raw & (uint64_t)0x80000000UL) != 0) { negate = true; negate_mask = ~((uint64_t)0)<<32; }
        } else if (t == 0x6) {
            if (!checkSizeHex(12, dif_vif_key, value)) return false;
            assert(v.size() == 6);
            raw = ((uint64_t)v[5])*256*256*256*256*256
                + ((uint64_t)v[4])*256*256*256*256
                + ((uint64_t)v[3])*256*256*256
                + ((uint64_t)v[2])*256*256
                + ((uint64_t)v[1])*256
                + ((uint64_t)v[0]);
            if (!force_unsigned && (raw & (uint64_t)0x800000000000UL) != 0) { negate = true; negate_mask = ~((uint64_t)0)<<48; }
        } else if (t == 0x7) {
            if (!checkSizeHex(16, dif_vif_key, value)) return false;
            assert(v.size() == 8);
            raw = ((uint64_t)v[7])*256*256*256*256*256*256*256
                + ((uint64_t)v[6])*256*256*256*256*256*256
                + ((uint64_t)v[5])*256*256*256*256*256
                + ((uint64_t)v[4])*256*256*256*256
                + ((uint64_t)v[3])*256*256*256
                + ((uint64_t)v[2])*256*256
                + ((uint64_t)v[1])*256
                + ((uint64_t)v[0]);
            if (!force_unsigned && (raw & (uint64_t)0x8000000000000000UL) != 0) { negate = true; negate_mask = 0; }
        }
        double scale = 1.0;
        double draw = (double)raw;
        if (negate)
        {
            draw = (double)((int64_t)(negate_mask | raw));
        }
        if (auto_scale) scale = vifScale(dif_vif_key.vif());
        *out = (draw) / scale;
    }
    else
    if (t == 0x9 || // 2 digit BCD
        t == 0xA || // 4 digit BCD
        t == 0xB || // 6 digit BCD
        t == 0xC || // 8 digit BCD
        t == 0xE)   // 12 digit BCD
    {
        // Negative BCD values are always visible in bcd. I.e. they are always signed.
        // Ignore assumption on signedness.
        // 74140000 -> 00001474
        string& v = value;
        uint64_t raw = 0;
        bool negate = false;

        if (is_all_F(v))
        {
            *out = std::nan("");
            return false;
        }
        if (t == 0x9) {
            if (!checkSizeHex(2, dif_vif_key, v)) return false;
            if (v[0] == 'F') { negate = true; v[0] = '0'; }
            raw = (v[0]-'0')*10 + (v[1]-'0');
        } else if (t ==  0xA) {
            if (!checkSizeHex(4, dif_vif_key, v)) return false;
            if (v[2] == 'F') { negate = true; v[2] = '0'; }
            raw = (v[2]-'0')*10*10*10 + (v[3]-'0')*10*10
                + (v[0]-'0')*10 + (v[1]-'0');
        } else if (t ==  0xB) {
            if (!checkSizeHex(6, dif_vif_key, v)) return false;
            if (v[4] == 'F') { negate = true; v[4] = '0'; }
            raw = (v[4]-'0')*10*10*10*10*10 + (v[5]-'0')*10*10*10*10
                + (v[2]-'0')*10*10*10 + (v[3]-'0')*10*10
                + (v[0]-'0')*10 + (v[1]-'0');
        } else if (t ==  0xC) {
            if (!checkSizeHex(8, dif_vif_key, v)) return false;
            if (v[6] == 'F') { negate = true; v[6] = '0'; }
            raw = (v[6]-'0')*10*10*10*10*10*10*10 + (v[7]-'0')*10*10*10*10*10*10
                + (v[4]-'0')*10*10*10*10*10 + (v[5]-'0')*10*10*10*10
                + (v[2]-'0')*10*10*10 + (v[3]-'0')*10*10
                + (v[0]-'0')*10 + (v[1]-'0');
        } else if (t ==  0xE) {
            if (!checkSizeHex(12, dif_vif_key, v)) return false;
            if (v[10] == 'F') { negate = true; v[10] = '0'; }
            raw =(v[10]-'0')*10*10*10*10*10*10*10*10*10*10*10 + (v[11]-'0')*10*10*10*10*10*10*10*10*10*10
                + (v[8]-'0')*10*10*10*10*10*10*10*10*10 + (v[9]-'0')*10*10*10*10*10*10*10*10
                + (v[6]-'0')*10*10*10*10*10*10*10 + (v[7]-'0')*10*10*10*10*10*10
                + (v[4]-'0')*10*10*10*10*10 + (v[5]-'0')*10*10*10*10
                + (v[2]-'0')*10*10*10 + (v[3]-'0')*10*10
                + (v[0]-'0')*10 + (v[1]-'0');
        }
        double scale = 1.0;
        double draw = (double)raw;
        if (negate)
        {
            draw = (double)draw * -1;
        }
        if (auto_scale) scale = vifScale(dif_vif_key.vif());
        *out = (draw) / scale;
    }
    else
    if (t == 0x5) // 32 Bit Real
    {
        vector<uchar> v;
        hex2bin(value, &v);
        if (!checkSizeHex(8, dif_vif_key, value)) return false;
        assert(v.size() == 4);
        RealConversion rc;
        rc.i = v[3]<<24 | v[2]<<16 | v[1]<<8 | v[0];

        // Assumes float uses the standard IEEE 754 bit set.
        // 1 bit sign,  8 bit exp, 23 bit mantissa
        // RealConversion is tested on an amd64 platform. How about
        // other platsforms with different byte ordering?
        double draw = rc.f;
        double scale = 1.0;
        if (auto_scale) scale = vifScale(dif_vif_key.vif());
        *out = (draw) / scale;
    }
    else
    {
        warning("(dvparser) Unsupported dif format for extraction to double! dif=%02x\n", dif_vif_key.dif());
        return false;
    }

    return true;
}

bool extractDVlong(map<string,pair<int,DVEntry>> *dv_entries,
                   string key,
                   int *offset,
                   uint64_t *out)
{
    if ((*dv_entries).count(key) == 0) {
        verbose("(dvparser) warning: cannot extract long from non-existant key \"%s\"\n", key.c_str());
        *offset = 0;
        *out = 0;
        return false;
    }

    pair<int,DVEntry>&  p = (*dv_entries)[key];
    *offset = p.first;

    if (p.second.value.length() == 0) {
        verbose("(dvparser) warning: key found but no data  \"%s\"\n", key.c_str());
        *offset = 0;
        *out = 0;
        return false;
    }

    return p.second.extractLong(out);
}

bool DVEntry::extractLong(uint64_t *out)
{
    int t = dif_vif_key.dif() & 0xf;
    if (t == 0x1 || // 8 Bit Integer/Binary
        t == 0x2 || // 16 Bit Integer/Binary
        t == 0x3 || // 24 Bit Integer/Binary
        t == 0x4 || // 32 Bit Integer/Binary
        t == 0x6 || // 48 Bit Integer/Binary
        t == 0x7)   // 64 Bit Integer/Binary
    {
        vector<uchar> v;
        hex2bin(value, &v);
        uint64_t raw = 0;
        if (t == 0x1) {
            if (!checkSizeHex(2, dif_vif_key, value)) return false;
            assert(v.size() == 1);
            raw = v[0];
        } else if (t == 0x2) {
            if (!checkSizeHex(4, dif_vif_key, value)) return false;
            assert(v.size() == 2);
            raw = v[1]*256 + v[0];
        } else if (t == 0x3) {
            if (!checkSizeHex(6, dif_vif_key, value)) return false;
            assert(v.size() == 3);
            raw = v[2]*256*256 + v[1]*256 + v[0];
        } else if (t == 0x4) {
            if (!checkSizeHex(8, dif_vif_key, value)) return false;
            assert(v.size() == 4);
            raw = ((unsigned int)v[3])*256*256*256
                + ((unsigned int)v[2])*256*256
                + ((unsigned int)v[1])*256
                + ((unsigned int)v[0]);
        } else if (t == 0x6) {
            if (!checkSizeHex(12, dif_vif_key, value)) return false;
            assert(v.size() == 6);
            raw = ((uint64_t)v[5])*256*256*256*256*256
                + ((uint64_t)v[4])*256*256*256*256
                + ((uint64_t)v[3])*256*256*256
                + ((uint64_t)v[2])*256*256
                + ((uint64_t)v[1])*256
                + ((uint64_t)v[0]);
        } else if (t == 0x7) {
            if (!checkSizeHex(16, dif_vif_key, value)) return false;
            assert(v.size() == 8);
            raw = ((uint64_t)v[7])*256*256*256*256*256*256*256
                + ((uint64_t)v[6])*256*256*256*256*256*256
                + ((uint64_t)v[5])*256*256*256*256*256
                + ((uint64_t)v[4])*256*256*256*256
                + ((uint64_t)v[3])*256*256*256
                + ((uint64_t)v[2])*256*256
                + ((uint64_t)v[1])*256
                + ((uint64_t)v[0]);
        }
        *out = raw;
    }
    else
    if (t == 0x9 || // 2 digit BCD
        t == 0xA || // 4 digit BCD
        t == 0xB || // 6 digit BCD
        t == 0xC || // 8 digit BCD
        t == 0xE)   // 12 digit BCD
    {
        // 74140000 -> 00001474
        string& v = value;
        if (is_all_F(v))
        {
            return false;
        }
        uint64_t raw = 0;
        bool negate = false;
        if (t == 0x9) {
            if (!checkSizeHex(2, dif_vif_key, value)) return false;
            if (v[0] == 'F') { negate = true; v[0] = '0'; }
            assert(v.size() == 2);
            raw = (v[0]-'0')*10 + (v[1]-'0');
        } else if (t ==  0xA) {
            if (!checkSizeHex(4, dif_vif_key, value)) return false;
            if (v[2] == 'F') { negate = true; v[2] = '0'; }
            assert(v.size() == 4);
            raw = (v[2]-'0')*10*10*10 + (v[3]-'0')*10*10
                + (v[0]-'0')*10 + (v[1]-'0');
        } else if (t ==  0xB) {
            if (!checkSizeHex(6, dif_vif_key, value)) return false;
            if (v[4] == 'F') { negate = true; v[4] = '0'; }
            assert(v.size() == 6);
            raw = (v[4]-'0')*10*10*10*10*10 + (v[5]-'0')*10*10*10*10
                + (v[2]-'0')*10*10*10 + (v[3]-'0')*10*10
                + (v[0]-'0')*10 + (v[1]-'0');
        } else if (t ==  0xC) {
            if (!checkSizeHex(8, dif_vif_key, value)) return false;
            if (v[6] == 'F') { negate = true; v[6] = '0'; }
            assert(v.size() == 8);
            raw = (v[6]-'0')*10*10*10*10*10*10*10 + (v[7]-'0')*10*10*10*10*10*10
                + (v[4]-'0')*10*10*10*10*10 + (v[5]-'0')*10*10*10*10
                + (v[2]-'0')*10*10*10 + (v[3]-'0')*10*10
                + (v[0]-'0')*10 + (v[1]-'0');
        } else if (t ==  0xE) {
            if (!checkSizeHex(12, dif_vif_key, value)) return false;
            if (v[10] == 'F') { negate = true; v[10] = '0'; }
            assert(v.size() == 12);
            raw =(v[10]-'0')*10*10*10*10*10*10*10*10*10*10*10 + (v[11]-'0')*10*10*10*10*10*10*10*10*10*10
                + (v[8]-'0')*10*10*10*10*10*10*10*10*10 + (v[9]-'0')*10*10*10*10*10*10*10*10
                + (v[6]-'0')*10*10*10*10*10*10*10 + (v[7]-'0')*10*10*10*10*10*10
                + (v[4]-'0')*10*10*10*10*10 + (v[5]-'0')*10*10*10*10
                + (v[2]-'0')*10*10*10 + (v[3]-'0')*10*10
                + (v[0]-'0')*10 + (v[1]-'0');
        }

        if (negate)
        {
            raw = (uint64_t)(((int64_t)raw)*-1);
        }

        *out = raw;
    }
    else
    {
        error("Unsupported dif format for extraction to long! dif=%02x\n", dif_vif_key.dif());
    }

    return true;
}

bool extractDVHexString(map<string,pair<int,DVEntry>> *dv_entries,
                        string key,
                        int *offset,
                        string *value)
{
    if ((*dv_entries).count(key) == 0) {
        verbose("(dvparser) warning: cannot extract string from non-existant key \"%s\"\n", key.c_str());
        *offset = -1;
        return false;
    }
    pair<int,DVEntry>&  p = (*dv_entries)[key];
    *offset = p.first;
    *value = p.second.value;

    return true;
}


bool extractDVReadableString(map<string,pair<int,DVEntry>> *dv_entries,
                             string key,
                             int *offset,
                             string *out)
{
    if ((*dv_entries).count(key) == 0) {
        verbose("(dvparser) warning: cannot extract string from non-existant key \"%s\"\n", key.c_str());
        *offset = -1;
        return false;
    }
    pair<int,DVEntry>&  p = (*dv_entries)[key];
    *offset = p.first;

    return p.second.extractReadableString(out);
}

bool DVEntry::extractReadableString(string *out)
{
    int t = dif_vif_key.dif() & 0xf;

    string v = value;

    if (t == 0x1 || // 8 Bit Integer/Binary
        t == 0x2 || // 16 Bit Integer/Binary
        t == 0x3 || // 24 Bit Integer/Binary
        t == 0x4 || // 32 Bit Integer/Binary
        t == 0x6 || // 48 Bit Integer/Binary
        t == 0x7 || // 64 Bit Integer/Binary
        t == 0xD)   // Variable length
    {
        if (isLikelyAscii(v))
        {
            // For example an enhanced id 32 bits binary looks like:
            // 44434241 and will be reversed to: 41424344 and translated using ascii
            // to ABCD
            v = reverseBinaryAsciiSafeToString(v);
        }
        else
        {
            v = reverseBCD(v);
        }
    }
    if (t == 0x9 || // 2 digit BCD
        t == 0xA || // 4 digit BCD
        t == 0xB || // 6 digit BCD
        t == 0xC || // 8 digit BCD
        t == 0xE)   // 12 digit BCD
    {
        // For example an enhanced id 12 digit bcd looks like:
        // 618171183100 and will be reversed to: 003118718161
        v = reverseBCD(v);
    }

    *out = v;
    return true;
}

double DVEntry::getCounter(DVEntryCounterType ct)
{
    switch (ct)
    {
    case DVEntryCounterType::STORAGE_COUNTER: return storage_nr.intValue();
    case DVEntryCounterType::TARIFF_COUNTER: return tariff_nr.intValue();
    case DVEntryCounterType::SUBUNIT_COUNTER: return subunit_nr.intValue();
    case DVEntryCounterType::UNKNOWN: break;
    }

    return std::numeric_limits<double>::quiet_NaN();
}

string DVEntry::str()
{
    string s =
        tostrprintf("%d: %s %s vif=%x %s%s st=%d ta=%d su=%d",
                    offset,
                    dif_vif_key.str().c_str(),
                    toString(measurement_type),
                    vif.intValue(),
                    combinable_vifs.size() > 0 ? "HASCOMB ":"",
                    combinable_vifs_raw.size() > 0 ? "HASCOMBRAW ":"",
                    storage_nr.intValue(),
                    tariff_nr.intValue(),
                    subunit_nr.intValue()
            );

    return s;
}

bool extractDate(uchar hi, uchar lo, struct tm *date)
{
    // |     hi    |    lo     |
    // | YYYY MMMM | YYY DDDDD |

    int day   = (0x1f) & lo;
    int year1 = ((0xe0) & lo) >> 5;
    int month = (0x0f) & hi;
    int year2 = ((0xf0) & hi) >> 1;
    int year  = (2000 + year1 + year2);

    date->tm_mday = day;         /* Day of the month (1-31) */
    date->tm_mon = month - 1;    /* Month (0-11) */
    date->tm_year = year - 1900; /* Year - 1900 */

    if (month > 12) return false;
    return true;
}

bool extractTime(uchar hi, uchar lo, struct tm *date)
{
    // |    hi    |    lo    |
    // | ...hhhhh | ..mmmmmm |
    int min  = (0x3f) & lo;
    int hour = (0x1f) & hi;

    date->tm_min = min;
    date->tm_hour = hour;

    if (min > 59) return false;
    if (hour > 23) return false;
    return true;
}

bool extractDVdate(map<string,pair<int,DVEntry>> *dv_entries,
                   string key,
                   int *offset,
                   struct tm *out)
{
    if ((*dv_entries).count(key) == 0)
    {
        verbose("(dvparser) warning: cannot extract date from non-existant key \"%s\"\n", key.c_str());
        *offset = -1;
        memset(out, 0, sizeof(struct tm));
        return false;
    }
    pair<int,DVEntry>&  p = (*dv_entries)[key];
    *offset = p.first;

    return p.second.extractDate(out);
}

bool DVEntry::extractDate(struct tm *out)
{
    memset(out, 0, sizeof(*out));
    out->tm_isdst = -1; // Figure out the dst automatically!

    vector<uchar> v;
    hex2bin(value, &v);

    bool ok = true;
    if (v.size() == 2) {
        ok &= ::extractDate(v[1], v[0], out);
    }
    else if (v.size() == 4) {
        ok &= ::extractDate(v[3], v[2], out);
        ok &= ::extractTime(v[1], v[0], out);
    }
    else if (v.size() == 6) {
        ok &= ::extractDate(v[4], v[3], out);
        ok &= ::extractTime(v[2], v[1], out);
        // ..ss ssss
        int sec  = (0x3f) & v[0];
        out->tm_sec = sec;
        // There are also bits for day of week, week of year.
        // A bit for if daylight saving is in use or not and its offset.
        // A bit if it is a leap year.
        // I am unsure how to deal with this here..... TODO
    }

    return ok;
}

bool FieldMatcher::matches(DVEntry &dv_entry)
{
    if (!active) return false;

    // Test an explicit dif vif key.
    if (match_dif_vif_key)
    {
        bool b = dv_entry.dif_vif_key == dif_vif_key;
        return b;
    }

    // Test ranges and types.
    bool range = (!match_vif_range || isInsideVIFRange(dv_entry.vif, vif_range));
    bool raw = (!match_vif_raw || dv_entry.vif == vif_raw);
    bool type = (!match_measurement_type || dv_entry.measurement_type == measurement_type);
    bool storage = (!match_storage_nr || (dv_entry.storage_nr >= storage_nr_from && dv_entry.storage_nr <= storage_nr_to));
    bool tariff = (!match_tariff_nr || (dv_entry.tariff_nr >= tariff_nr_from && dv_entry.tariff_nr <= tariff_nr_to));
    bool subunit = (!match_subunit_nr || (dv_entry.subunit_nr >= subunit_nr_from && dv_entry.subunit_nr <= subunit_nr_to));

    //printf("Match? range=%d raw=%d type=%d storage=%d tariff=%d subunit=%d \n", range, raw, type, storage, tariff, subunit);

    bool b = range & raw & type & storage & tariff & subunit;

    if (!b) return false;

    // So far so good, now test the combinables.

    // If field matcher has no combinables, then do NOT match any dventry with a combinable!
    if (vif_combinables.size()== 0 && vif_combinables_raw.size() == 0)
    {
        // If there is a combinable vif, then there is a raw combinable vif. So comparing both not strictly necessary.
        if (dv_entry.combinable_vifs.size() == 0 && dv_entry.combinable_vifs_raw.size() == 0) return true;
        // Oups, field matcher does not expect any combinables, but the dv_entry has combinables.
        // This means no match for us since combinables must be handled explicitly.
        return false;
    }

    // Lets check that the dv_entry vif combinables raw contains the field matcher requested combinable raws.
    // The raws are used for meters using reserved and manufacturer specific vif combinables.
    for (uint16_t vcr : vif_combinables_raw)
    {
        if (dv_entry.combinable_vifs_raw.count(vcr) == 0)
        {
            // Ouch, one of the requested vif combinables raw did not exist in the dv_entry. No match!
            return false;
        }
    }

    // Lets check that the dv_entry combinables contains the field matcher requested combinables.
    // The named vif combinables are used by well behaved meters.
    for (VIFCombinable vc : vif_combinables)
    {
        if (vc != VIFCombinable::Any && dv_entry.combinable_vifs.count(vc) == 0)
        {
            // Ouch, one of the requested combinables did not exist in the dv_entry. No match!
            return false;
        }
    }

    // Now if we have not selected the Any combinable match pattern,
    // then we need to check if there are unmatched combinables in the telegram, if so fail the match.
    if (vif_combinables.count(VIFCombinable::Any) == 0)
    {
        if (vif_combinables.size() > 0)
        {
            for (VIFCombinable vc : dv_entry.combinable_vifs)
            {
                if (vif_combinables.count(vc) == 0)
                {
                    // Oups, the telegram entry had a vif combinable that we had no matcher for.
                    return false;
                }
            }
        }
        else
        {
            for (uint16_t vcr : dv_entry.combinable_vifs_raw)
            {
                if (vif_combinables_raw.count(vcr) == 0)
                {
                    // Oups, the telegram entry had a vif combinable raw that we had no matcher for.
                    return false;
                }
            }
        }
    }

    // Yay, they were all found.
    return true;
}

const char *toString(MeasurementType mt)
{
    switch (mt)
    {
    case MeasurementType::Any: return "Any";
    case MeasurementType::Instantaneous: return "Instantaneous";
    case MeasurementType::Minimum: return "Minimum";
    case MeasurementType::Maximum: return "Maximum";
    case MeasurementType::AtError: return "AtError";
    case MeasurementType::Unknown: return "Unknown";
    }
    return "?";
}

MeasurementType toMeasurementType(const char *s)
{
    if (!strcmp(s, "Any")) return MeasurementType::Any;
    if (!strcmp(s, "Instantaneous")) return MeasurementType::Instantaneous;
    if (!strcmp(s, "Minimum")) return MeasurementType::Minimum;
    if (!strcmp(s, "Maximum")) return MeasurementType::Maximum;
    if (!strcmp(s, "AtError")) return MeasurementType::AtError;
    if (!strcmp(s, "Unknown")) return MeasurementType::Unknown;

    return MeasurementType::Unknown;
}

string FieldMatcher::str()
{
    string s = "";

    if (match_dif_vif_key)
    {
        s = s+"DVK("+dif_vif_key.str()+") ";
    }

    if (match_measurement_type)
    {
        s = s+"MT("+toString(measurement_type)+") ";
    }

    if (match_vif_range)
    {
        s = s+"VR("+toString(vif_range)+") ";
    }

    if (match_vif_raw)
    {
        s = s+"VRR("+to_string(vif_raw)+") ";
    }

    if (vif_combinables.size() > 0)
    {
        s += "Comb(";

        for (auto vc : vif_combinables)
        {
            s = s+toString(vc)+" ";
        }

        s.pop_back();
        s += ")";
    }

    if (match_storage_nr)
    {
        s = s+"S("+to_string(storage_nr_from.intValue())+"-"+to_string(storage_nr_to.intValue())+") ";
    }

    if (match_tariff_nr)
    {
        s = s+"T("+to_string(tariff_nr_from.intValue())+"-"+to_string(tariff_nr_to.intValue())+") ";
    }

    if (match_subunit_nr)
    {
        s += "U("+to_string(subunit_nr_from.intValue())+"-"+to_string(subunit_nr_to.intValue())+") ";
    }

    if (index_nr.intValue() != 1)
    {
        s += "I("+to_string(index_nr.intValue())+")";
    }

    if (s.size() > 0)
    {
        s.pop_back();
    }

    return s;
}

DVEntryCounterType toDVEntryCounterType(const std::string &s)
{
    if (s == "storage_counter") return DVEntryCounterType::STORAGE_COUNTER;
    if (s == "tariff_counter") return DVEntryCounterType::TARIFF_COUNTER;
    if (s == "subunit_counter") return DVEntryCounterType::SUBUNIT_COUNTER;
    return DVEntryCounterType::UNKNOWN;
}

const char *toString(DVEntryCounterType ct)
{
    switch (ct)
    {
    case DVEntryCounterType::UNKNOWN: return "unknown";
    case DVEntryCounterType::STORAGE_COUNTER: return "storage_counter";
    case DVEntryCounterType::TARIFF_COUNTER: return "tariff_counter";
    case DVEntryCounterType::SUBUNIT_COUNTER: return "subunit_counter";
    }

    return "unknown";
}

string available_vif_ranges_;

const string &availableVIFRanges()
{
    if (available_vif_ranges_ != "") return available_vif_ranges_;

#define X(n,from,to,q,u) available_vif_ranges_ += string(#n) + "\n";
LIST_OF_VIF_RANGES
#undef X

    // Remove last newline
    available_vif_ranges_.pop_back();
    return available_vif_ranges_;
}

string available_vif_combinables_;

const string &availableVIFCombinables()
{
    if (available_vif_combinables_ != "") return available_vif_combinables_;

#define X(n,from,to) available_vif_combinables_ += string(#n) + "\n";
LIST_OF_VIF_COMBINABLES
#undef X

    // Remove last newline
    available_vif_combinables_.pop_back();
    return available_vif_combinables_;
}
