#ifndef DIRECTORY_WALKER_H
#define DIRECTORY_WALKER_H

#include <Arduino.h>
#include <vector>
#include "DirectoryNode.h"

struct WalkerState {
    const DirectoryNode* node;
    uint8_t phase; // 0: Start, 1: Files, 2: Subdirs, 3: End
    size_t idx;    // Index for vectors
    
    WalkerState(const DirectoryNode* n) : node(n), phase(0), idx(0) {}
};

class DirectoryWalker {
private:
    std::vector<WalkerState> stack;
    String pending;
    size_t pendingOffset;

    void generateNext() {
        if (stack.empty()) return;

        WalkerState& state = stack.back();
        const DirectoryNode* node = state.node;

        switch (state.phase) {
            case 0: // Start
                if (node->getName() == "/") {
                    pending += F("<ul>\r\n");
                } else {
                    pending += F("<li data-id=\"");
                    pending += String(node->getId());
                    pending += F("\"><b>");
                    pending += node->getName();
                    pending += F("</b></li>\r\n");
                }
                state.phase = 1;
                state.idx = 0;
                break;

            case 1: // Files
                if (state.idx < node->getMP3Files().size()) {
                    pending += F("<li data-id=\"");
                    pending += String(node->getFileIdAt(state.idx));
                    pending += F("\">");
                    pending += node->getMP3Files()[state.idx];
                    pending += F("</li>\r\n");
                    state.idx++;
                } else {
                    state.phase = 2;
                    state.idx = 0;
                }
                break;

            case 2: // Subdirs
                if (state.idx < node->getSubdirectories().size()) {
                    // Push child
                    const DirectoryNode* child = node->getSubdirectories()[state.idx];
                    state.idx++; // Advance index for when we return
                    stack.emplace_back(child);
                    // Next loop will process the child (Phase 0)
                } else {
                    state.phase = 3;
                }
                break;

            case 3: // End
                if (node->getName() == "/") {
                    pending += F("</ul>\r\n");
                }
                stack.pop_back();
                break;
        }
    }

public:
    DirectoryWalker(const DirectoryNode* root) : pendingOffset(0) {
        if (root) {
            stack.emplace_back(root);
            // Reserve some space for pending string to avoid frequent reallocations
            pending.reserve(256);
        }
    }

    size_t read(uint8_t* buffer, size_t maxLen) {
        size_t written = 0;

        while (written < maxLen) {
            // If pending buffer is empty or fully consumed, generate more
            if (pending.length() == 0 || pendingOffset >= pending.length()) {
                pending = ""; // Reset string content (capacity is kept)
                pendingOffset = 0;
                
                if (stack.empty()) {
                    break; // Done
                }
                generateNext();
            }

            // Copy from pending to output buffer
            if (pending.length() > pendingOffset) {
                size_t available = pending.length() - pendingOffset;
                size_t toCopy = std::min(available, maxLen - written);
                memcpy(buffer + written, pending.c_str() + pendingOffset, toCopy);
                written += toCopy;
                pendingOffset += toCopy;
            }
        }
        return written;
    }
};

#endif
