--[[
  Prefiks `MindfulLens` w polu `profile` musi dokładnie odpowiadać nazwie profilu
  osadzonej w binarnych plikach `.dcp` (Camera Raw). Zmiana na „Analog Signature”
  wymaga przebudowy DCP i ponownej instalacji profili z menu wtyczki.
]]
local LrPathUtils = import "LrPathUtils"

local function pluginLoad(relativePath)
    return dofile(LrPathUtils.child(_PLUGIN.path, relativePath))
end

local publicCatalog = pluginLoad("lib/PublicCatalog.lua")

local M = {}

M.emulsions = {
    {
        id = "leicachrome",
        label = "Leicachrome",
        profile = "MindfulLens Leicachrome",
        dcpFile = "AI_KM_LEICACHROME.dcp",
        foundationPreset = "profiles/foundation/leicachrome_foundation.xmp",
        lutFile = "profiles/luts/leicachrome.cube",
        microTune = {
            Contrast2012 = 2,
            Clarity2012 = 2,
            SaturationAdjustmentRed = 2,
            SaturationAdjustmentBlue = 6,
            LuminanceAdjustmentBlue = -6,
        },
    },
    {
        id = "bw_hp5",
        label = "Ilford HP5 400",
        profile = "MindfulLens Ilford HP5 400",
        dcpFile = "AI_KM_BW_HP5.dcp",
        foundationPreset = "profiles/foundation/bw_hp5_foundation.xmp",
        lutFile = "profiles/luts/bw_hp5.cube",
        bw = true,
        microTune = {
            Contrast2012 = 2,
            Shadows2012 = 8,
            Blacks2012 = -3,
            Texture = 6,
        },
    },
}

M.formats = {
    { id = "35mm", label = "35mm | Small Format" },
    { id = "mf_120", label = "120 | Medium Format" },
    { id = "lf_4x5", label = "4x5 | Large Format" },
    { id = "lf_8x10", label = "8x10 | Ultra Large Format" },
}

M.sourceScales = {
    { id = "auto", label = "Auto (detect from camera)" },
    { id = "full_frame", label = "Full Frame" },
    { id = "aps_c", label = "APS-C" },
    { id = "micro_four_thirds", label = "Micro 4/3" },
    { id = "compact_small", label = "Small sensor / compact" },
    { id = "digital_mf", label = "Digital Medium Format" },
}

M.sensorProfiles = {
    {
        id = "balanced_cmos",
        label = "Balanced CMOS (Full Frame)",
        smartMatch = {
            exposureBias = 0.00,
            contrastBias = 0,
            tempShift = 0,
            tintShift = 0,
            strengthScale = 1.00,
        },
    },
    {
        id = "aps_c_cmos",
        label = "APS-C CMOS",
        smartMatch = {
            exposureBias = 0.06,
            contrastBias = -1,
            tempShift = 20,
            tintShift = 0,
            strengthScale = 1.05,
        },
    },
    {
        id = "micro43_cmos",
        label = "Micro 4/3 CMOS",
        smartMatch = {
            exposureBias = 0.10,
            contrastBias = -2,
            tempShift = 40,
            tintShift = 1,
            strengthScale = 1.08,
        },
    },
    {
        id = "compact_cmos",
        label = "Compact / Small Sensor",
        smartMatch = {
            exposureBias = 0.12,
            contrastBias = -3,
            tempShift = 60,
            tintShift = 1,
            strengthScale = 1.12,
        },
    },
    {
        id = "medium_format_cmos",
        label = "Digital Medium Format",
        smartMatch = {
            exposureBias = -0.04,
            contrastBias = 1,
            tempShift = -30,
            tintShift = -1,
            strengthScale = 0.92,
        },
    },
    {
        id = "leica_m9_ccd",
        label = "Leica M9 CCD Signature",
        smartMatch = {
            exposureBias = 0.14,
            contrastBias = -4,
            tempShift = 140,
            tintShift = 7,
            strengthScale = 1.18,
        },
    },
    {
        id = "kodak_ccd_legacy",
        label = "Kodak CCD Legacy",
        smartMatch = {
            exposureBias = 0.03,
            contrastBias = 6,
            tempShift = -45,
            tintShift = -3,
            strengthScale = 1.24,
        },
    },
}

M.sensorProfilesBySourceScale = {
    auto = "balanced_cmos",
    full_frame = "balanced_cmos",
    aps_c = "aps_c_cmos",
    micro_four_thirds = "micro43_cmos",
    compact_small = "compact_cmos",
    digital_mf = "medium_format_cmos",
}

M.baseFamilies = {
    {
        id = "neutral_cmos",
        label = "Zero Point CMOS",
        description = "Neutral zero-point CMOS baseline: no added sensor character, clean lab reference.",
        adjustments = {},
    },
    {
        id = "leica_m9_ccd",
        label = "Leica M9 CCD",
        description = "Warm-magenta Leica CCD signature with soft highlight shoulder and skin-priority reds.",
        strength = 0.41,
        adjustments = {
            Contrast2012 = -12,
            Highlights2012 = -28,
            Shadows2012 = 18,
            Whites2012 = -22,
            Blacks2012 = 16,
            Clarity2012 = -8,
            Dehaze = -4,
            Texture = -6,
            Vibrance = 15,
            Saturation = 10,
            HueAdjustmentRed = -8,
            HueAdjustmentOrange = -7,
            HueAdjustmentBlue = 5,
            HueAdjustmentAqua = 4,
            SaturationAdjustmentRed = 26,
            SaturationAdjustmentOrange = 20,
            SaturationAdjustmentBlue = -16,
            SaturationAdjustmentAqua = -12,
            SaturationAdjustmentGreen = -10,
            LuminanceAdjustmentOrange = 14,
            LuminanceAdjustmentRed = 12,
            LuminanceAdjustmentBlue = 6,
            ColorGradeHighlightsHue = 44,
            ColorGradeHighlightsSat = 12,
            ColorGradeMidtoneHue = 28,
            ColorGradeMidtoneSat = 8,
            ColorGradeShadowsHue = 314,
            ColorGradeShadowsSat = 12,
            ColorGradeBalance = 22,
            ColorGradeBlending = 40,
        },
    },
    {
        id = "kodak_ccd_legacy",
        label = "Kodak CCD Legacy",
        description = "Legacy Kodak CCD split-tone: cooler cyan shadows, harder blacks, punchier structure.",
        strength = 0.44,
        adjustments = {
            Contrast2012 = 20,
            Highlights2012 = -12,
            Shadows2012 = -12,
            Whites2012 = 10,
            Blacks2012 = -24,
            Clarity2012 = 15,
            Dehaze = 10,
            Texture = 12,
            Vibrance = 18,
            Saturation = 12,
            HueAdjustmentBlue = -12,
            HueAdjustmentAqua = -14,
            HueAdjustmentYellow = 10,
            HueAdjustmentGreen = 8,
            SaturationAdjustmentBlue = 24,
            SaturationAdjustmentAqua = 22,
            SaturationAdjustmentYellow = 14,
            SaturationAdjustmentGreen = -10,
            SaturationAdjustmentOrange = -6,
            LuminanceAdjustmentBlue = -16,
            LuminanceAdjustmentAqua = -12,
            LuminanceAdjustmentYellow = 10,
            LuminanceAdjustmentOrange = -5,
            ColorGradeShadowsHue = 208,
            ColorGradeShadowsSat = 16,
            ColorGradeMidtoneHue = 196,
            ColorGradeMidtoneSat = 8,
            ColorGradeHighlightsHue = 56,
            ColorGradeHighlightsSat = 7,
            ColorGradeBalance = -18,
            ColorGradeBlending = 62,
        },
    },
    {
        id = "cinema_digital",
        label = "Cinema Digital",
        description = "Low-contrast digital cinema baseline for grading headroom.",
        adjustments = {
            Contrast2012 = -6,
            Highlights2012 = -8,
            Shadows2012 = 8,
            Blacks2012 = 5,
            Dehaze = -2,
            Clarity2012 = -3,
            Saturation = -3,
            Vibrance = -2,
        },
    },
}

M.heroPresets = {
    panel7 = {
        {
            id = "lomo_purple_street",
            title = "Lomo Purple Street",
            tip = "Greens to magenta with punchy urban contrast.",
            settings = { lomoPurple = 72, metropolis = 18 },
        },
        {
            id = "metropolis_editorial",
            title = "Metropolis Editorial",
            tip = "Muted palette with bleach-bypass style structure.",
            settings = { metropolis = 64 },
        },
        {
            id = "redscale_sunset",
            title = "Redscale Sunset",
            tip = "Warm optical leak mood with deep blue suppression.",
            settings = { redscale = 62, lomoTurquoise = 10 },
        },
    },
    panel8 = {
        {
            id = "velvia_landscape",
            title = "Velvia Landscape",
            tip = "Dense greens/blues and high scenic separation.",
            settings = { portaSkinGuard = 14 },
        },
        {
            id = "portra_people",
            title = "Portra People",
            tip = "Skin-protective grading for portrait and reportage.",
            settings = { portaSkinGuard = 68 },
        },
        {
            id = "cineon_print",
            title = "Cineon Print",
            tip = "Flattened print-gamma base for cinematic finishing.",
            settings = { kodachromeK14 = 20 },
        },
    },
    panel9 = {
        {
            id = "filmsoup_experimental",
            title = "Film Soup Experimental",
            tip = "Chemical corrosion and chroma instability accents.",
            settings = { filmSoup = 56 },
        },
        {
            id = "vintage_90s",
            title = "Vintage 90s",
            tip = "Teal shadows, lifted blacks, and faded contrast.",
            settings = { filmSoup = 10 },
        },
    },
}

M.profileModes = {
    { id = "production", label = "Production (v1 locked)" },
    { id = "debug", label = "Debug (strong split)" },
}

M.nightBoostLevels = {
    { id = "off", label = "Off" },
    { id = "soft", label = "Soft" },
    { id = "medium", label = "Medium" },
    { id = "strong", label = "Strong" },
}

M.modularPresets = {
    {
        id = "print_kodak_2383_soft",
        label = "Print Media | Kodak 2383 Soft",
        xmpFile = "profiles/modular/print/print_kodak_2383_soft.xmp",
    },
    {
        id = "print_kodak_endura_warm",
        label = "Print Media | Kodak Endura Warm",
        xmpFile = "profiles/modular/print/print_kodak_endura_warm.xmp",
    },
    {
        id = "print_fuji_crystal_clean",
        label = "Print Media | Fuji Crystal Clean",
        xmpFile = "profiles/modular/print/print_fuji_crystal_clean.xmp",
    },
    {
        id = "halation_soft",
        label = "Optics | Halation Soft",
        xmpFile = "profiles/modular/optics/halation_soft.xmp",
    },
    {
        id = "halation_strong",
        label = "Optics | Halation Strong",
        xmpFile = "profiles/modular/optics/halation_strong.xmp",
    },
    {
        id = "bloom_soft",
        label = "Optics | Bloom Soft",
        xmpFile = "profiles/modular/optics/bloom_soft.xmp",
    },
    {
        id = "grain_35mm_soft",
        label = "Grain | 35mm Soft",
        xmpFile = "profiles/modular/grain/grain_35mm_soft.xmp",
    },
    {
        id = "grain_35mm_rough",
        label = "Grain | 35mm Rough",
        xmpFile = "profiles/modular/grain/grain_35mm_rough.xmp",
    },
    {
        id = "grain_120_clean",
        label = "Grain | 120 Clean",
        xmpFile = "profiles/modular/grain/grain_120_clean.xmp",
    },
    {
        id = "prep_defringe_soft",
        label = "Prep | Defringe Soft",
        xmpFile = "profiles/modular/prep/prep_defringe_soft.xmp",
    },
    {
        id = "prep_defringe_strong",
        label = "Prep | Defringe Strong",
        xmpFile = "profiles/modular/prep/prep_defringe_strong.xmp",
    },
}

M.cameraFallbacks = {
    ["OLYMPUS EP-1"] = "OLYMPUS E-M1 Mark II",
    ["OLYMPUS E-P1"] = "OLYMPUS E-M1 Mark II",
    ["SONY A9"] = "SONY A9 II",
    ["LEICA M9"] = "LEICA M10",
}

M.defaultEmulsionId = "leicachrome"
M.defaultFormatId = "35mm"
M.defaultSourceScaleId = "auto"
M.defaultSensorProfileId = "balanced_cmos"
M.defaultBaseFamilyId = "neutral_cmos"
M.defaultProfileMode = "production"
M.defaultConversionMode = "analyze"
M.defaultColorProcess = "refined"
M.defaultNightBoostLevel = "off"
M.defaultAutoDmin = false
M.defaultApplyFoundation = true
M.defaultUseAnalyzerWB = false
M.PREVIEW_WB_ALIGNMENT_ENABLED = false
M.defaultSmartMatch = false
M.defaultSmartMatchStrength = "balanced_film"
M.defaultSmartMatchAmount = 40
M.defaultSmartMatchWorkflow = "rep"
M.defaultAnalogizerProfile = "auto_ektar"
M.defaultUseAnalyzerOverrides = false
M.defaultApplyCameraProfile = false
M.defaultEnableBackupRender = true
M.defaultBackupOnProfileReject = true
M.defaultAllowGenericFallback = true
M.defaultDebugKeepTemp = false
M.defaultLutIntensity = 100
M.defaultSaturationTrim = 0
M.defaultLabGlow = 0
M.defaultLabFade = 0
M.defaultSoftHighs = false
M.defaultSoftLows = false
M.defaultWhiteClip = 0
M.defaultBlackClip = 0
M.defaultMatchMode = "full"
M.publicCatalogVersion = publicCatalog.catalogVersion
M.publicCatalogCodename = publicCatalog.catalogCodename

M.demoMode = true
M.demoProductName = "Analog Signature Demo"
M.demoUnlockedEmulsionIds = {
    leicachrome = true,
    bw_hp5 = true,
}
M.demoLockedEmulsionItems = {
    { title = "Kodak Gold 200 [C] | Inspired by MindfulLens", value = "__ml_demo_locked_001" },
    { title = "Kodak ColorPlus 200 [C] | Inspired by MindfulLens", value = "__ml_demo_locked_002" },
    { title = "Kodak Ektar 100 [C] | Inspired by MindfulLens", value = "__ml_demo_locked_003" },
    { title = "Kodak UltraMax 400 [C] | Inspired by MindfulLens", value = "__ml_demo_locked_004" },
    { title = "CineStill 800T [C] | Inspired by MindfulLens", value = "__ml_demo_locked_005" },
    { title = "CineStill 50D [C] | Inspired by MindfulLens", value = "__ml_demo_locked_006" },
    { title = "Fujifilm Pro 400H [A] | Inspired by MindfulLens", value = "__ml_demo_locked_007" },
    { title = "Fujifilm Superia X-TRA 400 [C] | Inspired by MindfulLens", value = "__ml_demo_locked_008" },
    { title = "Fujifilm Natura 1600 [C] | Inspired by MindfulLens", value = "__ml_demo_locked_009" },
    { title = "Fujifilm Superia 1600 [C] | Inspired by MindfulLens", value = "__ml_demo_locked_010" },
    { title = "Fujifilm Velvia 50 [C] | Inspired by MindfulLens", value = "__ml_demo_locked_011" },
    { title = "Fujifilm Astia 100F [C] | Inspired by MindfulLens", value = "__ml_demo_locked_012" },
    { title = "Fujifilm Provia 100F [A] | Inspired by MindfulLens", value = "__ml_demo_locked_013" },
    { title = "Fujifilm Provia 400X [C] | Inspired by MindfulLens", value = "__ml_demo_locked_014" },
    { title = "Fujifilm Fortia 50 [C] | Inspired by MindfulLens", value = "__ml_demo_locked_015" },
    { title = "Kodak Vision3 50D [C] | Inspired by MindfulLens", value = "__ml_demo_locked_016" },
    { title = "Kodak Vision3 250D [C] | Inspired by MindfulLens", value = "__ml_demo_locked_017" },
    { title = "Kodak Vision3 200T [C] | Inspired by MindfulLens", value = "__ml_demo_locked_018" },
    { title = "Kodak Vision3 200T+ [C] | Inspired by MindfulLens", value = "__ml_demo_locked_019" },
    { title = "Kodak Vision3 500T [A] | Inspired by MindfulLens", value = "__ml_demo_locked_020" },
    { title = "Clean Color 100 | Creative by MindfulLens", value = "__ml_demo_locked_021" },
    { title = "AgfaPhoto CT Precisa 100 [C] | Inspired by MindfulLens", value = "__ml_demo_locked_022" },
    { title = "Amber Reverse | Creative by MindfulLens", value = "__ml_demo_locked_023" },
    { title = "Ember Color | Creative by MindfulLens", value = "__ml_demo_locked_024" },
    { title = "Retro Color 500 | Creative by MindfulLens", value = "__ml_demo_locked_025" },
    { title = "Eastman Color Negative II 100 [C] | Inspired by MindfulLens", value = "__ml_demo_locked_026" },
    { title = "Sun Color 100 | Creative by MindfulLens", value = "__ml_demo_locked_027" },
    { title = "Kodak Tri-X 400 [C] | Inspired by MindfulLens", value = "__ml_demo_locked_028" },
    { title = "Kodak Tri-X 1600 [C] | Inspired by MindfulLens", value = "__ml_demo_locked_029" },
    { title = "Ilford XP2 Super 400 [C] | Inspired by MindfulLens", value = "__ml_demo_locked_030" },
    { title = "Kodak T-Max 400 [C] | Inspired by MindfulLens", value = "__ml_demo_locked_031" },
    { title = "Kodak T-Max 100 [C] | Inspired by MindfulLens", value = "__ml_demo_locked_032" },
    { title = "Fomapan 100 [C] | Inspired by MindfulLens", value = "__ml_demo_locked_033" },
    { title = "Arista EDU Ultra 400 [C] | Inspired by MindfulLens", value = "__ml_demo_locked_034" },
    { title = "Ilford Delta 3200 [C] | Inspired by MindfulLens", value = "__ml_demo_locked_035" },
    { title = "Kosmo Foto Mono 100 [C] | Inspired by MindfulLens", value = "__ml_demo_locked_036" },
    { title = "Fomapan 400 [C] | Inspired by MindfulLens", value = "__ml_demo_locked_037" },
    { title = "Ilford Delta 100 [C] | Inspired by MindfulLens", value = "__ml_demo_locked_038" },
    { title = "Clean Standard | Creative by MindfulLens", value = "__ml_demo_locked_039" },
    { title = "Vektro 100 | Creative by MindfulLens", value = "__ml_demo_locked_040" },
    { title = "Senova Light | Creative by MindfulLens", value = "__ml_demo_locked_041" },
    { title = "EV Pro Plus | Creative by MindfulLens", value = "__ml_demo_locked_042" },
    { title = "Kodachrome V1 [Creative] | Creative by MindfulLens", value = "__ml_demo_locked_043" },
    { title = "Kodachrome V2 [Creative] | Creative by MindfulLens", value = "__ml_demo_locked_044" },
    { title = "Velvia Pro [Landscape] | Creative by MindfulLens", value = "__ml_demo_locked_045" },
    { title = "Classic Chrome | Creative by MindfulLens", value = "__ml_demo_locked_046" },
    { title = "Pro Negative Soft | Creative by MindfulLens", value = "__ml_demo_locked_047" },
    { title = "Classic Negative | Creative by MindfulLens", value = "__ml_demo_locked_048" },
    { title = "Nostalgic Negative | Creative by MindfulLens", value = "__ml_demo_locked_049" },
    { title = "Fujifilm Eterna [C] | Inspired by MindfulLens", value = "__ml_demo_locked_050" },
    { title = "Nostalgic Color | Creative by MindfulLens", value = "__ml_demo_locked_051" },
    { title = "Oktar | Creative by MindfulLens", value = "__ml_demo_locked_052" },
    { title = "Zero Mute | Creative by MindfulLens", value = "__ml_demo_locked_053" },
    { title = "ProColor | Creative by MindfulLens", value = "__ml_demo_locked_054" },
    { title = "CineStill 800 [C] | Inspired by MindfulLens", value = "__ml_demo_locked_055" },
    { title = "Chroma Fade | Creative by MindfulLens", value = "__ml_demo_locked_056" },
    { title = "Neo Max | Creative by MindfulLens", value = "__ml_demo_locked_057" },
    { title = "MidRed Infra | Creative by MindfulLens", value = "__ml_demo_locked_058" },
    { title = "DreamNeg | Creative by MindfulLens", value = "__ml_demo_locked_059" },
    { title = "Zetra 100 | Creative by MindfulLens", value = "__ml_demo_locked_060" },
    { title = "Rose Spectra | Creative by MindfulLens", value = "__ml_demo_locked_061" },
    { title = "Ayon 200 | Creative by MindfulLens", value = "__ml_demo_locked_062" },
    { title = "Phoenix Ember | Creative by MindfulLens", value = "__ml_demo_locked_063" },
    { title = "Phenomena | Creative by MindfulLens", value = "__ml_demo_locked_064" },
    { title = "Crimson | Creative by MindfulLens", value = "__ml_demo_locked_065" },
    { title = "Estra 500 | Creative by MindfulLens", value = "__ml_demo_locked_066" },
    { title = "X-Tarr | Creative by MindfulLens", value = "__ml_demo_locked_067" },
    { title = "Magic Spice | Creative by MindfulLens", value = "__ml_demo_locked_068" },
    { title = "Amarelo 30D | Creative by MindfulLens", value = "__ml_demo_locked_069" },
    { title = "Vespera | Creative by MindfulLens", value = "__ml_demo_locked_070" },
    { title = "Veniliqum | Creative by MindfulLens", value = "__ml_demo_locked_071" },
    { title = "Gold Luxe | Creative by MindfulLens", value = "__ml_demo_locked_072" },
    { title = "Pastel Archive 400 | Creative by MindfulLens", value = "__ml_demo_locked_073" },
    { title = "Clean Chrome 100 | Creative by MindfulLens", value = "__ml_demo_locked_074" },
    { title = "Konica Centuria 100 [C] | Inspired by MindfulLens", value = "__ml_demo_locked_075" },
    { title = "Fujifilm Reala 500D [C] | Inspired by MindfulLens", value = "__ml_demo_locked_076" },
    { title = "Rollei Ortho 25 [C] | Inspired by MindfulLens", value = "__ml_demo_locked_077" },
    { title = "Kodak Pro Image 100 [C] | Inspired by MindfulLens", value = "__ml_demo_locked_078" },
    { title = "Kodak Ektachrome E100 [A] | Inspired by MindfulLens", value = "__ml_demo_locked_079" },
    { title = "Kodak Portra 160 [A] | Inspired by MindfulLens", value = "__ml_demo_locked_080" },
    { title = "Kodak Portra 400 Skin-Safe | Inspired by MindfulLens", value = "__ml_demo_locked_081" },
    { title = "Kodak Portra 400 [A] | Inspired by MindfulLens", value = "__ml_demo_locked_082" },
    { title = "Kodak Portra 800 [A] | Inspired by MindfulLens", value = "__ml_demo_locked_083" },
    { title = "Kodak Kodachrome 64 [A] | Inspired by MindfulLens", value = "__ml_demo_locked_084" },
}

function M.isDemoUnlockedEmulsion(emulsionId)
    return M.demoUnlockedEmulsionIds[tostring(emulsionId or "")] == true
end

function M.getDemoLockedEmulsionItems()
    return M.demoLockedEmulsionItems
end

for _, em in ipairs(M.emulsions) do
    em.legacyLabel = em.label
    em.publicLabel = publicCatalog.getPublicLabel(em.id, em.label)
    em.clientFacingLabel = publicCatalog.getClientFacingLabel(em.id, em.legacyLabel, em.publicLabel)
    em.publicCategoryId = publicCatalog.getCategoryId(em.id, em.type, em.bw == true)
    em.publicCategoryMeta = publicCatalog.getCategoryMeta(em.publicCategoryId)
    em.publicCategoryTitle = tostring((em.publicCategoryMeta and em.publicCategoryMeta.title) or "")
    em.publicCategoryDescription = tostring((em.publicCategoryMeta and em.publicCategoryMeta.short) or "")
    em.publicShortDescription = publicCatalog.getShortDescription(em.id)
    em.publicLongDescription = publicCatalog.getLongDescription(em.id)
    em.publicTags = publicCatalog.getTags(em.id, em.type, em.bw == true)
    em.customerVisible = (type(publicCatalog.isEmulsionVisibleToCustomer) == "function")
        and publicCatalog.isEmulsionVisibleToCustomer(em.id) == true
end

function M.isEmulsionVisibleToCustomer(emulsionId)
    if type(publicCatalog.isEmulsionVisibleToCustomer) == "function" then
        return publicCatalog.isEmulsionVisibleToCustomer(emulsionId) == true
    end
    if type(publicCatalog.isCustomerVisible) == "function" then
        return publicCatalog.isCustomerVisible(emulsionId) == true
    end
    return false
end

function M.getCustomerEmulsions()
    local visible = {}
    local seen = {}
    for _, em in ipairs(M.emulsions or {}) do
        local emId = tostring(em.id or "")
        if emId ~= "" and not seen[emId] and M.isEmulsionVisibleToCustomer(emId) then
            seen[emId] = true
            visible[#visible + 1] = em
        end
    end
    return visible
end

function M.findEmulsion(emulsionId)
    for _, em in ipairs(M.emulsions) do
        if em.id == emulsionId then
            return em
        end
    end
    return nil
end

function M.findSensorProfile(sensorProfileId)
    local wanted = tostring(sensorProfileId or "")
    for _, entry in ipairs(M.sensorProfiles or {}) do
        if tostring(entry.id or "") == wanted then
            return entry
        end
    end
    return nil
end

function M.findBaseFamily(baseFamilyId)
    local wanted = tostring(baseFamilyId or "")
    for _, entry in ipairs(M.baseFamilies or {}) do
        if tostring(entry.id or "") == wanted then
            return entry
        end
    end
    return nil
end

function M.resolveSensorProfileId(sourceScaleId)
    local key = tostring(sourceScaleId or M.defaultSourceScaleId or "auto")
    local mapped = (M.sensorProfilesBySourceScale or {})[key]
    if mapped and M.findSensorProfile(mapped) then
        return mapped
    end
    local fallback = tostring(M.defaultSensorProfileId or "balanced_cmos")
    if M.findSensorProfile(fallback) then
        return fallback
    end
    return "balanced_cmos"
end

function M.resolveBaseFamilyId(baseFamilyId, sensorProfileId)
    local requested = tostring(baseFamilyId or M.defaultBaseFamilyId or "neutral_cmos")
    if requested == "auto_sensor" then
        return "neutral_cmos"
    end
    if requested ~= "auto_sensor" and M.findBaseFamily(requested) then
        return requested
    end
    return "neutral_cmos"
end

function M.getHeroPresets(panelId)
    local key = tostring(panelId or "")
    return (M.heroPresets or {})[key] or {}
end

function M.getModularPresetCount()
    return #(M.modularPresets or {})
end

return M
