FilesApp.c
//
//  FilesApp.c
//
//
//  Created by Chris Galzerano on 2/17/26.
//

#include "FilesApp.h"

//
//  SystemApps.c
//  Files App - Table View Implementation
//

#include <sys/stat.h>
#include <dirent.h>
#include <time.h>
#include "esp_vfs_fat.h"

// --- Globals ---
FileSortMode fileSortMode = SORT_MODE_NAME;
bool fileSortAscending = true;
CCView* uiFilesHeader = NULL;

// Re-declaring globals from previous context
char currentPath[512]= "/";
CCArray* currentFileList= NULL;
int filesPageIndex = 0;
//CCView* mainWindowView= NULL;
CCView* uiFilesContextMenu= NULL;

char clipboardPath[512] = {0};
bool isCutOperation = false;
CCView* uiFilesProperties = NULL;

CCView* uiNewFolderDialog = NULL;
CCLabel* uiNewFolderInput = NULL;
CCArray* uiDynamicPageViews = NULL;

// --- Constants ---
#define COL_NAME_WIDTH 170
#define COL_DATE_WIDTH 90
#define COL_SIZE_WIDTH 60
#define ROW_HEIGHT 40

// --- Helpers ---

// Long Press State
static uint64_t touchStartTime = 0;
static int touchStartX = 0;
static int touchStartY = 0;
static char selectedFilename[128] = {0};
static bool is_pressing = false;
static bool long_press_fired = false;

// --- Helper: Detect Archives ---
bool is_archive_file(const char* filename) {
    const char* ext = strrchr(filename, '.');
    if (!ext) return false;
    if (strcasecmp(ext, ".zip") == 0 ||
        strcasecmp(ext, ".tar") == 0 ||
        strcasecmp(ext, ".gz") == 0 ||
        strcasecmp(ext, ".tar.gz") == 0) {
        return true;
    }
    return false;
}


static bool is_root_mode() {
    return (strcmp(currentPath, "/") == 0);
}

void format_file_size(long bytes, char* buffer, size_t bufSize) {
    if (bytes < 1024) {
        snprintf(buffer, bufSize, "%ld B", bytes);
    } else if (bytes < 1024 * 1024) {
        snprintf(buffer, bufSize, "%.1f KB", bytes / 1024.0);
    } else {
        snprintf(buffer, bufSize, "%.1f MB", bytes / (1024.0 * 1024.0));
    }
}

int compare_file_items(CCDictionary* dictA, CCDictionary* dictB) {
    CCString* keyName = ccs("name");
    CCString* keyDate = ccs("time_t");
    CCString* keySize = ccs("size_bytes");
    
    CCString* nameA = (CCString*)dictionaryObjectForKey(dictA, keyName);
    CCString* nameB = (CCString*)dictionaryObjectForKey(dictB, keyName);
    CCNumber* sizeA = (CCNumber*)dictionaryObjectForKey(dictA, keySize);
    CCNumber* sizeB = (CCNumber*)dictionaryObjectForKey(dictB, keySize);
    CCNumber* timeA = (CCNumber*)dictionaryObjectForKey(dictA, keyDate);
    CCNumber* timeB = (CCNumber*)dictionaryObjectForKey(dictB, keyDate);
    
    freeCCString(keyName);
    freeCCString(keyDate);
    freeCCString(keySize);
    
    int result = 0;
    
    switch (fileSortMode) {
        case SORT_MODE_NAME:
            // FIXED: Using cStringOfString
            result = strcmp(cStringOfString(nameA), cStringOfString(nameB));
            break;
        case SORT_MODE_SIZE: {
            double valA = numberDoubleValue(sizeA);
            double valB = numberDoubleValue(sizeB);
            if (valA > valB) result = 1;
            else if (valA < valB) result = -1;
            break;
        }
        case SORT_MODE_DATE: {
            double valA = numberDoubleValue(timeA);
            double valB = numberDoubleValue(timeB);
            if (valA > valB) result = 1;
            else if (valA < valB) result = -1;
            break;
        }
    }
    
    return fileSortAscending ? result : -result;
}

void sort_file_list() {
    if (!currentFileList || currentFileList->count < 2) return;
    
    // Bubble Sort for safe array access
    for (int i = 0; i < currentFileList->count - 1; i++) {
        for (int j = 0; j < currentFileList->count - i - 1; j++) {
            CCDictionary* a = (CCDictionary*)arrayObjectAtIndex(currentFileList, j);
            CCDictionary* b = (CCDictionary*)arrayObjectAtIndex(currentFileList, j + 1);
            
            if (compare_file_items(a, b) > 0) {
                arrayDeleteObjectAtIndex(currentFileList, j);
                arrayInsertObjectAtIndex(currentFileList, a, j + 1);
            }
        }
    }
}

// --- Directory Scanning ---

void scan_current_directory() {
    if (currentFileList) {
        currentFileList = NULL; // Assuming close_current_app already handles the freeing
    }
    
    currentFileList = array();
    
    if (is_root_mode()) {
        CCDictionary* d1 = dictionary();
        // BOTH Values and Keys must be copied to the heap so freeElement() doesn't crash on ROM literals
        dictionarySetObjectForKey(d1, copyCCString(ccs("Internal Storage")), copyCCString(ccs("name")));
        dictionarySetObjectForKey(d1, copyCCString(ccs("-")), copyCCString(ccs("date")));
        dictionarySetObjectForKey(d1, copyCCString(ccs("-")), copyCCString(ccs("size")));
        dictionarySetObjectForKey(d1, numberWithInt(1), copyCCString(ccs("is_dir")));
        dictionarySetObjectForKey(d1, numberWithInt(0), copyCCString(ccs("size_bytes")));
        dictionarySetObjectForKey(d1, numberWithInt(0), copyCCString(ccs("time_t")));
        arrayAddObject(currentFileList, d1);
        
        CCDictionary* d2 = dictionary();
        dictionarySetObjectForKey(d2, copyCCString(ccs("SD Card")), copyCCString(ccs("name")));
        dictionarySetObjectForKey(d2, copyCCString(ccs("-")), copyCCString(ccs("date")));
        dictionarySetObjectForKey(d2, copyCCString(ccs("-")), copyCCString(ccs("size")));
        dictionarySetObjectForKey(d2, numberWithInt(1), copyCCString(ccs("is_dir")));
        dictionarySetObjectForKey(d2, numberWithInt(0), copyCCString(ccs("size_bytes")));
        dictionarySetObjectForKey(d2, numberWithInt(0), copyCCString(ccs("time_t")));
        arrayAddObject(currentFileList, d2);
        return;
    }
    
    DIR *d = opendir(currentPath);
    if (d) {
        struct dirent *dir;
        while ((dir = readdir(d)) != NULL) {
            if (dir->d_name[0] == '.') continue;
            
            char fullPath[512];
            snprintf(fullPath, sizeof(fullPath), "%s/%s", currentPath, dir->d_name);
            
            struct stat st;
            stat(fullPath, &st);
            
            CCDictionary* item = dictionary();
            
            // Heap-allocate name and key
            dictionarySetObjectForKey(item, copyCCString(ccs(dir->d_name)), copyCCString(ccs("name")));
            
            bool isDir = S_ISDIR(st.st_mode);
            // CCNumber is already dynamically allocated by numberWithInt, but the key needs copying
            dictionarySetObjectForKey(item, numberWithInt(isDir ? 1 : 0), copyCCString(ccs("is_dir")));
            
            char sizeBuf[32];
            if (isDir) strcpy(sizeBuf, "--");
            else format_file_size(st.st_size, sizeBuf, 32);
            
            // Heap-allocate size and key
            dictionarySetObjectForKey(item, copyCCString(ccs(sizeBuf)), copyCCString(ccs("size")));
            dictionarySetObjectForKey(item, numberWithDouble((double)st.st_size), copyCCString(ccs("size_bytes")));
            
            struct tm *tm_info = localtime(&st.st_mtime);
            char dateBuf[32];
            strftime(dateBuf, 32, "%m/%d/%y", tm_info);
            
            // Heap-allocate date and key
            dictionarySetObjectForKey(item, copyCCString(ccs(dateBuf)), copyCCString(ccs("date")));
            dictionarySetObjectForKey(item, numberWithDouble((double)st.st_mtime), copyCCString(ccs("time_t")));
            
            arrayAddObject(currentFileList, item);
        }
        closedir(d);
        sort_file_list();
    }
}


// --- Context Menu Logic ---

void show_context_menu(const char* filename) {
    if (uiFilesContextMenu) {
        uiFilesContextMenu = NULL;
    }
    
    uiFilesContextMenu = viewWithFrame(ccRect(40, 40, 240, 400));
    uiFilesContextMenu->backgroundColor = color(0.14, 0.14, 0.14, 1.0);
    
    CCLabel* title = labelWithFrame(ccRect(10, 10, 220, 20));
    title->text = copyCCString(ccs(filename));
    title->fontSize = 16;
    title->textColor = color(1.0, 1.0, 0.0, 1.0);
    viewAddSubview(uiFilesContextMenu, title);
    
    // Build options dynamically
    const char* options[10];
    int tags[10];
    int count = 0;
    
    options[count] = "Open";       tags[count++] = TAG_CTX_OPEN;
    options[count] = "Copy";       tags[count++] = TAG_CTX_COPY;
    
    // Only show Paste if something is on the clipboard
    if (strlen(clipboardPath) > 0) {
        options[count] = "Paste";  tags[count++] = TAG_CTX_PASTE;
    }
    
    options[count] = "Properties"; tags[count++] = TAG_CTX_PROPS;
    options[count] = "New Folder"; tags[count++] = TAG_CTX_NEW;
    
    // Check if it's an archive
    if (is_archive_file(filename)) {
        options[count] = "Decompress"; tags[count++] = TAG_CTX_DECOMPRESS;
    } else {
        options[count] = "Compress";   tags[count++] = TAG_CTX_COMPRESS;
    }
    
    options[count] = "Cancel";     tags[count++] = TAG_CTX_CANCEL;
    
    // Render the dynamic list
    int y = 40;
    for (int i = 0; i < count; i++) {
        CCView* btn = viewWithFrame(ccRect(10, y, 220, 35));
        btn->backgroundColor = color(0.24, 0.24, 0.24, 1.0);
        btn->tag = tags[i];
        
        CCLabel* l = labelWithFrame(ccRect(10, 8, 200, 20));
        l->text = copyCCString(ccs(options[i]));
        l->fontSize = 16;
        l->textColor = color(1.0, 1.0, 1.0, 1.0);
        viewAddSubview(btn, l);
        
        viewAddSubview(uiFilesContextMenu, btn);
        y += 40;
    }
    
    // Adjust container height to fit the dynamic number of buttons
    uiFilesContextMenu->frame->size->height = y + 10;
    
    viewAddSubview(mainWindowView, uiFilesContextMenu);
}

void show_directory_context_menu(void) {
    if (uiFilesContextMenu) uiFilesContextMenu = NULL;
    
    uiFilesContextMenu = viewWithFrame(ccRect(40, 100, 240, 200));
    uiFilesContextMenu->backgroundColor = color(0.14, 0.14, 0.14, 1.0);
    
    CCLabel* title = labelWithFrame(ccRect(10, 10, 220, 20));
    title->text = copyCCString(ccs("Current Folder"));
    title->fontSize = 16;
    title->textColor = color(1.0, 1.0, 0.0, 1.0);
    viewAddSubview(uiFilesContextMenu, title);
    
    const char* options[5];
    int tags[5];
    int count = 0;
    
    // Only show paste if clipboard has data
    if (strlen(clipboardPath) > 0) {
        options[count] = "Paste";  tags[count++] = TAG_CTX_PASTE;
    }
    options[count] = "New Folder"; tags[count++] = TAG_CTX_NEW;
    options[count] = "Properties"; tags[count++] = TAG_CTX_PROPS;
    options[count] = "Cancel";     tags[count++] = TAG_CTX_CANCEL;
    
    int y = 40;
    for (int i = 0; i < count; i++) {
        CCView* btn = viewWithFrame(ccRect(10, y, 220, 35));
        btn->backgroundColor = color(0.24, 0.24, 0.24, 1.0);
        btn->tag = tags[i];
        
        CCLabel* l = labelWithFrame(ccRect(10, 8, 200, 20));
        l->text = copyCCString(ccs(options[i]));
        l->fontSize = 16;
        l->textColor = color(1.0, 1.0, 1.0, 1.0);
        viewAddSubview(btn, l);
        
        viewAddSubview(uiFilesContextMenu, btn);
        y += 40;
    }
    
    uiFilesContextMenu->frame->size->height = y + 10;
    viewAddSubview(mainWindowView, uiFilesContextMenu);
}

void show_new_folder_dialog(void) {
    if (uiNewFolderDialog) uiNewFolderDialog = NULL;
    
    // Placed near the top (y=30) so the keyboard has room below it
    uiNewFolderDialog = viewWithFrame(ccRect(20, 30, 280, 130));
    uiNewFolderDialog->backgroundColor = color(0.18, 0.18, 0.18, 1.0); // Dark Gray
    
    // Title
    CCLabel* title = labelWithFrame(ccRect(10, 10, 260, 20));
    title->text = copyCCString(ccs("Create New Folder"));
    title->fontSize = 16;
    title->textColor = color(1.0, 1.0, 1.0, 1.0);
    viewAddSubview(uiNewFolderDialog, title);
    
    // Pseudo-Text Field (The Target Label)
    uiNewFolderInput = labelWithFrame(ccRect(10, 40, 260, 30));
    uiNewFolderInput->text = copyCCString(ccs("New_Folder")); // Default placeholder
    uiNewFolderInput->fontSize = 16;
    uiNewFolderInput->textColor = color(0.0, 0.0, 0.0, 1.0); // Black text so it's readable
    uiNewFolderInput->textVerticalAlignment = CCTextVerticalAlignmentCenter;
    
    // Give it a white background so it visually looks like an input box
    uiNewFolderInput->view->backgroundColor = color(1.0, 1.0, 1.0, 1.0);
    viewAddSubview(uiNewFolderDialog, uiNewFolderInput);
    
    // Cancel Button
    CCView* btnCancel = viewWithFrame(ccRect(10, 85, 120, 35));
    btnCancel->backgroundColor = color(0.4, 0.4, 0.4, 1.0); // Gray
    btnCancel->tag = TAG_NEW_FOLDER_CANCEL;
    
    CCLabel* lblCancel = labelWithFrame(ccRect(0, 8, 120, 20));
    lblCancel->text = copyCCString(ccs("Cancel"));
    lblCancel->fontSize = 16;
    lblCancel->textAlignment = CCTextAlignmentCenter;
    lblCancel->textColor = color(1.0, 1.0, 1.0, 1.0);
    viewAddSubview(btnCancel, lblCancel);
    viewAddSubview(uiNewFolderDialog, btnCancel);
    
    // Create Button
    CCView* btnCreate = viewWithFrame(ccRect(150, 85, 120, 35));
    btnCreate->backgroundColor = color(0.0, 0.47, 1.0, 1.0); // Blue
    btnCreate->tag = TAG_NEW_FOLDER_CREATE;
    
    CCLabel* lblCreate = labelWithFrame(ccRect(0, 8, 120, 20));
    lblCreate->text = copyCCString(ccs("Create"));
    lblCreate->fontSize = 16;
    lblCreate->textAlignment = CCTextAlignmentCenter;
    lblCreate->textColor = color(1.0, 1.0, 1.0, 1.0);
    viewAddSubview(btnCreate, lblCreate);
    viewAddSubview(uiNewFolderDialog, btnCreate);
    
    viewAddSubview(mainWindowView, uiNewFolderDialog);
    
    // Summon the OS Keyboard and point it at our Label!
    setup_keyboard_ui(uiNewFolderInput);
}

void drawFileTableView(void) {
    // --- 1. TARGETED CLEANUP ---
    if (uiDynamicPageViews) {
        for (int i = 0; i < uiDynamicPageViews->count; i++) {
            CCView* oldView = (CCView*)arrayObjectAtIndex(uiDynamicPageViews, i);
            
            // Unlink from the main window so the graphics engine stops rendering it
            viewRemoveFromSuperview(oldView);
            
            // CRITICAL: You must still free the memory, otherwise the detached views leak!
            freeViewHierarchy(oldView);
        }
        freeElement(uiDynamicPageViews); // Delete the tracking array itself
        uiDynamicPageViews = NULL;
    }
    
    // --- 2. BUILD NEW PAGE ---
    uiDynamicPageViews = array(); // Create a fresh array for the new page
    
    int headerY = 30;
#define ROW_HEIGHT 40
    int yPos = headerY + 28;
    int count = currentFileList ? currentFileList->count : 0;
    int startIdx = filesPageIndex * 5;
    int endIdx = startIdx + 5;
    if (endIdx > count) endIdx = count;
    
    // Draw Rows
    for (int i = startIdx; i < endIdx; i++) {
        CCDictionary* item = (CCDictionary*)arrayObjectAtIndex(currentFileList, i);
        
        CCString* keyName = ccs("name");
        CCString* keyDate = ccs("date");
        CCString* keySize = ccs("size");
        CCString* keyIsDir = ccs("is_dir");
        
        CCString* nameStr = (CCString*)dictionaryObjectForKey(item, keyName);
        CCString* dateStr = (CCString*)dictionaryObjectForKey(item, keyDate);
        CCString* sizeStr = (CCString*)dictionaryObjectForKey(item, keySize);
        CCNumber* isDirNum = (CCNumber*)dictionaryObjectForKey(item, keyIsDir);
        
        freeCCString(keyName);
        freeCCString(keyDate);
        freeCCString(keySize);
        freeCCString(keyIsDir);
        
        bool isDir = (numberIntValue(isDirNum) > 0);
        
        CCView* row = viewWithFrame(ccRect(0, yPos, 320, ROW_HEIGHT));
        row->backgroundColor = (i % 2 == 0) ? color(0.12, 0.12, 0.12, 1.0) : color(0.14, 0.14, 0.14, 1.0);
        row->tag = TAG_FILES_ITEM_BASE + i;
        
        CCView* icon = viewWithFrame(ccRect(5, 5, 30, 30));
        icon->backgroundColor = isDir ? color(1.0, 0.8, 0.2, 1.0) : color(0.4, 0.4, 1.0, 1.0);
        viewAddSubview(row, icon);
        
        CCLabel* lName = labelWithFrame(ccRect(45, 10, 170 - 40, 20));
        lName->text = copyCCString(nameStr); // Deep Copy!
        lName->fontSize = 16;
        lName->textColor = color(1.0, 1.0, 1.0, 1.0);
        viewAddSubview(row, lName);
        
        CCLabel* lDate = labelWithFrame(ccRect(170 + 10, 12, 90, 20));
        lDate->text = copyCCString(dateStr); // Deep Copy!
        lDate->fontSize = 12;
        lDate->textColor = color(0.7, 0.7, 0.7, 1.0);
        viewAddSubview(row, lDate);
        
        CCLabel* lSize = labelWithFrame(ccRect(170 + 90 + 10, 12, 60, 20));
        lSize->text = copyCCString(sizeStr); // Deep Copy!
        lSize->fontSize = 12;
        lSize->textColor = color(0.59, 0.59, 0.59, 1.0);
        viewAddSubview(row, lSize);
        
        // Add to Screen AND Tracking Array
        viewAddSubview(mainWindowView, row);
        arrayAddObject(uiDynamicPageViews, row);
        
        yPos += (ROW_HEIGHT + 2);
    }
    
    // Draw Pagination Controls
    int maxPages = (count + 5 - 1) / 5;
    if (maxPages > 1) {
        if (filesPageIndex > 0) {
            CCView* btnPrev = viewWithFrame(ccRect(10, 430, 80, 40));
            btnPrev->backgroundColor = color(0.0, 0.47, 1.0, 1.0);
            btnPrev->tag = TAG_FILES_PREV;
            
            CCLabel* l = labelWithFrame(ccRect(30, 10, 20, 20));
            l->text = copyCCString(ccs("<"));
            l->fontSize = 20;
            l->textColor = color(1.0, 1.0, 1.0, 1.0);
            viewAddSubview(btnPrev, l);
            
            viewAddSubview(mainWindowView, btnPrev);
            arrayAddObject(uiDynamicPageViews, btnPrev); // TRACK IT
        }
        
        // --- THE FIX: Wrap the label in a true CCView container ---
                CCView* pageContainer = viewWithFrame(ccRect(110, 440, 100, 20));
                pageContainer->backgroundColor = color(0.0, 0.0, 0.0, 0.0); // Transparent
                pageContainer->tag = 0; // No tag needed

                char pageStr[16];
                snprintf(pageStr, 16, "%d / %d", filesPageIndex + 1, maxPages);
                
                // Coordinates are now relative to the new pageContainer (0,0)
                CCLabel* lblPage = labelWithFrame(ccRect(0, 0, 100, 20));
                lblPage->text = copyCCString(ccs(pageStr));
                lblPage->textColor = color(1.0, 1.0, 1.0, 1.0);
                lblPage->textAlignment = CCTextAlignmentCenter; // Optional: keeps it looking nice
                
                viewAddSubview(pageContainer, lblPage); // Add label to container
                
                // Add the container to the screen AND the tracking array
                viewAddSubview(mainWindowView, pageContainer);
                arrayAddObject(uiDynamicPageViews, pageContainer);
        
        if (filesPageIndex < maxPages - 1) {
            CCView* btnNext = viewWithFrame(ccRect(230, 430, 80, 40));
            btnNext->backgroundColor = color(0.0, 0.47, 1.0, 1.0);
            btnNext->tag = TAG_FILES_NEXT;
            
            CCLabel* l = labelWithFrame(ccRect(30, 10, 20, 20));
            l->text = copyCCString(ccs(">"));
            l->fontSize = 20;
            l->textColor = color(1.0, 1.0, 1.0, 1.0);
            viewAddSubview(btnNext, l);
            
            viewAddSubview(mainWindowView, btnNext);
            arrayAddObject(uiDynamicPageViews, btnNext); // TRACK IT
        }
    }
}

// --- UI Construction ---

void setup_files_ui(void) {
    FreeOSLogI("FilesApp", "Starting setup_files_ui App");
    
    currentView = CurrentViewFiles;
    
    uiFilesContextMenu = NULL;
    uiFilesProperties = NULL;
    uiNewFolderDialog = NULL;
    uiNewFolderInput = NULL;
    
    if (uiDynamicPageViews) {
        freeElement(uiDynamicPageViews); // Just free the array container
        uiDynamicPageViews = NULL;       // Reset so drawFileTableView starts fresh
    }
    
    // --- 3. THE FIX: ONLY FREE IF IT IS A FILES APP WINDOW ---
        // If the window has our tag, it's safe to destroy (e.g., navigating folders).
        // If it does NOT have our tag, it's the Home Screen on the stack! Leave it alone!
        if (mainWindowView != NULL && mainWindowView->tag == TAG_FILES_APP_WINDOW) {
            freeViewHierarchy(mainWindowView);
            mainWindowView = NULL;
        }
        
        if (!currentFileList) {
            scan_current_directory();
        }
        
        // --- 4. BUILD THE NEW WINDOW & TAG IT ---
        // Only build a new base window if we don't already have one
        if (mainWindowView == NULL || mainWindowView->tag != TAG_FILES_APP_WINDOW) {
            mainWindowView = viewWithFrame(ccRect(0, 0, 320, 480));
            mainWindowView->backgroundColor = color(0.08, 0.08, 0.08, 1.0);
            mainWindowView->tag = TAG_FILES_APP_WINDOW; // Stamp it so we know it's ours!
        }
    
    CCLabel* lblPath = labelWithFrame(ccRect(10, 5, 300, 20));
    lblPath->text = copyCCString(ccs(is_root_mode() ? "My Devices" : currentPath));
    lblPath->fontSize = 14;
    lblPath->textColor = color(0.78, 0.78, 0.78, 1.0);
    viewAddSubview(mainWindowView, lblPath); // NO CAST
    
    int headerY = 30;
    uiFilesHeader = viewWithFrame(ccRect(0, headerY, 320, 25));
    uiFilesHeader->backgroundColor = color(0.24, 0.24, 0.24, 1.0);
    
#define COL_NAME_WIDTH 170
#define COL_DATE_WIDTH 90
#define COL_SIZE_WIDTH 60
    
    CCLabel* hName = labelWithFrame(ccRect(10, 5, COL_NAME_WIDTH, 15));
    hName->text = copyCCString(ccs("Name"));
    hName->fontSize = 12;
    hName->textColor = (fileSortMode == SORT_MODE_NAME) ? color(1.0, 1.0, 0.0, 1.0) : color(0.78, 0.78, 0.78, 1.0);
    viewAddSubview(uiFilesHeader, hName); // NO CAST
    
    CCLabel* hDate = labelWithFrame(ccRect(COL_NAME_WIDTH + 10, 5, COL_DATE_WIDTH, 15));
    hDate->text = copyCCString(ccs("Date"));
    hDate->fontSize = 12;
    hDate->textColor = (fileSortMode == SORT_MODE_DATE) ? color(1.0, 1.0, 0.0, 1.0) : color(0.78, 0.78, 0.78, 1.0);
    viewAddSubview(uiFilesHeader, hDate); // NO CAST
    
    CCLabel* hSize = labelWithFrame(ccRect(COL_NAME_WIDTH + COL_DATE_WIDTH + 10, 5, COL_SIZE_WIDTH, 15));
    hSize->text = copyCCString(ccs("Size"));
    hSize->fontSize = 12;
    hSize->textColor = (fileSortMode == SORT_MODE_SIZE) ? color(1.0, 1.0, 0.0, 1.0) : color(0.78, 0.78, 0.78, 1.0);
    viewAddSubview(uiFilesHeader, hSize); // NO CAST
    
    viewAddSubview(mainWindowView, uiFilesHeader);
    
    drawFileTableView();
    
    if (!is_root_mode()) {
        CCView* btnUp = viewWithFrame(ccRect(260, 0, 60, 30));
        btnUp->backgroundColor = color(0.31, 0.31, 0.31, 1.0);
        btnUp->tag = TAG_FILES_UP_DIR;
        
        CCLabel* l = labelWithFrame(ccRect(20, 5, 20, 20));
        l->text = copyCCString(ccs("UP"));
        l->fontSize = 14;
        l->textColor = color(1.0, 1.0, 1.0, 1.0);
        
        viewAddSubview(btnUp, l); // NO CAST
        viewAddSubview(mainWindowView, btnUp);
    }
    
    if (uiFilesContextMenu) {
        viewAddSubview(mainWindowView, uiFilesContextMenu);
    }
    if (uiFilesProperties) {
        viewAddSubview(mainWindowView, uiFilesProperties);
    }
}
// --- Navigation & Touch Handling ---

void files_open_file(const char* name) {
    if (!name) return; // Safety check
    
    if (is_root_mode()) {
        if (strcmp(name, "Internal Storage") == 0) strcpy(currentPath, "/spiflash");
        else if (strcmp(name, "SD Card") == 0) strcpy(currentPath, "/sdcard");
        else return;
        
        filesPageIndex = 0;
        scan_current_directory();
        setup_files_ui();
        update_full_ui(); // Force refresh to show new directory
        return;
    }
    
    char fullPath[512];
    snprintf(fullPath, 512, "%s/%s", currentPath, name);
    
    struct stat path_stat;
    if (stat(fullPath, &path_stat) == 0 && S_ISDIR(path_stat.st_mode)) {
        strcpy(currentPath, fullPath);
        filesPageIndex = 0;
        scan_current_directory();
        setup_files_ui();
        update_full_ui(); // Force refresh to show new directory
    } else {
        FreeOSLogI("FilesApp", "Opening file: %s", fullPath);
        // Put file opening logic here (e.g., text editor/image viewer)
    }
}

// --- Properties Viewer ---

void show_properties_view(const char* filename) {
    if (uiFilesProperties) uiFilesProperties = NULL;
    
    uiFilesProperties = viewWithFrame(ccRect(20, 60, 280, 360));
    uiFilesProperties->backgroundColor = color(0.18, 0.18, 0.18, 1.0); // Slightly lighter than context menu
    
    // Header
    CCLabel* title = labelWithFrame(ccRect(10, 10, 260, 20));
    title->text = copyCCString(ccs("Properties"));
    title->fontSize = 18;
    title->textColor = color(1.0, 1.0, 1.0, 1.0);
    viewAddSubview(uiFilesProperties, title);
    
    char infoBuffer[512] = {0};
    
    // Drive Logic vs File Logic
    if (is_root_mode() && (strcmp(filename, "Internal Storage") == 0 || strcmp(filename, "SD Card") == 0)) {
        const char* drivePath = (strcmp(filename, "Internal Storage") == 0) ? "/spiflash" : "/sdcard";
        uint64_t total_bytes = 0, free_bytes = 0;
        
        esp_vfs_fat_info(drivePath, &total_bytes, &free_bytes);
        
        char totalStr[32], freeStr[32];
        format_file_size(total_bytes, totalStr, 32);
        format_file_size(free_bytes, freeStr, 32);
        
        snprintf(infoBuffer, sizeof(infoBuffer),
                 "Name: %s\nType: Drive\nPath: %s\n\nTotal Space: %s\nFree Space: %s",
                 filename, drivePath, totalStr, freeStr);
    }
    else {
        // Standard File/Folder Logic
        char fullPath[512];
        snprintf(fullPath, sizeof(fullPath), "%s/%s", strcmp(currentPath, "/") == 0 ? "" : currentPath, filename);
        
        struct stat st;
        if (stat(fullPath, &st) == 0) {
            char sizeStr[32];
            format_file_size(st.st_size, sizeStr, 32);
            
            struct tm *tm_mod = localtime(&st.st_mtime);
            char modBuf[64];
            strftime(modBuf, sizeof(modBuf), "%b %d, %Y %H:%M", tm_mod);
            
            const char* typeStr = S_ISDIR(st.st_mode) ? "Folder" : (is_archive_file(filename) ? "Archive" : "File");
            
            snprintf(infoBuffer, sizeof(infoBuffer),
                     "Name: %s\nType: %s\nSize: %s\n\nModified:\n%s",
                     filename, typeStr, S_ISDIR(st.st_mode) ? "--" : sizeStr, modBuf);
        } else {
            strcpy(infoBuffer, "Error: Could not read file stat.");
        }
    }
    
    // Body Text
    CCLabel* body = labelWithFrame(ccRect(15, 50, 250, 240));
    body->text = copyCCString(ccs(infoBuffer));
    body->fontSize = 14;
    body->textColor = color(0.8, 0.8, 0.8, 1.0);
    body->textVerticalAlignment = CCTextVerticalAlignmentTop;
    viewAddSubview(uiFilesProperties, body);
    
    // Close Button
    CCView* btnClose = viewWithFrame(ccRect(90, 310, 100, 35));
    btnClose->backgroundColor = color(0.0, 0.47, 1.0, 1.0); // Blue
    btnClose->tag = TAG_CTX_PROPS_CLOSE;
    
    CCLabel* lblClose = labelWithFrame(ccRect(0, 8, 100, 20));
    lblClose->text = copyCCString(ccs("Close"));
    lblClose->fontSize = 16;
    lblClose->textAlignment = CCTextAlignmentCenter;
    lblClose->textColor = color(1.0, 1.0, 1.0, 1.0);
    viewAddSubview(btnClose, lblClose);
    
    viewAddSubview(uiFilesProperties, btnClose);
    viewAddSubview(mainWindowView, uiFilesProperties);
}

void paste_clipboard_file(void) {
    if (strlen(clipboardPath) == 0) return;
    if (is_root_mode()) return;
    
    // 1. Extract the original filename
    const char* original_filename = strrchr(clipboardPath, '/');
    if (!original_filename) original_filename = clipboardPath;
    else original_filename++; // Skip the '/'
    
    // 2. Separate basename and extension
    char basename[128] = {0};
    char ext[32] = {0};
    const char* dot = strrchr(original_filename, '.');
    
    if (dot && dot != original_filename) {
        strncpy(basename, original_filename, dot - original_filename);
        strcpy(ext, dot); // Includes the '.'
    } else {
        strcpy(basename, original_filename); // No extension
    }
    
    // 3. Find an available filename
    char destPath[512];
    char newFilename[128];
    strcpy(newFilename, original_filename);
    snprintf(destPath, sizeof(destPath), "%s/%s", currentPath, newFilename);
    
    struct stat st;
    int copyNum = 0;
    
    // Loop until we find a filename that DOES NOT exist
    while (stat(destPath, &st) == 0) {
        if (copyNum == 0) {
            snprintf(newFilename, sizeof(newFilename), "%s copy%s", basename, ext);
        } else {
            snprintf(newFilename, sizeof(newFilename), "%s copy %d%s", basename, copyNum, ext);
        }
        snprintf(destPath, sizeof(destPath), "%s/%s", currentPath, newFilename);
        copyNum++;
    }
    
    // 4. Perform the byte-by-byte copy
    FILE* src = fopen(clipboardPath, "rb");
    if (!src) return;
    
    FILE* dst = fopen(destPath, "wb");
    if (!dst) { fclose(src); return; }
    
    size_t bufferSize = 4096;
    char* buffer = malloc(bufferSize);
    if (buffer) {
        size_t bytesRead;
        while ((bytesRead = fread(buffer, 1, bufferSize, src)) > 0) {
            fwrite(buffer, 1, bytesRead, dst);
        }
        free(buffer);
    }
    
    fclose(src);
    fclose(dst);
}


void handle_files_touch(int x, int y, int touchState) {
    
    // ==========================================
    // FINGER IS TOUCHING THE SCREEN (DOWN / HELD)
    // ==========================================
    if (touchState == 1) {
        if (!is_pressing) {
            // First frame of touch down
            is_pressing = true;
            long_press_fired = false; // Reset the flag for this new tap
            touchStartTime = esp_timer_get_time() / 1000;
            touchStartX = x;
            touchStartY = y;
        } else {
            // Finger is held down or dragging
            if (abs(x - touchStartX) > 15 || abs(y - touchStartY) > 15) {
                touchStartTime = 0; // Dragged too far, cancel the tap
            }
            
            // --- REAL-TIME LONG PRESS CHECK ---
            if (touchStartTime != 0 && !long_press_fired && !uiFilesContextMenu && !uiNewFolderDialog && !uiFilesProperties) {
                uint64_t now = esp_timer_get_time() / 1000;
                
                if ((now - touchStartTime) >= 1000) { // EXACTLY 1 SECOND
                    long_press_fired = true;
                    
                    CCView* target = find_subview_at_point(mainWindowView, touchStartX, touchStartY);
                    
                    // 1. Did they long-press a file/folder row?
                    if (target && target->tag >= TAG_FILES_ITEM_BASE) {
                        int idx = target->tag - TAG_FILES_ITEM_BASE;
                        CCDictionary* item = (CCDictionary*)arrayObjectAtIndex(currentFileList, idx);
                        CCString* name = (CCString*)dictionaryObjectForKeyFreeKey(item, ccs("name"));
                        
                        if (name && name->string) {
                            strcpy(selectedFilename, name->string);
                            
                            // --- CORRECT ORDER ---
                            //setup_files_ui();                  // 1. Wipe and rebuild base UI
                            show_context_menu(name->string);   // 2. Spawn menu ON TOP safely
                            update_full_ui();                  // 3. Draw
                        }
                    }
                    // 2. Did they long-press the background/empty space?
                    else if (!is_root_mode()) {
                        selectedFilename[0] = '\0';
                        
                        // --- CORRECT ORDER ---
                        //setup_files_ui();                  // 1. Wipe and rebuild base UI
                        show_directory_context_menu();     // 2. Spawn directory menu ON TOP safely
                        update_full_ui();                  // 3. Draw
                    }
                }
            }
        }
        
        return;
    }
    
    // ==========================================
    // FINGER LIFTED (RELEASED)
    // ==========================================
    if (touchState == 0) {
        if (!is_pressing) return; // Ignore if we are already released
        is_pressing = false;      // Reset state for next tap
        
        // If they scrolled, OR if the 2-second long press already fired, DO NOTHING on release!
        if (touchStartTime == 0 || long_press_fired) {
            long_press_fired = false; // Clean up
            return;
        }
        
        // --- If we reach here, it was a valid SHORT TAP! ---
        
        int tapX = touchStartX;
        int tapY = touchStartY;
        
        // 0. Handle Properties Window Taps (Highest Z-Index)
        if (uiFilesProperties) {
            CCView* target = find_subview_at_point(uiFilesProperties, tapX, tapY);
            if (target && target->tag == TAG_CTX_PROPS_CLOSE) {
                // --- YOUR ARCHITECTURAL FIX ---
                // 1. Detach from the main window so it stops rendering
                viewRemoveFromSuperview(uiFilesProperties);
                
                // 2. Safely destroy the isolated menu
                freeViewHierarchy(uiFilesProperties);
                uiFilesProperties = NULL;
                
                // 3. Just push the graphics update to erase it from the screen!
                update_full_ui();
            }
            return; // Eat the touch so it doesn't click items behind the window
        }
        
        // 1. Handle Context Menu Taps
        if (uiFilesContextMenu) {
            CCView* target = find_subview_at_point(uiFilesContextMenu, tapX, tapY);
            if (target) {
                int tag = target->tag;
                
                viewRemoveFromSuperview(uiFilesContextMenu);
                freeViewHierarchy(uiFilesContextMenu);
                uiFilesContextMenu = NULL;
                
                if (tag == TAG_CTX_CANCEL) {
                    
                }
                else if (tag == TAG_CTX_OPEN) {
                    files_open_file(selectedFilename);
                }
                else if (tag == TAG_CTX_COPY) {
                    // Save to clipboard
                    snprintf(clipboardPath, sizeof(clipboardPath), "%s/%s", strcmp(currentPath, "/") == 0 ? "" : currentPath, selectedFilename);
                    FreeOSLogI("FilesApp", "Copied to clipboard: %s", clipboardPath);
                }
                else if (tag == TAG_CTX_PASTE) {
                    paste_clipboard_file();
                    scan_current_directory();
                    //setup_files_ui(); // Rebuild one more time to show the pasted file
                }
                else if (tag == TAG_CTX_PROPS) {
                    // 2. Spawn Properties window on top
                    if (strlen(selectedFilename) > 0) {
                        show_properties_view(selectedFilename);
                    } else {
                        char* folderName = strrchr(currentPath, '/');
                        if (folderName && strlen(folderName) > 1) show_properties_view(folderName + 1);
                        else show_properties_view("SD Card");
                    }
                }
                else if (tag == TAG_CTX_COMPRESS) {
                    FreeOSLogI("FilesApp", "Compressing %s...", selectedFilename);
                    uiFilesContextMenu = NULL;
                }
                else if (tag == TAG_CTX_DECOMPRESS) {
                    FreeOSLogI("FilesApp", "Decompressing archive %s...", selectedFilename);
                    uiFilesContextMenu = NULL;
                }
                else if (tag == TAG_CTX_NEW) {
                    uiFilesContextMenu = NULL;
                    show_new_folder_dialog();
                }
                
                update_full_ui();
            }
            return; // Eat the touch
        }
        
        // 0. Handle New Folder Dialog Taps (Highest Z-Index)
        if (uiNewFolderDialog) {
            CCView* target = find_subview_at_point(uiNewFolderDialog, tapX, tapY);
            if (target) {
                if (target->tag == TAG_NEW_FOLDER_CANCEL) {
                    uiNewFolderDialog = NULL;
                    uiNewFolderInput = NULL;
                    setup_files_ui(); // Rebuild UI (which should clear keyboard)
                    update_full_ui();
                }
                else if (target->tag == TAG_NEW_FOLDER_CREATE) {
                    if (uiNewFolderInput && uiNewFolderInput->text && uiNewFolderInput->text->string) {
                        if (!is_root_mode()) {
                            char newDirPath[512];
                            snprintf(newDirPath, sizeof(newDirPath), "%s/%s", currentPath, uiNewFolderInput->text->string);
                            
                            FreeOSLogI("FilesApp", "Creating folder: %s", newDirPath);
                            
                            // 0777 grants read/write/execute permissions
                            if (mkdir(newDirPath, 0777) == 0) {
                                FreeOSLogI("FilesApp", "Folder created successfully.");
                            } else {
                                FreeOSLogE("FilesApp", "Failed to create folder.");
                            }
                        } else {
                            FreeOSLogE("FilesApp", "Cannot create folder in virtual root.");
                        }
                    }
                    
                    uiNewFolderDialog = NULL;
                    uiNewFolderInput = NULL;
                    filesPageIndex = 0; // Reset to page 1 to see the new folder
                    scan_current_directory(); // Rescan drive
                    setup_files_ui(); // Rebuild UI
                    update_full_ui();
                }
            }
            return; // Eat the touch so we don't click anything else
        }
        
        // 2. Handle Header Sort Taps
        if (tapY > 30 && tapY < 55) {
            if (tapX < 170) {
                if (fileSortMode == SORT_MODE_NAME) fileSortAscending = !fileSortAscending;
                else { fileSortMode = SORT_MODE_NAME; fileSortAscending = true; }
            } else if (tapX < 260) {
                if (fileSortMode == SORT_MODE_DATE) fileSortAscending = !fileSortAscending;
                else { fileSortMode = SORT_MODE_DATE; fileSortAscending = false; }
            } else {
                if (fileSortMode == SORT_MODE_SIZE) fileSortAscending = !fileSortAscending;
                else { fileSortMode = SORT_MODE_SIZE; fileSortAscending = false; }
            }
            sort_file_list();
            setup_files_ui();
            update_full_ui();
            return;
        }
        
        // 3. Find what row was tapped
        CCView* target = find_subview_at_point(mainWindowView, tapX, tapY);
        if (!target) return;
        
        // Handle Back/Up Navigation
        if (target->tag == TAG_FILES_UP_DIR) {
            if (strcmp(currentPath, "/fat") == 0 || strcmp(currentPath, "/sdcard") == 0) {
                strcpy(currentPath, "/");
            } else {
                char* lastSlash = strrchr(currentPath, '/');
                if (lastSlash && lastSlash != currentPath) *lastSlash = '\0';
            }
            filesPageIndex = 0;
            
            // 1. WIPE THE UI FIRST so the graphics engine stops trying to read the old text
            if (mainWindowView) {
                freeViewHierarchy(mainWindowView);
                mainWindowView = NULL;
            }
            
            // 2. FREE OLD LIST & SCAN NEW DIRECTORY safely
            if (currentFileList) {
                freeElement(currentFileList);
                currentFileList = NULL;
            }
            scan_current_directory();
            
            // 3. REBUILD THE CLEAN UI
            setup_files_ui();
            update_full_ui();
            return;
        }
        
        // Handle Pagination
        if (target->tag == TAG_FILES_PREV) {
            if (filesPageIndex > 0) {
                filesPageIndex--;
                FreeOSLogI("FilesApp", "filesPageIndex - %d", filesPageIndex);
                
                // Targeted redraw instead of full UI rebuild!
                drawFileTableView();
                update_full_ui();
            }
            return;
        }
        if (target->tag == TAG_FILES_NEXT) {
            filesPageIndex++;
            FreeOSLogI("FilesApp", "filesPageIndex + %d", filesPageIndex);
            
            // Targeted redraw instead of full UI rebuild!
            drawFileTableView();
            update_full_ui();
            return;
        }
        
        // 4. Handle File/Folder Row Taps (Short Tap Only)
        if (target->tag >= TAG_FILES_ITEM_BASE) {
            int idx = target->tag - TAG_FILES_ITEM_BASE;
            CCDictionary* item = (CCDictionary*)arrayObjectAtIndex(currentFileList, idx);
            CCString* name = (CCString*)dictionaryObjectForKeyFreeKey(item, ccs("name"));
            
            if (!name || !name->string) return;
            
            files_open_file(name->string);
        }
    }
}

void freeFilesView(void) {
    if (currentFileList != NULL) {
        freeElement(currentFileList);
        currentFileList = NULL;  // <--- CRITICAL FIX
        filesPageIndex = 0;      // Reset pagination for the next time it opens
    }
}