# -*- coding: utf-8 -*-
"""Hiệu chỉnh TỈ LỆ cho đúng bộ mã mà AGY đã chọn (giữ nguyên màu AGY chọn, chỉ tính lại tỉ lệ
để màu dự kiến khớp mục tiêu L* 47,1 / a* +15,8 / b* +5,6), bằng mô hình K-M của code."""
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 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 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 hexs(lab):
    return '#%02X%02X%02X' % tuple(np.round(lab_to_rgb01(lab) * 255).astype(int))


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


c = sqlite3.connect('file:%s?mode=ro' % DB.replace('\\', '/'), uri=True)
hang = {}
for nhom, ten, nd, hexv, L, a, b, tin in c.execute("SELECT nhom, ma_mau, nong_do, hex, L_star, a_star, b_star, do_tin_cay FROM mau_bang_in"):
    hang[(ten or '').strip()] = dict(nhom=nhom, nd=str(nd), hex=hexv, lab=np.array([L, a, b]), tin=tin or '')


def lay(mau, nd):
    for k, v in hang.items():
        if mau.lower() in k.lower() and v['nd'] == str(nd):
            return k, v
    raise KeyError('%s (%s%%)' % (mau, nd))


BO = {
    'PA1-hiệu chỉnh (bộ mã như AGY: BS1008 5% + VM334 5% + đen + Vàng Y57 5%)': [
        ('Nâu BS1008', '5'), ('Đỏ VM334', '5'), ('Đen TCL3 (1-2)DM (V4)', '2'), ('Vàng Y57', '5')],
    'PA2-hiệu chỉnh (bộ mã như AGY: BS1008 10% + RS718 10% + M100S 2% + YB 87 5%)': [
        ('Nâu BS1008', '10'), ('Đỏ RS718', '10'), ('Đen BT M100S', '2'), ('Vàng YB 87', '5')],
}
kq_all = {}
for ten, ds in BO.items():
    comps = [lay(*x) for x in ds]
    KS = np.stack([ks_of(lab_to_rgb01(v['lab'])) for _, v in comps])
    best = []
    for ws in itertools.product(np.arange(0.0, 1.01, 0.05), repeat=len(comps)):
        if abs(sum(ws) - 1) > 1e-9:
            continue
        K = (np.array(ws)[:, None] * KS).sum(0)
        lab_lop = rgb01_to_lab(np.clip(1 + K - np.sqrt(K ** 2 + 2 * K), 1e-4, 1))
        for w in (0.40, 0.45, 0.50, 0.55, 0.60):
            pred = (1 - w) * GO_MOC + w * lab_lop
            best.append((float(np.linalg.norm(pred - MUC_TIEU)), w, ws, pred))
    best.sort(key=lambda x: x[0])
    print('\n### %s' % ten)
    for e, w, ws, pred in best[:5]:
        print('   ΔE %-5.2f  w=%.2f  %s  →  %s   %s' % (
            e, w, ' + '.join('%s %d%%' % (k[:20], round(t * 100)) for (k, _), t in zip(comps, ws)),
            hexs(pred), 'L*%.1f a*%+.1f b*%+.1f' % tuple(pred)))
    e, w, ws, pred = best[0]
    kq_all[ten] = dict(dE=round(e, 2), w=w,
                       thanh_phan=[dict(ten=k, nong_do_o=v['nd'], hex=v['hex'], tin_cay=v['tin'], ti_le=round(float(t), 3))
                                   for (k, v), t in zip(comps, ws)],
                       hex_du_kien=hexs(pred), lab_du_kien=[round(float(x), 1) for x in pred])

print('\nmục tiêu: L* 47.1 a* +15.8 b* +5.6 (%s)' % hexs(MUC_TIEU))
json.dump(dict(muc_tieu=[float(v) for v in MUC_TIEU], go_moc=[float(v) for v in GO_MOC], ket_qua=kq_all),
          io.open(os.path.join(B, 'phan-tich-nen-mau-07-10', '_ket_qua_hieu_chinh_theo_bo_ma_agy.json'), 'w', encoding='utf-8'),
          ensure_ascii=False, indent=1)
print('lưu: _ket_qua_hieu_chinh_theo_bo_ma_agy.json')
