-- Rykord - ImGui Media Explorer

-- folders are represented as tree nodes
-- items are selectables
-- play functionality with volume knob and autoplay
-- drag and drop functionalities
    -- drop to the arrange view (priority)
    -- drop to the media explorer
-- search bar functionality

-- Store the reference to the currently playing preview
local current_preview = nil

-- Variables for search functionality
local search_query = ""
local last_search_query = ""
local search_cache = {}
local debounce_timer = 0

-- Function to check if a directory contains any matching files
function directory_contains_matching_files(path)
    if search_cache[path] ~= nil then
        return search_cache[path]
    end

    local idx = 0
    local contains_matches = false
    while true do
        local file = reaper.EnumerateFiles(path, idx)
        if not file then break end

        -- Ignore hidden files and directories
        if not file:match("^%.") then
            -- Check if the file matches the search query
            if (file:match("%.wav$") or file:match("%.mp3$")) and (search_query == "" or file:lower():find(search_query:lower(), 1, true)) then
                contains_matches = true
                break
            end
        end

        idx = idx + 1
    end

    if not contains_matches then
        idx = 0
        while true do
            local subdir = reaper.EnumerateSubdirectories(path, idx)
            if not subdir then break end

            -- Ignore hidden files and directories
            if not subdir:match("^%.") then
                local full_path = path .. "/" .. subdir
                if directory_contains_matching_files(full_path) then
                    contains_matches = true
                    break
                end
            end

            idx = idx + 1
        end
    end

    search_cache[path] = contains_matches
    return contains_matches
end

-- Function to create a directory tree and search for files recursively
function create_directory_tree(path, depth)
    if depth > 5 then return end  -- Limit search depth

    -- Enumerate subdirectories
    local idx = 0
    while true do
        local subdir = reaper.EnumerateSubdirectories(path, idx)
        if not subdir then break end

        -- Ignore hidden files and directories
        if not subdir:match("^%.") then
            local full_path = path .. "/" .. subdir

            -- Check if the subdir contains matching files
            if directory_contains_matching_files(full_path) then
                if reaper.ImGui_TreeNode(ctx, subdir) then
                    create_directory_tree(full_path, depth + 1)
                    reaper.ImGui_TreePop(ctx)
                end
            end
        end

        idx = idx + 1
    end

    -- Enumerate files
    idx = 0
    while true do
        local file = reaper.EnumerateFiles(path, idx)
        if not file then break end

        -- Ignore hidden files and directories
        if not file:match("^%.") then
            -- Check if the file matches the search query
            if (file:match("%.wav$") or file:match("%.mp3$")) and (search_query == "" or file:lower():find(search_query:lower(), 1, true)) then
                if reaper.ImGui_Selectable(ctx, file) then
                    -- Preview the selected file
                    preview_file(path .. "/" .. file)
                end

                -- Begin drag and drop source
                if reaper.ImGui_BeginDragDropSource(ctx) then
                    reaper.ImGui_SetDragDropPayload(ctx, "FILE_PATH", path .. "/" .. file)
                    reaper.ImGui_Text(ctx, "Dragging " .. file)
                    reaper.ImGui_EndDragDropSource(ctx)
                end
            end
        end

        idx = idx + 1
    end
end

-- Function to preview a selected file within Reaper
function preview_file(file)
    -- Stop any currently playing preview
    if current_preview then
        reaper.CF_Preview_StopAll()
        current_preview = nil
    end

    -- Create a PCM_Source from the file
    local source = reaper.PCM_Source_CreateFromFile(file)

    -- Create a preview object
    current_preview = reaper.CF_CreatePreview(source)

    -- Start the preview
    local play_success = reaper.CF_Preview_Play(current_preview)
    if not play_success then
        reaper.ShowMessageBox("Failed to play preview: " .. file, "Error", 0)
    end
end

-- Function to handle drag and drop from Reaper
function handle_reaper_drag_drop()
    -- Get the number of media items selected in Reaper
    local count = reaper.CountSelectedMediaItems(0)
    if count == 0 then return end

    -- Iterate over selected items
    for i = 0, count - 1 do
        local item = reaper.GetSelectedMediaItem(0, i)
        local take = reaper.GetActiveTake(item)
        if take then
            local source = reaper.GetMediaItemTake_Source(take)
            local file_path = reaper.GetMediaSourceFileName(source, "")
            if file_path then
                -- Begin drag and drop target
                if reaper.ImGui_BeginDragDropTarget(ctx) then
                    local payload = reaper.ImGui_AcceptDragDropPayload(ctx, "FILE_PATH")
                    if payload then
                        -- Handle the dropped file
                        reaper.ShowMessageBox("Dropped file: " .. file_path, "Info", 0)
                    end
                    reaper.ImGui_EndDragDropTarget(ctx)
                end
            end
        end
    end
end

-- Function to handle file drops in the arrange view
function handle_arrange_view_drop(file)
    -- Get the position where the file is dropped
    local cursor_pos = reaper.GetCursorPosition()
    local track_count = reaper.CountTracks(0)
    if track_count == 0 then
        -- Create a new track if there are no tracks
        reaper.InsertTrackAtIndex(0, true)
        track_count = 1
    end

    -- Get the first track (or the last created track)
    local track = reaper.GetTrack(0, track_count > 0 and track_count - 1 or 0)
    if not track then return end

    -- Insert the media item at the drop position
    reaper.PreventUIRefresh(1)
    reaper.Undo_BeginBlock()
    local item = reaper.AddMediaItemToTrack(track)
    reaper.SetMediaItemPosition(item, cursor_pos, false)
    local take = reaper.AddTakeToMediaItem(item)
    reaper.BR_SetTakeSourceFromFile(take, file, false)
    reaper.UpdateArrange()
    reaper.Undo_EndBlock("Insert media item from ImGui drag and drop", -1)
    reaper.PreventUIRefresh(-1)
end

function main()
    reaper.ImGui_SetNextWindowDockID(ctx, -7)
    local retval, is_open = reaper.ImGui_Begin(ctx, "Media Explorer", true)

    if is_open then
        -- Search input box
        local changed, text = reaper.ImGui_InputText(ctx, "Search", search_query, 256)
        if changed then
            search_query = text
            debounce_timer = reaper.time_precise() + 0.3  -- 300 ms debounce
        end

        if reaper.time_precise() > debounce_timer then
            if search_query ~= last_search_query then
                search_cache = {}  -- Clear cache when the search query changes
                last_search_query = search_query
            end

            -- Call the function to create the directory tree and search for files
            create_directory_tree("E:\\Sample Packs\\COLLECTION", 0)
        end

        -- Handle drag and drop from Reaper
        handle_reaper_drag_drop()

        -- Handle drag and drop target (arrange view)
        if reaper.ImGui_BeginDragDropTarget(ctx) then
            local payload = reaper.ImGui_AcceptDragDropPayload(ctx, "FILE_PATH")
            if payload then
                local dropped_file = reaper.ImGui_GetDragDropPayload(ctx)
                if dropped_file then
                    reaper.ShowMessageBox("Dropped file: " .. dropped_file, "Info", 0)
                    handle_arrange_view_drop(dropped_file)
                else
                    reaper.ShowMessageBox("Payload was accepted but file path is nil", "Error", 0)
                end
            end
            reaper.ImGui_EndDragDropTarget(ctx)
        end

        reaper.defer(main)
    end

    reaper.ImGui_End(ctx)
end

ctx = reaper.ImGui_CreateContext("ctx")
main()