#include "ltedsapi.h"
#include <math.h>
#include <string.h>
#include "ltrapi.h"
#ifndef NAN
    #define NAN (0./0.)
#endif


#define LTEDS_DATABYTE_IS_CHECKSUM(ctx, pos)  ((((ctx)->Flags & LTEDS_DATA_FLAG_HAS_CHECKSUM)) \
    && (((pos) % LTEDS_CHECKSUM_BLOCK_LEN) == 0))

#define IS_LEAP_YEAR(year)   ((((year) % 4) == 0) && ((((year) % 400) == 0) || (((year) % 100) != 0)))

#define ALL_BITS_VAL(bits)  (DWORD)((1UL << (bits)) - 1)
#define IS_ALL_SET(data, bits) ((data) == ALL_BITS_VAL(bits))


/* Текстовые описания кодов ошибок. */
static const TLTR_ERROR_STRING_DEF f_err_tbl[] = {
    {LTEDS_OK,                       "Выполнено без ошибок" },
    {LTEDS_ERROR_INSUF_SIZE,         "Недостаточно места в данных TEDS для выполнения операции"},
    {LTEDS_ERROR_CHECKSUM,           "Неверное значение контрольной суммы в данных TEDS"},
    {LTEDS_ERROR_INVALID_BITSIZE,    "Неверно задан битовый размер данных TEDS"},
    {LTEDS_ERROR_UNSUPPORTED_FORMAT, "Не поддерживается указанный формат данных TEDS"},
    {LTEDS_ERROR_ENCODE_VALUE,       "Неверно указано значение для кодирования в TEDS"},
    {LTEDS_ERROR_UNKNOWN_SEL_CASE,   "Неизвестный вариант выбора ветвления данных TEDS"}
};
static const LPCSTR f_unknown_error_string = "Неизвестная ошибка";



static const CHAR f_chr5_symbols[] = {
    ' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
    'X', 'Y', 'Z', ',', '.', '/', '_', '@'
};

static INT f_get_chr5_symbol(TLTEDS_DECODE_CONTEXT *ctx, CHAR *val) {
    INT err;
    BYTE code;
    err = LTEDS_GetBits(ctx, 5, &code);
    if ((err == LTEDS_OK) && (val != NULL)) {
        *val = f_chr5_symbols[code];
    }
    return  err;
}

static INT f_put_chr5_symbol(TLTEDS_ENCODE_CONTEXT *ctx, CHAR val) {
    INT err = LTEDS_OK;
    BYTE code, cur_code;
    BOOL fnd_code = FALSE;
    for (cur_code = 0; (cur_code < sizeof(f_chr5_symbols)/sizeof(f_chr5_symbols[0]))
         && !fnd_code; cur_code++) {
        fnd_code = f_chr5_symbols[cur_code] == val;
        if (fnd_code) {
            code = cur_code;
        }
    }
    if (!fnd_code) {
        err = LTEDS_ERROR_ENCODE_VALUE;
    } else {
        err = LTEDS_PutBits(ctx, 5, &code);
    }
    return err;
}


LTEDSAPI_DllExport(INT) LTEDS_DecodeInit(TLTEDS_DECODE_CONTEXT *ctx, const BYTE *data, DWORD dataLen, DWORD flags) {
    ctx->Data = data;
    ctx->DataLen = dataLen;
    ctx->Flags = flags;
    ctx->ProcBitPos = 0;
    memset(ctx->Reserved, 0, sizeof(ctx->Reserved));
    return LTEDS_OK;
}

LTEDSAPI_DllExport(INT) LTEDS_EncodeInit(TLTEDS_ENCODE_CONTEXT *ctx, BYTE *data, DWORD dataLen, DWORD flags) {
    ctx->Data = data;
    ctx->DataLen = dataLen;
    ctx->Flags = flags;
    ctx->ProcBitPos = 0;
    memset(ctx->Reserved, 0, sizeof(ctx->Reserved));
    return LTEDS_OK;
}

LTEDSAPI_DllExport(INT) LTEDS_GetBasicInfo(TLTEDS_DECODE_CONTEXT *ctx, TLTEDS_INFO_BASIC *info) {
    DWORD val;
    INT err = LTEDS_OK;
    err = LTEDS_GetManufacturerID(ctx, info ? &info->ManufacturerID : NULL);
    if (err == LTEDS_OK) {
        err = LTEDS_GetUnInt(ctx, 15, &val);
        if ((err == LTEDS_OK) && (info != NULL))
            info->ModelNumber = val & 0x7FFF;
    }
    if (err == LTEDS_OK)
        err = f_get_chr5_symbol(ctx, info ? &info->VersionLetter : NULL);
    if (err == LTEDS_OK) {
        err = LTEDS_GetUnInt(ctx, 6, &val);
        if ((err == LTEDS_OK) && (info != NULL))
            info->VersionNumber = val & 0x3F;
    }

    if (err == LTEDS_OK) {
        err = LTEDS_GetUnInt(ctx, 24, &val);
        if ((err == LTEDS_OK) && (info != NULL))
            info->SerialNumber = val;
    }
    return err;
}

LTEDSAPI_DllExport(INT) LTEDS_PutBasicInfo(TLTEDS_ENCODE_CONTEXT *ctx, const TLTEDS_INFO_BASIC *info) {
    INT err = LTEDS_OK;
    err = LTEDS_PutManufacturerID(ctx, info->ManufacturerID);
    if (err == LTEDS_OK)
        err = LTEDS_PutUnInt(ctx, 15, info->ModelNumber);
    if (err == LTEDS_OK)
        err = f_put_chr5_symbol(ctx, info->VersionLetter);
    if (err == LTEDS_OK)
        err = LTEDS_PutUnInt(ctx, 6, info->VersionNumber);
    if (err == LTEDS_OK)
        err = LTEDS_PutUnInt(ctx, 24, info->SerialNumber);
    return err;
}

LTEDSAPI_DllExport(INT) LTEDS_GetSelector(TLTEDS_DECODE_CONTEXT *ctx, BYTE *selector) {
    DWORD val;
    INT err = LTEDS_GetUnInt(ctx, 2, &val);
    if ((err == LTEDS_OK) && (selector != NULL))
        *selector = val & 3;
    return err;
}

LTEDSAPI_DllExport(INT) LTEDS_PutSelector(TLTEDS_ENCODE_CONTEXT *ctx, BYTE selector) {
    return LTEDS_PutUnInt(ctx, 2, selector);
}

LTEDSAPI_DllExport(INT) LTEDS_GetEndExtendedSelector(TLTEDS_DECODE_CONTEXT *ctx, BYTE *extsel) {
    DWORD val;
    INT err = LTEDS_GetUnInt(ctx, 1, &val);
    if ((err == LTEDS_OK) && (extsel != NULL))
        *extsel = val & 1;
    return err;
}

LTEDSAPI_DllExport(INT) LTEDS_PutEndExtendedSelector(TLTEDS_ENCODE_CONTEXT *ctx, BYTE extsel) {
    return LTEDS_PutUnInt(ctx, 1, extsel);
}

LTEDSAPI_DllExport(INT) LTEDS_GetStdTemplateID(TLTEDS_DECODE_CONTEXT *ctx, BYTE *id) {
    DWORD val;
    INT err = LTEDS_GetUnInt(ctx, 8, &val);
    if ((err == LTEDS_OK) && (id != NULL))
        *id = val & 0xFF;
    return err;
}

LTEDSAPI_DllExport(INT) LTEDS_PutStdTemplateID(TLTEDS_ENCODE_CONTEXT *ctx, BYTE id) {
    return LTEDS_PutUnInt(ctx, 8, id);
}

LTEDSAPI_DllExport(INT) LTEDS_GetManufacturerTemplateID(TLTEDS_DECODE_CONTEXT *ctx,
                                                        DWORD bitsize, DWORD *id) {
    DWORD val;
    INT err = LTEDS_GetUnInt(ctx, bitsize, &val);
    if ((err == LTEDS_OK) && (id != NULL))
        *id = val & ((1UL << bitsize) - 1);
    return err;
}

LTEDSAPI_DllExport(INT) LTEDS_PutManufacturerTemplateID(TLTEDS_ENCODE_CONTEXT *ctx,
                                                        DWORD bitsize, DWORD id) {
    return LTEDS_PutUnInt(ctx, bitsize, id);
}

LTEDSAPI_DllExport(INT) LTEDS_GetManufacturerID(TLTEDS_DECODE_CONTEXT *ctx, WORD *id) {
    DWORD val;
    INT err =  LTEDS_GetUnInt(ctx, 14, &val);
    if ((err == LTEDS_OK)  && (id != NULL)) {
        *id = val & 0x3FFF;
    }
    return err;
}

LTEDSAPI_DllExport(INT) LTEDS_PutManufacturerID(TLTEDS_ENCODE_CONTEXT *ctx, WORD id) {
    return  LTEDS_PutUnInt(ctx, 14, id);
}



LTEDSAPI_DllExport(INT) LTEDS_CheckBlockSum(TLTEDS_DECODE_CONTEXT *ctx, DWORD start_bytepos) {
    INT err = LTEDS_OK;
    if (start_bytepos >= ctx->DataLen) {
        err = LTEDS_ERROR_INSUF_SIZE;
    } else {
        BYTE sum = 0;
        DWORD pos;

        for (pos = start_bytepos; (pos <  (start_bytepos + LTEDS_CHECKSUM_BLOCK_LEN))
             && (pos < ctx->DataLen); pos++) {
            sum += ctx->Data[pos];
        }

        if (sum != 0) {
            err = LTEDS_ERROR_CHECKSUM;
        }
    }
    return err;
}

LTEDSAPI_DllExport(INT) LTEDS_GetBits(TLTEDS_DECODE_CONTEXT *ctx, DWORD bitsize, void *result) {
    DWORD cur_bitpos = ctx->ProcBitPos;
    DWORD put_bytepos = 0;
    INT err = (cur_bitpos +  bitsize + 7)/8 > ctx->DataLen ? LTEDS_ERROR_INSUF_SIZE : LTEDS_OK;
    if (err == LTEDS_OK) {
        DWORD cur_bytepos = cur_bitpos/8;

        DWORD rem_bitsize = bitsize;
        BYTE  bit_offset =  cur_bitpos & 0x7;
        BYTE lower_byte_bits = 8 - bit_offset;
        BYTE *res_bytes = (BYTE *)result;

        while ((rem_bitsize > 0) && (err == LTEDS_OK)) {
            if (cur_bytepos >=  ctx->DataLen) {
                err = LTEDS_ERROR_INSUF_SIZE;
            } else if (LTEDS_DATABYTE_IS_CHECKSUM(ctx, cur_bytepos)) {
                err = LTEDS_CheckBlockSum(ctx, cur_bytepos);
                cur_bitpos += 8;
            } else {
                BYTE cur_byte;
                BYTE proc_bits = rem_bitsize < 8 ? (BYTE)rem_bitsize : 8;

                cur_byte = ((DWORD)ctx->Data[cur_bytepos] & 0xFF) >> bit_offset;
                if ((bit_offset > 0) && (rem_bitsize > lower_byte_bits)) {
                    DWORD next_bytepos = cur_bytepos + 1;
                    if (LTEDS_DATABYTE_IS_CHECKSUM(ctx, next_bytepos)) {
                        next_bytepos++;
                    }
                    if (next_bytepos >= ctx->DataLen) {
                        err = LTEDS_ERROR_INSUF_SIZE;
                    } else {
                        cur_byte |= ctx->Data[next_bytepos] << lower_byte_bits;
                    }
                }

                if (proc_bits < 8) {
                    cur_byte &=  (1 << proc_bits) - 1;
                }

                if (res_bytes != NULL)
                    res_bytes[put_bytepos++] = cur_byte;

                rem_bitsize-=proc_bits;
                cur_bitpos += proc_bits;
            }
            cur_bytepos++;
        }

        if (err == LTEDS_OK)
            ctx->ProcBitPos = cur_bitpos;
    }
    return err;
}


LTEDSAPI_DllExport(INT) LTEDS_PutBits(TLTEDS_ENCODE_CONTEXT *ctx, DWORD bitsize, const void *val) {
    DWORD cur_bitpos = ctx->ProcBitPos;
    INT err = (cur_bitpos +  bitsize + 7)/8 > ctx->DataLen ? LTEDS_ERROR_INSUF_SIZE : LTEDS_OK;
    if (err == LTEDS_OK) {
        DWORD cur_bytepos = cur_bitpos/8;
        DWORD rem_bitsize = bitsize;
        BYTE  bit_offset =  cur_bitpos & 0x7;
        BYTE lower_byte_bits = 8 - bit_offset;
        const BYTE *val_bytes = (const BYTE *)val;

        while ((rem_bitsize > 0) && (err == LTEDS_OK)) {
            if (cur_bytepos >=  ctx->DataLen) {
                err = LTEDS_ERROR_INSUF_SIZE;
            } else if (LTEDS_DATABYTE_IS_CHECKSUM(ctx, cur_bytepos)) {
                /** @todo put for previous block */
                cur_bitpos += 8;
            } else {
                BYTE cur_byte = *val_bytes++;
                BYTE proc_bits = rem_bitsize < 8 ? (BYTE)rem_bitsize : 8;

                ctx->Data[cur_bytepos] = (ctx->Data[cur_bytepos] & ((1 << bit_offset)-1))
                                          | ((cur_byte  << bit_offset) & 0xFF);

                if ((bit_offset > 0) && (rem_bitsize > lower_byte_bits)) {
                    DWORD next_bytepos = cur_bytepos + 1;
                    if (LTEDS_DATABYTE_IS_CHECKSUM(ctx, next_bytepos)) {
                        next_bytepos++;
                    }
                    if (next_bytepos >= ctx->DataLen) {
                        err = LTEDS_ERROR_INSUF_SIZE;
                    } else {
                        ctx->Data[next_bytepos] = cur_byte >> lower_byte_bits;
                        cur_byte |= ctx->Data[next_bytepos] << lower_byte_bits;
                    }
                }

                if (proc_bits < 8) {
                    cur_byte &=  (1 << proc_bits) - 1;
                }

                rem_bitsize-=proc_bits;
                cur_bitpos += proc_bits;
            }
            cur_bytepos++;
        }

        if (err == LTEDS_OK)
            ctx->ProcBitPos = cur_bitpos;
    }
    return err;
}

LTEDSAPI_DllExport(INT) LTEDS_GetSingle(TLTEDS_DECODE_CONTEXT *ctx, double *val) {
    INT err;
    float fval;
    err = LTEDS_GetBits(ctx, 32, &fval);
    if ((err == LTEDS_OK) && (val != NULL)) {
        *val = (double)fval;
    }
    return err;
}

LTEDSAPI_DllExport(INT) LTEDS_PutSingle(TLTEDS_ENCODE_CONTEXT *ctx, double val) {
    float fval = (float)val;
    return LTEDS_PutBits(ctx, 32, &fval);
}


LTEDSAPI_DllExport(INT) LTEDS_GetConRes(TLTEDS_DECODE_CONTEXT *ctx, DWORD bitsize,
                                     double start_value, double tolerance, double *val) {
    INT err = (bitsize == 0) || (bitsize > 32) ? LTEDS_ERROR_INVALID_BITSIZE : LTEDS_OK;
    if (err == LTEDS_OK) {
        DWORD num = 0;
        err = LTEDS_GetBits(ctx, bitsize, &num);
        if ((err == LTEDS_OK) && (val != NULL)) {
            /* все 1 соответствует NAN */
            if (IS_ALL_SET(num, bitsize)) {
                *val = (double)NAN;
            } else {
                *val = start_value + tolerance * num;
            }
        }
    }
    return err;
}

LTEDSAPI_DllExport(INT) LTEDS_PutConRes(TLTEDS_ENCODE_CONTEXT *ctx, DWORD bitsize,
                                        double start_value, double tolerance, double val) {
    INT err = (bitsize == 0) || (bitsize > 32) ? LTEDS_ERROR_INVALID_BITSIZE : LTEDS_OK;
    if (err == LTEDS_OK) {

        DWORD num = 0;
        if (isnan(val)) {
            num = ALL_BITS_VAL(bitsize);
        } else {
            double end_val = start_value + tolerance * (ALL_BITS_VAL(bitsize) - 1);
            if (((val < start_value) && (val < end_val)) ||
                    ((val > start_value) && (val > end_val))) {
                err = LTEDS_ERROR_ENCODE_VALUE;
            } else {
                num = (DWORD)((val - start_value)/tolerance + 0.5);
            }
        }

        if (!err)
            err = LTEDS_PutBits(ctx, bitsize, &num);
    }
    return err;
}




LTEDSAPI_DllExport(INT) LTEDS_GetConRelRes(TLTEDS_DECODE_CONTEXT *ctx, DWORD bitsize,
                                        double start_value, double step, double *val) {
    INT err = (bitsize == 0) || (bitsize > 32) ? LTEDS_ERROR_INVALID_BITSIZE : LTEDS_OK;
    if (err == LTEDS_OK) {
        DWORD num = 0;
        err = LTEDS_GetBits(ctx, bitsize, &num);
        if (err == LTEDS_OK) {
            /* все 1 соответствует NAN */
            if (IS_ALL_SET(num, bitsize)) {
                *val = (double)NAN;
            } else {
                *val = start_value * pow(1. + 2*step, num);
            }
        }
    }
    return err;
}


LTEDSAPI_DllExport(INT) LTEDS_PutConRelRes(TLTEDS_ENCODE_CONTEXT *ctx, DWORD bitsize,
                                            double start_value, double tolerance, double val) {
    INT err = (bitsize == 0) || (bitsize > 32) ? LTEDS_ERROR_INVALID_BITSIZE : LTEDS_OK;
    if (err == LTEDS_OK) {
        DWORD num = 0;
        if (isnan(val)) {
            num = ALL_BITS_VAL(bitsize);
        } else {
            double end_val = start_value * pow(1. + 2*tolerance, ALL_BITS_VAL(bitsize) - 1);
            if (((val < start_value) && (val < end_val)) ||
                    ((val > start_value) && (val > end_val))) {
                err = LTEDS_ERROR_ENCODE_VALUE;
            } else {
                num = (DWORD)(log10(val/start_value)/log10(1. + 2*tolerance) + 0.5);
            }
        }

        if (err == LTEDS_OK)
            err = LTEDS_PutBits(ctx, bitsize, &num);
    }
    return err;
}





static const BYTE f_months_days[]      = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
static const BYTE f_months_days_leap[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};


LTEDSAPI_DllExport(INT) LTEDS_GetDate(TLTEDS_DECODE_CONTEXT *ctx, DWORD bitsize,  TLTEDS_INFO_DATE *date) {
    INT err = LTEDS_OK;
    DWORD val;
    TLTEDS_INFO_DATE cur_date;

    err = LTEDS_GetUnInt(ctx, bitsize, &val);
    if (err == LTEDS_OK) {
        if (IS_ALL_SET(val, bitsize)) {
            cur_date.Year = 0;
            cur_date.Day = 0;
            cur_date.Month = 0;
        } else {
            cur_date.Year = LTEDS_DATE_MIN_YEAR;
            cur_date.Day = 1;
            cur_date.Month = 1;
            DWORD rem_days = val;
            while (rem_days > 0) {
                BOOL leap_year = IS_LEAP_YEAR(cur_date.Year);
                WORD year_days = leap_year ? 366 : 365;
                if (year_days <= rem_days) {
                    cur_date.Year++;
                    rem_days -= year_days;
                } else {
                    BYTE month;
                    const BYTE *months_days = leap_year ? f_months_days_leap : f_months_days;
                    for (month = 0; (month < sizeof(f_months_days)/sizeof(f_months_days[0])) && (rem_days > 0); month++) {
                        BYTE month_days = months_days[month];
                        if (month_days <= rem_days) {
                            cur_date.Month++;
                            rem_days -= month_days;
                        } else {
                            cur_date.Day += (BYTE)rem_days;
                            rem_days = 0;
                        }
                    }
                }
            }
        }
    }
    if ((err == LTEDS_OK) && (date != NULL)) {
        *date = cur_date;
    }
    return err;
}


LTEDSAPI_DllExport(INT) LTEDS_PutDate(TLTEDS_ENCODE_CONTEXT *ctx, DWORD bitsize,  const TLTEDS_INFO_DATE *date) {
    INT err = ((date->Year == 0) && (date->Month == 0) && (date->Day == 0)) ||
            LTEDS_DateIsValid(date) ? LTEDS_OK : LTEDS_ERROR_ENCODE_VALUE;
    if (err == LTEDS_OK) {
        DWORD days = 0;
        if (date->Year == 0) {
            days = ALL_BITS_VAL(bitsize);
        } else {
            BYTE month_idx;
            DWORD year_idx;
            BOOL leap_year = IS_LEAP_YEAR(date->Year);
            days = date->Day - 1;
            const BYTE *months_days = leap_year ? f_months_days_leap : f_months_days;

            for (month_idx = 0; (month_idx < sizeof(f_months_days)/sizeof(f_months_days[0]))
                  && (month_idx < (date->Month - 1)); month_idx++) {
                days += months_days[month_idx];
            }

            for (year_idx = LTEDS_DATE_MIN_YEAR; year_idx < date->Year; year_idx++) {
                leap_year = IS_LEAP_YEAR(year_idx);
                days+= leap_year ? 366 : 365;
            }
        }
        err = LTEDS_PutUnInt(ctx, bitsize, days);
    }
    return err;
}

LTEDSAPI_DllExport(BOOLEAN) LTEDS_DateIsValid(const TLTEDS_INFO_DATE *date) {
    return (date->Year >= LTEDS_DATE_MIN_YEAR) && (date->Month > 0) &&
            (date->Month <= sizeof(f_months_days)/sizeof(f_months_days[0])) &&
            (date->Day > 0) && (date->Day <= IS_LEAP_YEAR(date->Year) ?
                                    f_months_days_leap[date->Month-1] :
                                    f_months_days[date->Month-1]);
}


static INT LTEDS_GetPlainDWORD(TLTEDS_DECODE_CONTEXT *ctx, DWORD bitsize, DWORD *val) {
    INT err = bitsize > 32 ? LTEDS_ERROR_INVALID_BITSIZE : LTEDS_OK;
    if (err == LTEDS_OK) {
        DWORD rdval = 0;
        err = LTEDS_GetBits(ctx, bitsize, &rdval);
        if ((err == LTEDS_OK) && (val != NULL)) {
            *val = rdval;
        }
    }
    return err;
}


static INT LTEDS_PutPlainDWORD(TLTEDS_ENCODE_CONTEXT *ctx, DWORD bitsize, DWORD val) {
    INT err = bitsize > 32 ? LTEDS_ERROR_INVALID_BITSIZE : LTEDS_OK;
    if (err == LTEDS_OK) {
        if ((bitsize < 32) && (val >= 1UL << bitsize)) {
            err = LTEDS_ERROR_ENCODE_VALUE;
        } else {
            err = LTEDS_PutBits(ctx, bitsize, &val);
        }
    }
    return err;
}




LTEDSAPI_DllExport(INT) LTEDS_GetUnInt(TLTEDS_DECODE_CONTEXT *ctx, DWORD bitsize, DWORD *val) {    
    INT err = bitsize > 32 ? LTEDS_ERROR_INVALID_BITSIZE : LTEDS_OK;
    if (err == LTEDS_OK) {
        DWORD rdval = 0;
        err = LTEDS_GetPlainDWORD(ctx, bitsize, &rdval);
        if ((err == LTEDS_OK) && (IS_ALL_SET(rdval, bitsize))) {
            rdval = LTEDS_UNINT_VAL_NOT_USED;
        }
        if ((err == LTEDS_OK) && (val != NULL)) {
            *val = rdval;
        }
    }
    return err;
}

LTEDSAPI_DllExport(INT) LTEDS_PutUnInt(TLTEDS_ENCODE_CONTEXT *ctx, DWORD bitsize, DWORD val) {
    INT err = bitsize > 32 ? LTEDS_ERROR_INVALID_BITSIZE : LTEDS_OK;
    if (err == LTEDS_OK) {
        /* значение все 1 соответствует не действительному и кодируется
         * как все 1 в результируещем поле */
        if (val == LTEDS_UNINT_VAL_NOT_USED)
            val = (DWORD)((1UL << bitsize) - 1);

        err = LTEDS_PutPlainDWORD(ctx, bitsize, val);
    }
    return err;
}




LTEDSAPI_DllExport(INT) LTEDS_GetEnumVal(TLTEDS_DECODE_CONTEXT *ctx, DWORD bitsize, DWORD *val) {
    return  LTEDS_GetPlainDWORD(ctx, bitsize, val);
}


LTEDSAPI_DllExport(INT) LTEDS_PutEnumVal(TLTEDS_ENCODE_CONTEXT *ctx, DWORD bitsize, DWORD val) {
    return  LTEDS_PutPlainDWORD(ctx, bitsize, val);
}

LTEDSAPI_DllExport(INT) LTEDS_GetSelectCase(TLTEDS_DECODE_CONTEXT *ctx, DWORD bitsize, DWORD *val) {
    return  LTEDS_GetPlainDWORD(ctx, bitsize, val);
}

LTEDSAPI_DllExport(INT) LTEDS_PutSelectCase(TLTEDS_ENCODE_CONTEXT *ctx, DWORD bitsize, DWORD val) {
    return  LTEDS_PutPlainDWORD(ctx, bitsize, val);
}

LTEDSAPI_DllExport(INT) LTEDS_GetStructArraySize(TLTEDS_DECODE_CONTEXT *ctx, DWORD bitsize, DWORD *val) {
    return  LTEDS_GetPlainDWORD(ctx, bitsize, val);
}

LTEDSAPI_DllExport(INT) LTEDS_PutStructArraySize(TLTEDS_ENCODE_CONTEXT *ctx, DWORD bitsize, DWORD val) {
    return  LTEDS_PutPlainDWORD(ctx, bitsize, val);
}





LTEDSAPI_DllExport(INT) LTEDS_GetChr5(TLTEDS_DECODE_CONTEXT *ctx, DWORD bitsize, CHAR *val) {
    INT err = bitsize % 5 == 0 ? LTEDS_OK : LTEDS_ERROR_INVALID_BITSIZE;
    if (err == LTEDS_OK) {
        DWORD char_pos, total_chars = bitsize / 5;
        for (char_pos = 0; (char_pos < total_chars) && (err == LTEDS_OK); char_pos++) {
            err = f_get_chr5_symbol(ctx, val++);
        }        
    }
    if (val)
        *val++ = '\0';
    return err;
}

LTEDSAPI_DllExport(INT) LTEDS_PutChr5(TLTEDS_ENCODE_CONTEXT *ctx, DWORD bitsize, const CHAR *str) {
    INT err = bitsize % 5 == 0 ? LTEDS_OK : LTEDS_ERROR_INVALID_BITSIZE;
    if (err == LTEDS_OK) {
        DWORD char_pos, total_chars = bitsize / 5;
        BOOL strend = FALSE;
        for (char_pos = 0; (char_pos < total_chars) && (err == LTEDS_OK); char_pos++) {
            CHAR sym;
            if (!strend && (*str == '\0'))
                strend = TRUE;
            sym = strend ? ' ' : *str++;
            err = f_put_chr5_symbol(ctx, sym);
        }
    }
    return  err;
}




LTEDSAPI_DllExport(INT) LTEDS_GetASCII(TLTEDS_DECODE_CONTEXT *ctx, DWORD bitsize, CHAR *val) {
    INT err = bitsize % 7 == 0 ? LTEDS_OK : LTEDS_ERROR_INVALID_BITSIZE;
    if (err == LTEDS_OK) {
        DWORD char_pos, total_chars = bitsize / 5;
        for (char_pos = 0; (char_pos < total_chars) && (err == LTEDS_OK); char_pos++) {
            CHAR sym;
            err = LTEDS_GetBits(ctx, 7, &sym);
            if (err == LTEDS_OK) {
                if (val)
                    *val++ = sym;
            }
        }
    }

    if (val)
        *val++ = '\0';

    return err;
}

LTEDSAPI_DllExport(INT) LTEDS_PutASCII(TLTEDS_ENCODE_CONTEXT *ctx, DWORD bitsize, const CHAR *str) {
    INT err = bitsize % 7 == 0 ? LTEDS_OK : LTEDS_ERROR_INVALID_BITSIZE;
    if (err == LTEDS_OK) {
        DWORD char_pos, total_chars = bitsize / 5;
        BOOL strend = FALSE;
        for (char_pos = 0; (char_pos < total_chars) && (err == LTEDS_OK); char_pos++) {
            CHAR sym;

            if (!strend && (*str == '\0'))
                strend = TRUE;
            sym = strend ? '\0' : *str++;
            if ((sym & 0x80) != 0) {
                err = LTEDS_ERROR_ENCODE_VALUE;
            } else {
                err = LTEDS_PutBits(ctx, 7, &sym);
            }
        }
    }
    return err;
}




LTEDSAPI_DllExport(LPCSTR) LTEDS_GetErrorString(INT err) {
    size_t i;
    for (i = 0; (i < sizeof(f_err_tbl) / sizeof(f_err_tbl[0])); i++) {
        if (f_err_tbl[i].code == err)
            return f_err_tbl[i].message;
    }
    return f_unknown_error_string;
}


