# -*- coding: utf-8 -*-
"""Tìm tổ hợp màu nền từ bảng in CSDL B (kiểm chứng ĐỘC LẬP với AGY) — bản tối ưu tốc độ.
Mô hình: K-M 1 hằng số (3 kênh) cho hỗn hợp màu → phủ lên gỗ mộc với hệ số phủ w.
Lọc hợp lý trước khi tìm: loại ô tin cậy thấp, loại màu xanh dương/lá (mục tiêu là nâu đỏ ấm)."""
import io, itertools, json, os, sqlite3
import numpy as np

B = r'C:\Claw 2\Nghien cuu\Du-an-B-He-mau-Stain-Glaze'
DB = os.path.join(B, 'CSDLB.db')
MUC_TIEU = np.array([47.1, 15.8, 5.6])
GO_MOC = np.array([69.0, -0.4, 2.1])
M_FWD = np.array([[0.4124, 0.3576, 0.1805], [0.2126, 0.7152, 0.0722], [0.0193, 0.1192, 0.9505]])
M_INV = np.array([[3.2406, -1.5372, -0.4986], [-0.9689, 1.8758, 0.0415], [0.0557, -0.2040, 1.0570]])


def lab_to_rgb01(lab):
    L, a, b = lab
    fy = (L + 16) / 116
    f = np.array([fy + a / 500, fy, fy - b / 200])
    f = np.where(f ** 3 > 0.008856, f ** 3, (116 * f - 16) / 903.3)
    r = M_INV @ (f * np.array([0.95047, 1.0, 1.08883]))
    r = np.where(r <= 0.0031308, 12.92 * r, 1.055 * np.clip(r, 0, None) ** (1 / 2.4) - 0.055)
    return np.clip(r, 0, 1)


def rgb01_to_lab(rgb):
    a = np.clip(np.asarray(rgb, float), 0, 1)
    a = np.where(a <= 0.04045, a / 12.92, ((a + 0.055) / 1.055) ** 2.4)
    xyz = (M_FWD @ a) / np.array([0.95047, 1.0, 1.08883])
    f = np.where(xyz > 0.008856, np.cbrt(xyz), 7.787 * xyz + 16 / 116)
    return np.array([116 * f[1] - 16, 500 * (f[0] - f[1]), 200 * (f[1] - f[2])])


def hexs(lab):
    return '#%02X%02X%02X' % tuple(np.round(lab_to_rgb01(lab) * 255).astype(int))


def km_blend(KS, w):
    w = np.asarray(w, float); w = w / w.sum()
    K = (w[:, None] * KS).sum(0)
    R = np.clip(1 + K - np.sqrt(K ** 2 + 2 * K), 1e-4, 1)
    return R


def ks_of(rgb01):
    R = np.clip(rgb01, 0.004, 1.0)
    return ((1 - R) ** 2) / (2 * R)


# --- dữ liệu ---
c = sqlite3.connect('file:%s?mode=ro' % DB.replace('\\', '/'), uri=True)
o = []
for nhom, ten, nd, hexv, L, a, bb, tin in c.execute(
        """SELECT nhom, ma_mau, nong_do, hex, L_star, a_star, b_star, do_tin_cay FROM mau_bang_in
           WHERE L_star IS NOT NULL ORDER BY nhom, nong_do"""):
    o.append(dict(nhom=nhom, ten=ten, nd=nd, hex=hexv, lab=np.array([L, a, bb]), tin=tin or ''))
pool = [x for x in o if not x['tin'].startswith('thap') and x['lab'][2] > -12]      # loại tin cậy thấp + màu lạnh
for x in pool:
    x['ks'] = ks_of(lab_to_rgb01(x['lab']))
print('tổng ô đo được %d | dùng để tìm %d ô (bỏ tin cậy thấp & màu lạnh)' % (len(o), len(pool)))

ket = []
for w in (0.45, 0.55):
    # k = 1
    for i, x in enumerate(pool):
        lab_lop = rgb01_to_lab(lab_to_rgb01(x['lab']))
        pred = (1 - w) * GO_MOC + w * lab_lop
        ket.append((float(np.linalg.norm(pred - MUC_TIEU)), w, (x,), (1.0,), lab_lop, pred))
    # k = 2
    for i, j in itertools.combinations(range(len(pool)), 2):
        a, b = pool[i], pool[j]
        for wa in np.arange(0.1, 1.0, 0.05):
            lab_lop = rgb01_to_lab(km_blend(np.stack([a['ks'], b['ks']]), [wa, 1 - wa]))
            pred = (1 - w) * GO_MOC + w * lab_lop
            ket.append((float(np.linalg.norm(pred - MUC_TIEU)), w, (a, b), (round(float(wa), 2), round(1 - float(wa), 2)), lab_lop, pred))
    # k = 3: chỉ ghép từ nhóm tốt nhất của k<=2 + 12 ô sẫm nhất
    tot2 = sorted([k for k in ket if len(k[2]) <= 2], key=lambda x: x[0])[:25]
    ung = {x['ten']: x for k in tot2 for x in k[2]}
    for x in sorted(pool, key=lambda z: z['lab'][0])[:12]:
        ung[x['ten']] = x
    cand = list(ung.values())
    for trip in itertools.combinations(range(len(cand)), 3):
        A, Bb, C = (cand[t] for t in trip)
        KS = np.stack([A['ks'], Bb['ks'], C['ks']])
        for wa in np.arange(0.1, 0.9, 0.1):
            for wb in np.arange(0.1, 0.9, 0.1):
                if wa + wb >= 0.95:
                    continue
                lab_lop = rgb01_to_lab(km_blend(KS, [wa, wb, 1 - wa - wb]))
                pred = (1 - w) * GO_MOC + w * lab_lop
                ket.append((float(np.linalg.norm(pred - MUC_TIEU)), w, (A, Bb, C),
                            (round(float(wa), 2), round(float(wb), 2), round(1 - wa - wb, 2)), lab_lop, pred))
    print('  w=%.2f: %d tổ hợp đã xét' % (w, len(ket)))

ket.sort(key=lambda x: x[0])
print('\n=== TOP 15 (ΔE76 nhỏ = gần màu mẫu; w = hệ số phủ lớp màu trên gỗ) ===')
print('%-6s %-5s %-52s %-26s %s' % ('ΔE', 'w', 'thành phần', 'tỉ lệ', 'màu dự kiến'))
seen, dem = set(), 0
for e, w, comps, ws, lab_lop, pred in ket:
    key = tuple(sorted(x['ten'] for x in comps))
    if key in seen:
        continue
    seen.add(key); dem += 1
    ten = ' + '.join('%s' % x['ten'][:26] for x in comps)
    ti = '/'.join('%.0f' % (100 * y) for y in ws)
    print('%-6.1f %-5.2f %-52s %-26s %s' % (e, w, ten[:52], ti, hexs(pred)))
    if dem >= 15:
        break

kq = [dict(dE=round(e, 2), w=w,
           thanh_phan=[dict(ten=x['ten'], nhom=x['nhom'], nong_do_o=x['nd'], hex=x['hex'],
                            tin_cay=x['tin'], ti_le=y) for x, y in zip(comps, ws)],
           hex_ho_mau=hexs(lab_lop), lab_ho_mau=[round(float(v), 1) for v in lab_lop],
           hex_du_kien=hexs(pred), lab_du_kien=[round(float(v), 1) for v in pred])
      for e, w, comps, ws, lab_lop, pred in ket[:150]]
p = os.path.join(B, 'phan-tich-nen-mau-07-10', '_ket_qua_phoi_mau_bang_code.json')
json.dump(dict(muc_tieu=[float(v) for v in MUC_TIEU], go_moc=[float(v) for v in GO_MOC],
               mo_hinh='Kubelka-Munk 1 hằng số (3 kênh) cho hỗn hợp; kết quả = (1-w)*gỗ mộc + w*lớp màu',
               so_o_dung=len(pool), ket_qua=kq), io.open(p, 'w', encoding='utf-8'), ensure_ascii=False, indent=1)
print('\nlưu 150 tổ hợp tốt nhất:', p)
