--[[----------------------------------------------------------------------------
  A7InfoLRMenuItem.lua

  Einstiegspunkt fuer den Bibliotheksmenuepunkt "Sony-Aufnahmeinfos anzeigen...".
  Ruft a7infolr-cli fuer das ausgewaehlte Foto auf und zeigt Bild mit
  AF-Marker-Overlay plus strukturierte Infosektionen und
  MakerNotes-Kategorie-Reiter in einem modalen Dialog an.

  Navigation (Previous/Next): Der Dialog laeuft in einer repeat-Schleife
  und laedt jeweils das naechste Foto.

  Farbgebung: System-Standardfarben (kein dunkles Theme). Sektionskoepfe in
  Fettschrift, ShutterCount-Zeile ebenfalls fett hervorgehoben.
------------------------------------------------------------------------------]]

local LrApplication     = import 'LrApplication'
local LrBinding         = import 'LrBinding'
local LrColor           = import 'LrColor'
local LrDialogs         = import 'LrDialogs'
local LrFileUtils       = import 'LrFileUtils'
local LrFunctionContext = import 'LrFunctionContext'
local LrPrefs           = import 'LrPrefs'
local LrTasks           = import 'LrTasks'
local LrView            = import 'LrView'

local A7InfoLRCli       = require 'A7InfoLRCli'
local A7InfoLRDialog    = require 'A7InfoLRDialog'

local bind = LrView.bind

-- Akzentfarbe fuer Sektions-Ueberschriften (Amber statt Rot: neutraler in UI-Kontext,
-- gut lesbar auf hellem Hintergrund, fotografisch konnotiert).
local HEADER_COLOR = LrColor(0.85, 0.50, 0.10)

-- Breite des Info-Panels (rechte Spalte). buildCategoryPage verwendet
-- INFO_PANEL_WIDTH - 10 fuer die inneren scrolled_views damit kein
-- horizontales Scrollen auftritt (s. buildCategoryPage).
local INFO_PANEL_WIDTH = 400

-- Kategorie-Tabs folgen A7Info::TreeViewKategorien (CAMERA/FILE/.../GPS).
local CATEGORY_DEFS = {
    { key = "camera",    title = "Camera"   },
    { key = "file",      title = "File"     },
    { key = "picture",   title = "Picture"  },
    { key = "exposure",  title = "Exposure" },
    { key = "lens",      title = "Lens"     },
    { key = "autofocus", title = "AF"       },
    { key = "misc",      title = "Misc"     },
}

-- Scrollbare Spalte "Label: Wert" fuer eine MakerNotes-Kategorie.
-- fill_horizontal = 1 auf dem Wert-Text verhindert horizontales Scrollen:
-- der Wert fuellt den verfuegbaren Platz, zu lange Texte werden gekuerzt
-- anstatt die scrolled_view breiter zu machen.
-- Feste Pixelbreiten fuer Label- und Wert-Spalte in buildCategoryPage.
-- fill_horizontal = 1 funktioniert innerhalb eines scrolled_view nicht als
-- Breitenbegrenzung (die View erlaubt unbegrenzte horizontale Ausdehnung).
-- Feste Breiten garantieren: LABEL_W + spacing + VALUE_W < scrolled_view-Breite.
local LABEL_W = 155   -- ca. 20 Zeichen System-Font 13pt
local VALUE_W = 210   -- verbleibender Platz; Werte > 28 Zeichen werden gekuerzt

local function buildCategoryPage(f, fields, pageHeight)
    pageHeight = pageHeight or 210
    local rows = { spacing = f:control_spacing() / 2 }
    for _, field in ipairs(fields) do
        local value = field.value
        if type(value) == "string" and #value > 28 then
            value = string.sub(value, 1, 25) .. "..."
        end
        rows[#rows + 1] = f:row {
            spacing = f:label_spacing(),
            f:static_text {
                title = field.label .. ":",
                width = LABEL_W,
            },
            f:static_text {
                title     = value,
                alignment = "right",
                width     = VALUE_W,
            },
        }
    end
    return f:scrolled_view {
        width  = INFO_PANEL_WIDTH - 10,
        height = pageHeight,
        f:column(rows),
    }
end

-- GPS-Daten direkt aus LrPhoto (bereits von LR aufbereitet).
local function buildGpsFields(photo)
    local fields = {}
    local function add(label, value)
        if value ~= nil and value ~= "" then
            fields[#fields + 1] = { label = label, value = tostring(value) }
        end
    end
    add("Position",  photo:getFormattedMetadata("gps"))
    add("Altitude",  photo:getFormattedMetadata("gpsAltitude"))
    add("Direction", photo:getFormattedMetadata("gpsImgDirection"))
    return fields
end

-- Reiteransicht mit je einer Seite pro MakerNotes-Kategorie plus GPS.
-- pageHeight: Hoehe des scrolled_view je Seite (dynamisch aus verfuegbarem Platz).
local function buildCategoryTabs(f, info, photo, pageHeight)
    local categories = info.categories
    local tabItems = {}

    if type(categories) == "table" then
        for _, def in ipairs(CATEGORY_DEFS) do
            local fields = categories[def.key]
            if type(fields) == "table" and #fields > 0 then
                tabItems[#tabItems + 1] = f:tab_view_item {
                    title      = def.title,
                    identifier = def.key,
                    buildCategoryPage(f, fields, pageHeight),
                }
            end
        end
    end

    local gpsFields = buildGpsFields(photo)
    if #gpsFields > 0 then
        tabItems[#tabItems + 1] = f:tab_view_item {
            title      = "GPS",
            identifier = "gps",
            buildCategoryPage(f, gpsFields, pageHeight),
        }
    end

    if #tabItems < 1 then return nil end
    return f:tab_view(tabItems)
end

-- Persistente Anzeige-Umschalter.
local prefs = LrPrefs.prefsForPlugin(nil)
if prefs[A7InfoLRDialog.PREFS_SHOW_FOCUS_LOCATION] == nil then
    prefs[A7InfoLRDialog.PREFS_SHOW_FOCUS_LOCATION] = true
end
if prefs[A7InfoLRDialog.PREFS_SHOW_PHASE_AF] == nil then
    prefs[A7InfoLRDialog.PREFS_SHOW_PHASE_AF] = true
end
if prefs[A7InfoLRDialog.PREFS_SHOW_TRACKING_AF] == nil then
    prefs[A7InfoLRDialog.PREFS_SHOW_TRACKING_AF] = true
end

-- Marker-Umschalter (Checkboxen), an prefs gebunden.
local function buildVisibilityToggles(f)
    return f:row {
        bind_to_object = prefs,
        spacing = f:control_spacing(),
        f:checkbox {
            title = "Focus position",
            value = bind(A7InfoLRDialog.PREFS_SHOW_FOCUS_LOCATION),
        },
        f:checkbox {
            title = "Phase-AF points",
            value = bind(A7InfoLRDialog.PREFS_SHOW_PHASE_AF),
        },
        f:checkbox {
            title = "Tracking/Eye AF",
            value = bind(A7InfoLRDialog.PREFS_SHOW_TRACKING_AF),
        },
    }
end

-- Felder, die in "Main Data" nicht angezeigt werden (erscheinen aber weiterhin
-- im Reiter "Camera" unter "Complete Data").
local MAIN_DATA_SKIP = {
    BatteryLevel              = true,
    BatteryTemperature        = true,
    AmbientTemperature        = true,
    ElectronicFrontCurtainShutter = true,
}

-- Main Information: Kamera- und AF-Felder als eine einzige flache column.
-- width_in_chars = 18 deckt den laengsten Label ("ImageStabilization") ab und
-- haelt die Wertespalte breit genug, ohne horizontalen Scroll zu erzwingen.
local function buildInfoView(f, photo, info)
    local col = { spacing = f:control_spacing() / 2 }

    local function addRow(label, value, bold)
        if value == nil or value == "" then return end
        local fontSpec = bold and "<system/bold>" or nil
        col[#col + 1] = f:row {
            spacing         = f:label_spacing(),
            fill_horizontal = 1,
            f:static_text {
                title          = label .. ":",
                font           = fontSpec,
                width_in_chars = 18,
            },
            f:static_text {
                title           = tostring(value),
                font            = fontSpec,
                alignment       = "right",
                fill_horizontal = 1,
            },
        }
    end

    local camFields = info and info.categories and info.categories.camera
    if type(camFields) == "table" and #camFields > 0 then
        for _, field in ipairs(camFields) do
            if not MAIN_DATA_SKIP[field.label] then
                local bold = (field.label == "SonyModelID" or field.label == "ShutterCount")
                addRow(field.label, field.value, bold)
            end
        end
    end

    local focus = info and info.focus
    if focus then
        col[#col + 1] = f:spacer { height = f:control_spacing() }
        addRow("Focus Mode",   focus.focusMode)
        addRow("AF Area Mode", focus.afAreaMode)
        addRow("AF Tracking",  focus.afTracking)
        local points = focus.focalPlaneAFPointsUsed
        if type(points) == "table" and #points > 0 then
            addRow("Active AF points", #points)
        end
    end

    return f:column(col)
end

-- Navigations-Panel (accessoryView): Previous / Dateiname / Next.
-- Wird einmal erzeugt und bei jedem Dialog-Aufruf wiederverwendet.
-- props.fileName wird per LrView.bind live aktualisiert.
local UTF_RIGHT = string.char(0xe2, 0x96, 0xb6)
local UTF_LEFT  = string.char(0xe2, 0x97, 0x80)

local function buildNavPanel(f, selectedPhotos, props, LR5)
    return f:row {
        spacing = 0,
        f:push_button {
            title   = UTF_LEFT .. " Previous image",
            enabled = #selectedPhotos > 1 or not LR5,
            action  = function(button)
                LrDialogs.stopModalWithResult(button, "previous")
            end,
        },
        f:spacer { width = 10 },
        f:static_text {
            title = bind { key = "fileName", object = props },
        },
        f:spacer { width = 10 },
        f:push_button {
            title   = "Next image " .. UTF_RIGHT,
            enabled = #selectedPhotos > 1 or not LR5,
            action  = function(button)
                LrDialogs.stopModalWithResult(button, "next")
            end,
        },
    }
end

-- LRU-Cache fuer CLI-Aufrufe: verhindert wiederholte a7infolr-cli-Aufrufe
-- bei Previous/Next-Navigation. Overlay-Tempfiles werden erst beim Verdraengen
-- aus dem Cache geloescht, nicht nach jedem Dialog-Schliessen.
local CACHE_MAX = 4
local cache = {}  -- { key, info, overlayPath }[], Index 1 = zuletzt genutzt

local function cacheKey(path, w, h, cl, ct, cr, cb)
    return string.format("%s|%dx%d|%.6f,%.6f,%.6f,%.6f", path, w, h, cl, ct, cr, cb)
end

local function cacheGet(key)
    for i, e in ipairs(cache) do
        if e.key == key then
            if i > 1 then table.remove(cache, i); table.insert(cache, 1, e) end
            return e.info
        end
    end
end

local function cachePut(key, info, overlayPath)
    for i, e in ipairs(cache) do
        if e.key == key then table.remove(cache, i); break end
    end
    while #cache >= CACHE_MAX do
        local evicted = table.remove(cache)
        if evicted.overlayPath and LrFileUtils.exists(evicted.overlayPath) then
            LrFileUtils.delete(evicted.overlayPath)
        end
    end
    table.insert(cache, 1, { key = key, info = info, overlayPath = overlayPath })
end

local function showA7InfoLR()
    LrFunctionContext.callWithContext("showA7InfoLR", function(context)

        local catalog        = LrApplication.activeCatalog()
        local targetPhoto    = catalog:getTargetPhoto()
        local selectedPhotos = catalog:getTargetPhotos()

        if not targetPhoto then
            LrDialogs.message("Please select a photo first.")
            return
        end

        local LR5 = (LrApplication.versionTable().major == 5)
        local LrSelection
        if not LR5 then LrSelection = import "LrSelection" end

        -- Aktuellen Index in der Auswahl ermitteln.
        local current = 1
        for i, photo in ipairs(selectedPhotos) do
            if photo == targetPhoto then current = i; break end
        end

        local function nextPhoto()
            if #selectedPhotos > 1 then
                current     = (current % #selectedPhotos) + 1
                targetPhoto = selectedPhotos[current]
            elseif not LR5 then
                LrSelection.nextPhoto()
                targetPhoto = catalog:getTargetPhoto()
            end
        end

        local function previousPhoto()
            if #selectedPhotos > 1 then
                current     = (current - 2) % #selectedPhotos + 1
                targetPhoto = selectedPhotos[current]
            elseif not LR5 then
                LrSelection.previousPhoto()
                targetPhoto = catalog:getTargetPhoto()
            end
        end

        local props    = LrBinding.makePropertyTable(context)
        props.fileName = ""

        local f            = LrView.osFactory()
        local controlPanel = buildNavPanel(f, selectedPhotos, props, LR5)

        local exitPlugin = false
        repeat
            local photo = targetPhoto

            props.fileName = photo:getFormattedMetadata("fileName")
            if #selectedPhotos > 1 then
                props.fileName = props.fileName
                    .. " (" .. current .. "/" .. #selectedPhotos .. ")"
            end

            local path = photo:getRawMetadata("path")
            local displayWidth, displayHeight, cropLeft, cropTop, cropRight, cropBottom =
                A7InfoLRDialog.computeDisplaySizeAndCrop(photo)

            -- Hoehe des Info-Panels: passt sich ans Bildformat an (mind. 300px).
            local INFO_PANEL_HEIGHT = math.max(300, displayHeight + 20)

            local key = cacheKey(path, displayWidth, displayHeight, cropLeft, cropTop, cropRight, cropBottom)
            local info, errorMessage = cacheGet(key), nil

            if not info then
                -- Ladefortschritts-Dialog waehrend a7infolr-cli laeuft.
                LrFunctionContext.callWithContext("innerContext", function(dialogContext)
                    local dialogScope = LrDialogs.showModalProgressDialog {
                        title           = "A7InfoLR",
                        caption         = photo:getFormattedMetadata("fileName"),
                        width           = 300,
                        cannotCancel    = false,
                        functionContext = dialogContext,
                    }
                    dialogScope:setIndeterminate()
                    info, errorMessage = A7InfoLRCli.readInfo(path, "full",
                        { width = displayWidth, height = displayHeight },
                        { left = cropLeft, top = cropTop, right = cropRight, bottom = cropBottom })
                end)
                LrTasks.sleep(0.02)

                if info then
                    local ov = info.focus and info.focus.trackingOverlay
                    cachePut(key, info, (type(ov) == "table") and ov.image or nil)
                end
            end

            if not info then
                local userChoice = LrDialogs.confirm(
                    errorMessage or "Unknown error",
                    photo:getFormattedMetadata("fileName"),
                    "Continue", "Stop")
                if userChoice == "cancel" then
                    exitPlugin = true
                else
                    nextPhoto()
                end
            else
                local photoView, markerTypes =
                    A7InfoLRDialog.createPhotoView(f, photo, info.focus, prefs)

                -- Linke Spalte: Bild + Legende (wenn Marker vorhanden) + Umschalter.
                -- buildLegend liefert nil wenn keine Marker erkannt wurden (z.B. MF),
                -- dann faellt die Legende einfach weg ohne leere Luecke.
                local legend = A7InfoLRDialog.buildLegend(f, markerTypes, prefs)
                local photoColumn = legend
                    and { photoView, legend, buildVisibilityToggles(f) }
                    or  { photoView, buildVisibilityToggles(f) }
                photoColumn.spacing = f:control_spacing()

                -- Rechte Spalte: Main Information (rahmenlos, fett + Separator-Linie)
                -- + Exif/MakerNotes (selber Stil, mit Tab-Ansicht, extra Abstand oben).
                -- Kein group_box-Rahmen - Stil analog zum Referenz-Screenshot.
                -- categoryPageHeight: INFO_PANEL_HEIGHT abzueglich geschaetzter
                -- Main-Information-Hoehe (~220px) + Abstand (~8px) + Tab-Chrome (~70px).
                local categoryPageHeight = math.max(150, INFO_PANEL_HEIGHT - 298)

                local mainSection = f:column {
                    spacing = f:control_spacing() / 2,
                    f:static_text {
                        title      = "Main Information",
                        font       = "<system/bold>",
                        text_color = HEADER_COLOR,
                    },
                    f:separator {},
                    buildInfoView(f, photo, info),
                }

                local categoryTabs = buildCategoryTabs(f, info, photo, categoryPageHeight)
                local completeSection = categoryTabs and f:column {
                    spacing = f:control_spacing() / 2,
                    f:spacer { height = f:control_spacing() },
                    f:static_text {
                        title      = "Exif/MakerNotes",
                        font       = "<system/bold>",
                        text_color = HEADER_COLOR,
                    },
                    f:separator {},
                    categoryTabs,
                }

                local rightItems = {
                    spacing = f:control_spacing(),
                    width   = INFO_PANEL_WIDTH,
                    mainSection,
                }
                if completeSection then
                    rightItems[#rightItems + 1] = completeSection
                end

                local infoPanel = f:column(rightItems)

                local contents = f:row {
                    spacing = f:control_spacing(),
                    f:column(photoColumn),
                    infoPanel,
                }

                local userResponse = LrDialogs.presentModalDialog {
                    title         = "A7InfoLR 0.1 – 2026 by Wolfram Soens",
                    contents      = contents,
                    accessoryView = controlPanel,
                    actionVerb    = "Close",
                    cancelVerb    = "< exclude >",
                }

                if userResponse == "next" then
                    nextPhoto()
                elseif userResponse == "previous" then
                    previousPhoto()
                end

                exitPlugin = (userResponse == "ok") or (userResponse == "cancel")
            end
        until exitPlugin

    end)
end

LrTasks.startAsyncTask(showA7InfoLR)
