/*
 * acq_iso8583.c - ISO 8583 전문 코덱 구현 (1차 비트맵 + DE 인코딩).
 *   DE 포맷 정의(실무 핵심 필드 부분집합):
 *     n=숫자 고정, an/ans=영숫자 고정, LLVAR=2자리 길이접두 가변.
 *   와이어: MTI(4 ASCII) + 비트맵(16 hex = 64bit) + 필드들.
 *   비트맵 비트 i(1-base) = DE i 존재. DE1(비트1)=2차비트맵(미사용).
 */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "acq_iso8583.h"

/* DE 포맷 테이블: len>0 고정길이, len<0 = LLVAR(최대 -len). type: 0=n,1=ans */
static const struct { int len; int type; } DEF[ISO_MAX_DE + 1] = {
    [2]  = {-19, 0},  /* PAN (LLVAR) */
    [3]  = {6,   0},  /* 처리코드 */
    [4]  = {12,  0},  /* 거래금액 */
    [7]  = {10,  0},  /* 전송일시 MMDDhhmmss */
    [11] = {6,   0},  /* STAN */
    [12] = {6,   0},  /* 로컬시각 hhmmss */
    [13] = {4,   0},  /* 로컬일자 MMDD */
    [18] = {4,   0},  /* MCC */
    [22] = {3,   0},  /* POS entry mode */
    [37] = {12,  1},  /* RRN(참조번호) */
    [38] = {6,   1},  /* 승인번호 */
    [39] = {2,   1},  /* 응답코드 */
    [41] = {8,   1},  /* 단말ID(TID) */
    [42] = {15,  1},  /* 가맹점ID(MID) */
    [43] = {-40, 1},  /* 가맹점명(LLVAR) */
    [49] = {3,   0},  /* 통화코드 */
    [55] = {-99, 1},  /* EMV 데이터(LLVAR) */
};

void iso_init(iso_msg_t *m) {
    memset(m, 0, sizeof(*m));
}
void iso_set(iso_msg_t *m, int de, const char *val) {
    if (de < 2 || de > ISO_MAX_DE || !val) return;
    strncpy(m->de[de], val, sizeof(m->de[de]) - 1);
    m->de[de][sizeof(m->de[de]) - 1] = 0;
    m->present[de] = 1;
}
const char *iso_get(const iso_msg_t *m, int de) {
    if (de < 2 || de > ISO_MAX_DE || !m->present[de]) return "";
    return m->de[de];
}

/* 고정 숫자필드 좌0패딩 / 영숫자 우공백패딩 */
static void fmt_fixed(const char *v, int len, int type, char *out) {
    int vl = (int)strlen(v);
    if (vl >= len) { memcpy(out, v, len); out[len] = 0; return; }
    if (type == 0) { /* n: 좌 0패딩 */
        int pad = len - vl;
        memset(out, '0', pad); memcpy(out + pad, v, vl); out[len] = 0;
    } else {          /* ans: 우 공백패딩 */
        memcpy(out, v, vl); memset(out + vl, ' ', len - vl); out[len] = 0;
    }
}

int iso_build(const iso_msg_t *m, char *out, int outcap) {
    unsigned char bm[8] = {0};
    char body[ISO_MAXLEN]; int bl = 0; int de;
    for (de = 2; de <= 64; de++) {
        if (!m->present[de] || DEF[de].len == 0) continue;
        bm[(de - 1) / 8] |= (unsigned char)(0x80 >> ((de - 1) % 8));
        const char *v = m->de[de];
        if (DEF[de].len < 0) {                 /* LLVAR */
            int vl = (int)strlen(v); int maxl = -DEF[de].len;
            if (vl > maxl) vl = maxl;
            bl += snprintf(body + bl, sizeof(body) - bl, "%02d", vl);
            memcpy(body + bl, v, vl); bl += vl; body[bl] = 0;
        } else {                                /* 고정 */
            char f[160]; fmt_fixed(v, DEF[de].len, DEF[de].type, f);
            bl += snprintf(body + bl, sizeof(body) - bl, "%s", f);
        }
        if (bl >= (int)sizeof(body) - 200) return -1;
    }
    /* MTI + 비트맵(hex 16) + body */
    int n = snprintf(out, outcap, "%.4s", m->mti);
    for (de = 0; de < 8; de++) n += snprintf(out + n, outcap - n, "%02X", bm[de]);
    n += snprintf(out + n, outcap - n, "%s", body);
    return (n < outcap) ? n : -1;
}

static int hexbyte(const char *p) {
    int hi = (p[0] <= '9') ? p[0] - '0' : (p[0] & 0xDF) - 'A' + 10;
    int lo = (p[1] <= '9') ? p[1] - '0' : (p[1] & 0xDF) - 'A' + 10;
    return (hi << 4) | lo;
}

int iso_parse(const char *wire, iso_msg_t *m) {
    iso_init(m);
    int wl = (int)strlen(wire);
    if (wl < 4 + 16) return -1;
    memcpy(m->mti, wire, 4); m->mti[4] = 0;
    unsigned char bm[8];
    for (int i = 0; i < 8; i++) bm[i] = (unsigned char)hexbyte(wire + 4 + i * 2);
    int pos = 4 + 16, cnt = 0;
    for (int de = 2; de <= 64; de++) {
        if (!(bm[(de - 1) / 8] & (0x80 >> ((de - 1) % 8)))) continue;
        if (DEF[de].len == 0) return -1;      /* 정의없는 필드 켜짐 = 오류 */
        int flen;
        if (DEF[de].len < 0) {                 /* LLVAR: 앞 2자리 길이 */
            if (pos + 2 > wl) return -1;
            flen = (wire[pos] - '0') * 10 + (wire[pos + 1] - '0'); pos += 2;
        } else flen = DEF[de].len;
        if (pos + flen > wl || flen >= (int)sizeof(m->de[de])) return -1;
        memcpy(m->de[de], wire + pos, flen); m->de[de][flen] = 0;
        m->present[de] = 1; pos += flen; cnt++;
    }
    return cnt;
}
