/* DSStoreInfo.m
 *  
 * DS_Store information model for .DS_Store interoperability in Spatial mode.
 *
 */

#import "DSStoreInfo.h"
#import "DSStore.h"

#pragma mark - DSStoreIconInfo Implementation

@implementation DSStoreIconInfo

@synthesize filename = _filename;
@synthesize position = _position;
@synthesize hasPosition = _hasPosition;
@synthesize comments = _comments;
@synthesize labelColor = _labelColor;
@synthesize hasLabelColor = _hasLabelColor;

+ (instancetype)infoForFilename:(NSString *)filename
{
    return [[[self alloc] initWithFilename:filename] autorelease];
}

- (instancetype)initWithFilename:(NSString *)filename
{
    self = [super init];
    if (self) {
        _filename = [filename copy];
        _position = NSZeroPoint;
        _hasPosition = NO;
        _comments = nil;
        _labelColor = DSStoreLabelColorNone;
        _hasLabelColor = NO;
    }
    return self;
}

- (void)dealloc
{
    [_filename release];
    [_comments release];
    [super dealloc];
}

- (id)copyWithZone:(NSZone *)zone
{
    DSStoreIconInfo *copy = [[DSStoreIconInfo allocWithZone:zone] initWithFilename:_filename];
    copy.position = _position;
    copy.hasPosition = _hasPosition;
    copy.comments = _comments;
    copy.labelColor = _labelColor;
    copy.hasLabelColor = _hasLabelColor;
    return copy;
}

- (NSPoint)gnustepPositionForViewHeight:(CGFloat)viewHeight iconHeight:(CGFloat)iconHeight
{
    // Delegate to DSStore class method for .DS_Store interoperability coordinate conversion
    return [DSStore gnustepPointFromDSStorePoint:_position viewHeight:viewHeight iconHeight:iconHeight];
}

+ (NSColor *)colorForLabelColor:(DSStoreLabelColor)labelColor
{
    // macOS Finder label colors (approximate values)
    switch (labelColor) {
        case DSStoreLabelColorRed:
            return [NSColor colorWithCalibratedRed:1.0 green:0.23 blue:0.19 alpha:1.0];
        case DSStoreLabelColorOrange:
            return [NSColor colorWithCalibratedRed:1.0 green:0.58 blue:0.0 alpha:1.0];
        case DSStoreLabelColorYellow:
            return [NSColor colorWithCalibratedRed:1.0 green:0.87 blue:0.0 alpha:1.0];
        case DSStoreLabelColorGreen:
            return [NSColor colorWithCalibratedRed:0.3 green:0.85 blue:0.39 alpha:1.0];
        case DSStoreLabelColorBlue:
            return [NSColor colorWithCalibratedRed:0.25 green:0.61 blue:0.98 alpha:1.0];
        case DSStoreLabelColorPurple:
            return [NSColor colorWithCalibratedRed:0.69 green:0.32 blue:0.87 alpha:1.0];
        case DSStoreLabelColorGrey:
            return [NSColor colorWithCalibratedRed:0.6 green:0.6 blue:0.6 alpha:1.0];
        case DSStoreLabelColorNone:
        default:
            return nil;
    }
}

- (NSString *)description
{
    return [NSString stringWithFormat:@"<DSStoreIconInfo: %@ pos:(%.0f,%.0f) hasPos:%@ labelColor:%ld>",
            _filename, _position.x, _position.y, _hasPosition ? @"YES" : @"NO", (long)_labelColor];
}

@end

#pragma mark - DSStoreInfo Implementation

@implementation DSStoreInfo

@synthesize directoryPath = _directoryPath;
@synthesize loaded = _loaded;
@synthesize windowFrame = _windowFrame;
@synthesize hasWindowFrame = _hasWindowFrame;
@synthesize viewStyle = _viewStyle;
@synthesize hasViewStyle = _hasViewStyle;
@synthesize iconSize = _iconSize;
@synthesize hasIconSize = _hasIconSize;
@synthesize iconArrangement = _iconArrangement;
@synthesize hasIconArrangement = _hasIconArrangement;
@synthesize labelPosition = _labelPosition;
@synthesize hasLabelPosition = _hasLabelPosition;
@synthesize gridSpacing = _gridSpacing;
@synthesize hasGridSpacing = _hasGridSpacing;
@synthesize backgroundType = _backgroundType;
@synthesize backgroundColor = _backgroundColor;
@synthesize backgroundImagePath = _backgroundImagePath;
@synthesize sidebarWidth = _sidebarWidth;
@synthesize hasSidebarWidth = _hasSidebarWidth;
@synthesize listTextSize = _listTextSize;
@synthesize hasListTextSize = _hasListTextSize;
@synthesize listIconSize = _listIconSize;
@synthesize hasListIconSize = _hasListIconSize;
@synthesize sortColumn = _sortColumn;
@synthesize hasSortColumn = _hasSortColumn;
@synthesize sortAscending = _sortAscending;
@synthesize columnWidths = _columnWidths;
@synthesize columnVisible = _columnVisible;

#pragma mark - Factory Methods

+ (instancetype)infoForDirectoryPath:(NSString *)path
{
    return [self infoForDirectoryPath:path loadImmediately:YES];
}

+ (instancetype)infoForDirectoryPath:(NSString *)path loadImmediately:(BOOL)load
{
    DSStoreInfo *info = [[[self alloc] initWithDirectoryPath:path] autorelease];
    if (load) {
        [info load];
    }
    return info;
}

#pragma mark - Sort Column Conversion

+ (int)infoTypeForSortColumnName:(NSString *)columnName
{
    // DS_Store column names to FSNInfoType mapping
    // These are the standard macOS Finder column identifiers
    if (!columnName) return -1;
    
    if ([columnName isEqualToString:@"name"] || 
        [columnName isEqualToString:@"Name"]) {
        return 0;  // FSNInfoNameType
    }
    if ([columnName isEqualToString:@"kind"] || 
        [columnName isEqualToString:@"Kind"]) {
        return 1;  // FSNInfoKindType
    }
    if ([columnName isEqualToString:@"dateModified"] ||
        [columnName isEqualToString:@"Date Modified"] ||
        [columnName isEqualToString:@"modificationDate"]) {
        return 2;  // FSNInfoDateType
    }
    if ([columnName isEqualToString:@"size"] || 
        [columnName isEqualToString:@"Size"]) {
        return 3;  // FSNInfoSizeType
    }
    if ([columnName isEqualToString:@"owner"] || 
        [columnName isEqualToString:@"Owner"]) {
        return 4;  // FSNInfoOwnerType
    }
    if ([columnName isEqualToString:@"dateCreated"] ||
        [columnName isEqualToString:@"Date Created"] ||
        [columnName isEqualToString:@"creationDate"]) {
        return 2;  // Map to FSNInfoDateType (we don't have separate creation date)
    }
    if ([columnName isEqualToString:@"dateAdded"] ||
        [columnName isEqualToString:@"Date Added"]) {
        return 2;  // Map to FSNInfoDateType
    }
    
    return -1;  // Unknown column
}

+ (NSString *)sortColumnNameForInfoType:(int)infoType
{
    switch (infoType) {
        case 0:  // FSNInfoNameType
            return @"name";
        case 1:  // FSNInfoKindType
            return @"kind";
        case 2:  // FSNInfoDateType
            return @"dateModified";
        case 3:  // FSNInfoSizeType
            return @"size";
        case 4:  // FSNInfoOwnerType
            return @"owner";
        default:
            return @"name";
    }
}

+ (NSString *)viewTypeNameForViewStyle:(DSStoreViewStyle)style
{
    switch (style) {
        case DSStoreViewStyleList:   return @"List";
        case DSStoreViewStyleColumn: return @"Browser";
        case DSStoreViewStyleIcon:
        default:                     return @"Icon";
    }
}

#pragma mark - Initialization

- (instancetype)initWithDirectoryPath:(NSString *)path
{
    self = [super init];
    if (self) {
        _directoryPath = [path copy];
        _loaded = NO;
        
        // Initialize defaults
        _windowFrame = NSZeroRect;
        _hasWindowFrame = NO;
        
        _viewStyle = DSStoreViewStyleIcon;
        _hasViewStyle = NO;
        
        _iconSize = 48;  // Default icon size
        _hasIconSize = NO;
        _iconArrangement = DSStoreIconArrangementNone;
        _hasIconArrangement = NO;
        _labelPosition = DSStoreLabelPositionBottom;
        _hasLabelPosition = NO;
        _gridSpacing = 0;
        _hasGridSpacing = NO;
        
        _backgroundType = DSStoreBackgroundDefault;
        _backgroundColor = nil;
        _backgroundImagePath = nil;
        
        _sidebarWidth = 0;
        _hasSidebarWidth = NO;
        
        _iconInfoDict = [[NSMutableDictionary alloc] init];
    }
    return self;
}

- (void)dealloc
{
    [_directoryPath release];
    [_backgroundColor release];
    [_backgroundImagePath release];
    [_iconInfoDict release];
    [_sortColumn release];
    [_columnWidths release];
    [_columnVisible release];
    [super dealloc];
}

#pragma mark - Loading

- (BOOL)load
{
    NSString *dsStorePath = [_directoryPath stringByAppendingPathComponent:@".DS_Store"];
    
    NSDebugLLog(@"gwspace", @"╔══════════════════════════════════════════════════════════════════╗");
    NSDebugLLog(@"gwspace", @"║             DS_STORE COMPREHENSIVE LOADING                       ║");
    NSDebugLLog(@"gwspace", @"╠══════════════════════════════════════════════════════════════════╣");
    NSDebugLLog(@"gwspace", @"║ Directory: %@", _directoryPath);
    NSDebugLLog(@"gwspace", @"║ DS_Store path: %@", dsStorePath);
    
    if (![[NSFileManager defaultManager] fileExistsAtPath:dsStorePath]) {
        NSDebugLLog(@"gwspace", @"║ ✗ No .DS_Store file found");
        NSDebugLLog(@"gwspace", @"╚══════════════════════════════════════════════════════════════════╝");
        return NO;
    }
    
    NSDebugLLog(@"gwspace", @"║ ✓ Found .DS_Store file");
    
    DSStore *store = [DSStore storeWithPath:dsStorePath];
    if (![store load]) {
        NSDebugLLog(@"gwspace", @"║ ✗ Failed to load .DS_Store file");
        NSDebugLLog(@"gwspace", @"╚══════════════════════════════════════════════════════════════════╝");
        return NO;
    }
    
    NSDebugLLog(@"gwspace", @"║ ✓ Successfully parsed .DS_Store");
    NSDebugLLog(@"gwspace", @"╟──────────────────────────────────────────────────────────────────╢");
    
    // Get all entries to see what's available
    NSArray *allFilenames = [store allFilenames];
    NSDebugLLog(@"gwspace", @"║ Files with entries: %lu", (unsigned long)[allFilenames count]);
    
    // Process directory-level entries (filename = ".")
    [self loadDirectoryEntriesFromStore:store];
    
    // Process per-file entries (icon positions, comments, etc.)
    [self loadIconEntriesFromStore:store filenames:allFilenames];
    
    _loaded = YES;
    
    NSDebugLLog(@"gwspace", @"╟──────────────────────────────────────────────────────────────────╢");
    NSDebugLLog(@"gwspace", @"║                      LOADING COMPLETE                            ║");
    NSDebugLLog(@"gwspace", @"╚══════════════════════════════════════════════════════════════════╝");
    
    [self logAllInfo];
    
    return YES;
}

- (BOOL)reload
{
    // Reset all state
    _hasWindowFrame = NO;
    _hasViewStyle = NO;
    _hasIconSize = NO;
    _hasIconArrangement = NO;
    _hasLabelPosition = NO;
    _hasGridSpacing = NO;
    _hasSidebarWidth = NO;
    _backgroundType = DSStoreBackgroundDefault;
    [_backgroundColor release]; _backgroundColor = nil;
    [_backgroundImagePath release]; _backgroundImagePath = nil;
    [_iconInfoDict removeAllObjects];
    _loaded = NO;
    
    return [self load];
}

#pragma mark - Manual population support

- (void)markAsLoaded
{
    _loaded = YES;
}

#pragma mark - Alias Resolution

/**
 * Resolve an alias record to a file path.
 * This is a simplified implementation that looks for common path patterns.
 * Native alias records are complex and contain multiple fallback strategies.
 */
- (NSString *)resolveAliasData:(NSData *)aliasData relativeTo:(NSString *)baseDir
{
    if (!aliasData || [aliasData length] < 150) {
        return nil;
    }
    
    // Alias records are complex, but typically contain:
    // - Volume name
    // - Directory IDs
    // - Full path as UTF-16 or UTF-8 string
    // For now, we'll try to find ASCII/UTF-8 path strings in the data
    
    // TS: unused variables
    // const unsigned char *bytes = [aliasData bytes];
    // NSUInteger len = [aliasData length];
    
    // Look for path-like strings in the alias data
    // Common patterns: starts with '/' or contains '.bg/' etc.
    NSString *dataString = [[NSString alloc] initWithData:aliasData 
                                                  encoding:NSUTF8StringEncoding];
    if (dataString) {
        // Try to extract paths using regex
        NSRegularExpression *regex = [NSRegularExpression 
            regularExpressionWithPattern:@"(/[^\\x00-\\x1F]+\\.(png|jpg|jpeg|tiff|gif|bmp))"
            options:NSRegularExpressionCaseInsensitive
            error:nil];
        
        NSArray *matches = [regex matchesInString:dataString
                                          options:0
                                            range:NSMakeRange(0, [dataString length])];
        
        if ([matches count] > 0) {
            NSTextCheckingResult *match = [matches objectAtIndex:0];
            NSString *path = [dataString substringWithRange:[match range]];
            [dataString release];
            
            // Check if file exists
            if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
                return path;
            }
            
            // Try relative to base directory
            NSString *relativePath = [baseDir stringByAppendingPathComponent:path];
            if ([[NSFileManager defaultManager] fileExistsAtPath:relativePath]) {
                return relativePath;
            }
        }
        [dataString release];
    }
    
    // Fallback: Look for common .bg folder pattern
    NSString *bgPath = [baseDir stringByAppendingPathComponent:@".bg"];
    if ([[NSFileManager defaultManager] fileExistsAtPath:bgPath]) {
        NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:bgPath error:nil];
        for (NSString *file in contents) {
            if ([[file pathExtension] isEqualToString:@"png"] ||
                [[file pathExtension] isEqualToString:@"jpg"] ||
                [[file pathExtension] isEqualToString:@"jpeg"]) {
                return [bgPath stringByAppendingPathComponent:file];
            }
        }
    }
    
    return nil;
}

#pragma mark - Private Loading Methods

- (void)loadDirectoryEntriesFromStore:(DSStore *)store
{
    NSDebugLLog(@"gwspace", @"║ --- Directory-level entries (filename '.') ---");
    
    NSArray *dirCodes = [store allCodesForFilename:@"."];
    NSDebugLLog(@"gwspace", @"║ Available codes for directory: %@", dirCodes);
    
    // IMPORTANT: Format Preferences for Interoperability
    // Modern .DS_Store files use binary plist formats which are preferred:
    //   - bwsp: Window settings (preferred over legacy fwi0)
    //   - icvp: Icon view settings (preferred over legacy icvo)
    //   - lsvp/lsvP: List view settings (preferred over legacy lsvo)
    // Background images are stored in icvp's backgroundImageAlias for modern files
    
    // Load window geometry - prefer modern format (bwsp) over legacy (fwi0)
    // bwsp: Modern binary plist with WindowBounds string and sidebar settings
    // fwi0: Legacy 16-byte binary format with window rect only
    [self loadBrowserWindowSettingsFromStore:store];  // Modern format (includes geometry)
    [self loadWindowGeometryFromStore:store];          // Legacy format (fallback)
    
    // Load view style (vstl)
    [self loadViewStyleFromStore:store];
    
    // Load icon view options - prefer modern format (icvp) over legacy (icvo)
    // icvp: Modern binary plist with comprehensive settings including backgrounds
    // icvo: Legacy 18-26 byte binary format with limited settings
    [self loadIconViewPlistFromStore:store];   // New format (try first)
    [self loadIconViewOptionsFromStore:store]; // Old format (fallback)
    
    // Load grid/spacing options (icgo, icsp)
    [self loadIconGridOptionsFromStore:store];
    
    // Load background settings (BKGD, bwsp)
    [self loadBackgroundFromStore:store];
    
    // Load sidebar width (fwsw)
    [self loadSidebarWidthFromStore:store];
    
    // Load list view settings (lsvp, lsvP, lsvo)
    [self loadListViewSettingsFromStore:store];
}

- (void)loadWindowGeometryFromStore:(DSStore *)store
{
    // fwi0: Legacy window geometry format (pre-10.6)
    // Only used as fallback if bwsp is not present
    // Modern .DS_Store files use bwsp with WindowBounds instead
    
    if (_hasWindowFrame) {
        NSDebugLLog(@"gwspace", @"║ ○ Skipping fwi0 (already have geometry from bwsp)");
        return;
    }
    
    DSStoreEntry *entry = [store entryForFilename:@"." code:@"fwi0"];
    if (entry && [[entry type] isEqualToString:@"blob"]) {
        NSData *data = (NSData *)[entry value];
        if ([data length] >= 16) {
            const uint8_t *bytes = (const uint8_t *)[data bytes];
            
            // fwi0 format: 16 bytes total
            // - Bytes 0-1: top (2-byte big-endian integer)
            // - Bytes 2-3: left (2-byte big-endian integer)
            // - Bytes 4-5: bottom (2-byte big-endian integer)
            // - Bytes 6-7: right (2-byte big-endian integer)
            // - Bytes 8-11: view style (4CC: icnv/clmv/Nlsv/Flwv)
            // - Bytes 12-15: flags/unknown
            // These define the content area rect in .DS_Store screen coordinates
            
            uint16_t top    = (bytes[0] << 8) | bytes[1];
            uint16_t left   = (bytes[2] << 8) | bytes[3];
            uint16_t bottom = (bytes[4] << 8) | bytes[5];
            uint16_t right  = (bytes[6] << 8) | bytes[7];
            
            // Convert from top/left/bottom/right edges to x/y/width/height
            CGFloat x = left;
            CGFloat y = top;
            CGFloat width = right - left;
            CGFloat height = bottom - top;
            
            _windowFrame = NSMakeRect(x, y, width, height);
            _hasWindowFrame = YES;
            
            NSDebugLLog(@"gwspace", @"║ ✓ fwi0 (Window Geometry):");
            NSDebugLLog(@"gwspace", @"║   Edges: top=%d left=%d bottom=%d right=%d", top, left, bottom, right);
            NSDebugLLog(@"gwspace", @"║   Rect: x=%.0f y=%.0f w=%.0f h=%.0f", x, y, width, height);
            
            // Log view style from bytes 8-11 if present
            if ([data length] >= 12) {
                char viewStyle[5] = {bytes[8], bytes[9], bytes[10], bytes[11], 0};
                NSDebugLLog(@"gwspace", @"║   View style: %s", viewStyle);
            }
        } else {
            NSDebugLLog(@"gwspace", @"║ ⚠ fwi0 data too short: %lu bytes", (unsigned long)[data length]);
        }
    } else {
        NSDebugLLog(@"gwspace", @"║ ○ No fwi0 (window geometry) entry");
    }
}

- (void)loadViewStyleFromStore:(DSStore *)store
{
    DSStoreEntry *entry = [store entryForFilename:@"." code:@"vstl"];
    if (entry && [[entry type] isEqualToString:@"type"]) {
        NSString *style = (NSString *)[entry value];
        
        if ([style isEqualToString:@"icnv"]) {
            _viewStyle = DSStoreViewStyleIcon;
            _hasViewStyle = YES;
            NSDebugLLog(@"gwspace", @"║ ✓ vstl (View Style): Icon view (icnv)");
        } else if ([style isEqualToString:@"Nlsv"]) {
            _viewStyle = DSStoreViewStyleList;
            _hasViewStyle = YES;
            NSDebugLLog(@"gwspace", @"║ ✓ vstl (View Style): List view (Nlsv)");
        } else if ([style isEqualToString:@"clmv"]) {
            _viewStyle = DSStoreViewStyleColumn;
            _hasViewStyle = YES;
            NSDebugLLog(@"gwspace", @"║ ✓ vstl (View Style): Column view (clmv)");
        } else if ([style isEqualToString:@"glyv"]) {
            _viewStyle = DSStoreViewStyleGallery;
            _hasViewStyle = YES;
            NSDebugLLog(@"gwspace", @"║ ✓ vstl (View Style): Gallery view (glyv)");
        } else if ([style isEqualToString:@"Flwv"]) {
            _viewStyle = DSStoreViewStyleCoverflow;
            _hasViewStyle = YES;
            NSDebugLLog(@"gwspace", @"║ ✓ vstl (View Style): Coverflow view (Flwv)");
        } else {
            NSDebugLLog(@"gwspace", @"║ ⚠ vstl (View Style): Unknown style '%@'", style);
        }
    } else {
        NSDebugLLog(@"gwspace", @"║ ○ No vstl (view style) entry");
    }
}

- (void)loadBrowserWindowSettingsFromStore:(DSStore *)store
{
    // bwsp: Modern browser window settings format (10.6+)
    // Binary plist containing WindowBounds, sidebar settings, toolbar visibility, etc.
    // This is the preferred source for window geometry on modern systems
    
    DSStoreEntry *entry = [store entryForFilename:@"." code:@"bwsp"];
    if (entry && [[entry type] isEqualToString:@"blob"]) {
        NSData *data = (NSData *)[entry value];
        NSDebugLLog(@"gwspace", @"║ ✓ bwsp (Browser Window Settings - Modern): %lu bytes", (unsigned long)[data length]);
        
        NSError *error = nil;
        NSDictionary *plist = [NSPropertyListSerialization propertyListWithData:data
                                                                        options:NSPropertyListImmutable
                                                                         format:NULL
                                                                          error:&error];
        if (plist && [plist isKindOfClass:[NSDictionary class]]) {
            // Extract WindowBounds if present
            NSString *windowBounds = [plist objectForKey:@"WindowBounds"];
            if (windowBounds && [windowBounds isKindOfClass:[NSString class]]) {
                // Parse WindowBounds string format: "{{x, y}, {width, height}}"
                NSRect rect = NSRectFromString(windowBounds);
                if (rect.size.width > 0 && rect.size.height > 0) {
                    _windowFrame = rect;
                    _hasWindowFrame = YES;
                    NSDebugLLog(@"gwspace", @"║   ✓ Window bounds extracted: %@", windowBounds);
                    NSDebugLLog(@"gwspace", @"║     Parsed as: x=%.0f y=%.0f w=%.0f h=%.0f", 
                          rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
                } else {
                    NSDebugLLog(@"gwspace", @"║   ⚠ Window bounds string present but invalid");
                }
            }
            
            // Extract sidebar width
            id sidebarWidthObj = [plist objectForKey:@"SidebarWidth"];
            if (sidebarWidthObj) {
                _sidebarWidth = [sidebarWidthObj intValue];
                _hasSidebarWidth = YES;
                NSDebugLLog(@"gwspace", @"║   Sidebar width: %d", _sidebarWidth);
            }
            
            NSDebugLLog(@"gwspace", @"║   Show sidebar: %@", [plist objectForKey:@"ShowSidebar"]);
            NSDebugLLog(@"gwspace", @"║   Show toolbar: %@", [plist objectForKey:@"ShowToolbar"]);
        } else {
            NSDebugLLog(@"gwspace", @"║   ⚠ Failed to parse bwsp as plist: %@", error);
        }
    } else {
        NSDebugLLog(@"gwspace", @"║ ○ No bwsp (browser window settings) entry");
    }
}

- (void)loadIconViewOptionsFromStore:(DSStore *)store
{
    // icvo: Legacy icon view options format (pre-10.6)
    // Only used as fallback if icvp is not present
    // Modern .DS_Store files use icvp binary plist instead
    
    // Skip if we already have settings from icvp (new format)
    if (_hasIconSize && _hasIconArrangement && _hasLabelPosition) {
        NSDebugLLog(@"gwspace", @"║ ○ Skipping icvo (already have settings from icvp)");
        return;
    }
    
    DSStoreEntry *entry = [store entryForFilename:@"." code:@"icvo"];
    if (entry && [[entry type] isEqualToString:@"blob"]) {
        NSData *data = (NSData *)[entry value];
        const uint8_t *bytes = (const uint8_t *)[data bytes];
        NSUInteger len = [data length];
        
        NSDebugLLog(@"gwspace", @"║ ✓ icvo (Icon View Options): %lu bytes", (unsigned long)len);
        
        // External docs specify two variants:
        // 1) "icvo" format: 4-byte magic + 8 unknown + 2-byte size + 4-byte arrangement ("none")
        // 2) "icv4" format: 2-byte size + 4-byte arrangement + 4-byte label + 12 flags
        
        if (len >= 4) {
            char magic[5] = {bytes[0], bytes[1], bytes[2], bytes[3], 0};
            
            if (strcmp(magic, "icvo") == 0 && len >= 18) {
                // Old "icvo" format: skip magic+flags (12 bytes), then size at offset 12
                uint16_t size = (bytes[12] << 8) | bytes[13];
                if (size > 0 && size <= 512) {
                    _iconSize = size;
                    _hasIconSize = YES;
                    NSDebugLLog(@"gwspace", @"║   Format: icvo, Icon size: %d", _iconSize);
                }
                
                // Arrangement at bytes 14-17
                if (len >= 18) {
                    char arr[5] = {bytes[14], bytes[15], bytes[16], bytes[17], 0};
                    if (strcmp(arr, "none") == 0) {
                        _iconArrangement = DSStoreIconArrangementNone;
                        _hasIconArrangement = YES;
                        NSDebugLLog(@"gwspace", @"║   Arrangement: none");
                    } else if (strcmp(arr, "grid") == 0) {
                        _iconArrangement = DSStoreIconArrangementGrid;
                        _hasIconArrangement = YES;
                        NSDebugLLog(@"gwspace", @"║   Arrangement: grid");
                    }
                }
            } else if (strcmp(magic, "icv4") == 0 && len >= 14) {
                // New "icv4" format: size at bytes 4-5
                uint16_t size = (bytes[4] << 8) | bytes[5];
                if (size > 0 && size <= 512) {
                    _iconSize = size;
                    _hasIconSize = YES;
                    NSDebugLLog(@"gwspace", @"║   Format: icv4, Icon size: %d", _iconSize);
                }
                
                // Arrangement at bytes 6-9
                char arr[5] = {bytes[6], bytes[7], bytes[8], bytes[9], 0};
                if (strcmp(arr, "none") == 0) {
                    _iconArrangement = DSStoreIconArrangementNone;
                    _hasIconArrangement = YES;
                    NSDebugLLog(@"gwspace", @"║   Arrangement: none");
                } else if (strcmp(arr, "grid") == 0) {
                    _iconArrangement = DSStoreIconArrangementGrid;
                    _hasIconArrangement = YES;
                    NSDebugLLog(@"gwspace", @"║   Arrangement: grid");
                }
                
                // Label position at bytes 10-13
                if (len >= 14) {
                    char lbl[5] = {bytes[10], bytes[11], bytes[12], bytes[13], 0};
                    if (strcmp(lbl, "botm") == 0) {
                        _labelPosition = DSStoreLabelPositionBottom;
                        _hasLabelPosition = YES;
                        NSDebugLLog(@"gwspace", @"║   Label position: bottom");
                    } else if (strcmp(lbl, "rght") == 0) {
                        _labelPosition = DSStoreLabelPositionRight;
                        _hasLabelPosition = YES;
                        NSDebugLLog(@"gwspace", @"║   Label position: right");
                    }
                }
            } else {
                NSDebugLLog(@"gwspace", @"║   ⚠ Unknown icvo format variant (magic: %s)", magic);
            }
        }
    } else {
        NSDebugLLog(@"gwspace", @"║ ○ No icvo (icon view options) entry");
    }
}

- (void)loadIconViewPlistFromStore:(DSStore *)store
{
    // icvp: Modern icon view properties format (10.6+)
    // Binary plist with comprehensive icon view settings
    // Supersedes the older icvo binary format
    
    DSStoreEntry *entry = [store entryForFilename:@"." code:@"icvp"];
    if (entry && [[entry type] isEqualToString:@"blob"]) {
        NSData *data = (NSData *)[entry value];
        NSDebugLLog(@"gwspace", @"║ ✓ icvp (Icon View Plist): %lu bytes", (unsigned long)[data length]);
        
        // Try to parse as binary plist
        NSError *error = nil;
        NSDictionary *plist = [NSPropertyListSerialization propertyListWithData:data
                                                                        options:NSPropertyListImmutable
                                                                         format:NULL
                                                                          error:&error];
        if (plist && [plist isKindOfClass:[NSDictionary class]]) {
            NSDebugLLog(@"gwspace", @"║   Parsed plist keys: %@", [plist allKeys]);
            
            // Extract icon size
            id sizeObj = [plist objectForKey:@"iconSize"];
            if (sizeObj) {
                int size = [sizeObj intValue];
                if (size > 0 && size <= 512) {
                    _iconSize = size;
                    _hasIconSize = YES;
                    NSDebugLLog(@"gwspace", @"║   Icon size from plist: %d", _iconSize);
                }
            }
            
            // Extract arrangement
            id arrObj = [plist objectForKey:@"arrangeBy"];
            if (arrObj) {
                NSString *arr = [arrObj description];
                if ([arr isEqualToString:@"none"] || [arr isEqualToString:@"0"]) {
                    _iconArrangement = DSStoreIconArrangementNone;
                    _hasIconArrangement = YES;
                    NSDebugLLog(@"gwspace", @"║   Arrangement from plist: none");
                } else if ([arr isEqualToString:@"grid"]) {
                    _iconArrangement = DSStoreIconArrangementGrid;
                    _hasIconArrangement = YES;
                    NSDebugLLog(@"gwspace", @"║   Arrangement from plist: grid");
                }
            }
            
            // Extract grid spacing
            id spacingObj = [plist objectForKey:@"gridSpacing"];
            if (spacingObj) {
                _gridSpacing = [spacingObj floatValue];
                _hasGridSpacing = YES;
                NSDebugLLog(@"gwspace", @"║   Grid spacing from plist: %.1f", _gridSpacing);
            }
            
            // Extract label position
            id labelObj = [plist objectForKey:@"labelOnBottom"];
            if (labelObj) {
                _labelPosition = [labelObj boolValue] ? DSStoreLabelPositionBottom : DSStoreLabelPositionRight;
                _hasLabelPosition = YES;
                NSDebugLLog(@"gwspace", @"║   Label position from plist: %@", 
                      _labelPosition == DSStoreLabelPositionBottom ? @"bottom" : @"right");
            }
            
            // Extract background settings
            // Check background type first
            id bgTypeObj = [plist objectForKey:@"backgroundType"];
            int bgType = bgTypeObj ? [bgTypeObj intValue] : 0;
            
            if (bgType == 2) {
                // Picture background
                _backgroundType = DSStoreBackgroundPicture;
                NSDebugLLog(@"gwspace", @"║   Background type from plist: picture (2)");
                
                // Try to extract background image alias
                id bgImageAlias = [plist objectForKey:@"backgroundImageAlias"];
                if (bgImageAlias && [bgImageAlias isKindOfClass:[NSData class]]) {
                    // This is an alias record - try to resolve it to a path
                    NSData *aliasData = (NSData *)bgImageAlias;
                    NSString *resolvedPath = [self resolveAliasData:aliasData relativeTo:_directoryPath];
                    if (resolvedPath) {
                        [_backgroundImagePath release];
                        _backgroundImagePath = [resolvedPath copy];
                        NSDebugLLog(@"gwspace", @"║   Background image from alias: %@", _backgroundImagePath);
                    } else {
                        NSDebugLLog(@"gwspace", @"║   ⚠ Could not resolve background image alias (%lu bytes)", 
                              (unsigned long)[aliasData length]);
                    }
                }
            } else if (bgType == 1) {
                // Color background
                id bgColorObj = [plist objectForKey:@"backgroundColorRed"];
                if (bgColorObj) {
                    CGFloat r = [[plist objectForKey:@"backgroundColorRed"] floatValue];
                    CGFloat g = [[plist objectForKey:@"backgroundColorGreen"] floatValue];
                    CGFloat b = [[plist objectForKey:@"backgroundColorBlue"] floatValue];
                    _backgroundColor = [[NSColor colorWithCalibratedRed:r green:g blue:b alpha:1.0] retain];
                    _backgroundType = DSStoreBackgroundColor;
                    NSDebugLLog(@"gwspace", @"║   Background type from plist: color (1) R=%.2f G=%.2f B=%.2f", r, g, b);
                }
            } else {
                NSDebugLLog(@"gwspace", @"║   Background type from plist: default (0)");
            }
            
        } else {
            NSDebugLLog(@"gwspace", @"║   ⚠ Failed to parse icvp as plist: %@", error);
        }
    } else {
        NSDebugLLog(@"gwspace", @"║ ○ No icvp (icon view plist) entry");
    }
}

- (void)loadBackgroundFromStore:(DSStore *)store
{
    // Check BKGD entry first
    DSStoreEntry *entry = [store entryForFilename:@"." code:@"BKGD"];
    if (entry && [[entry type] isEqualToString:@"blob"]) {
        NSData *data = (NSData *)[entry value];
        if ([data length] >= 4) {
            const char *bytes = (const char *)[data bytes];
            
            if (strncmp(bytes, "DefB", 4) == 0) {
                _backgroundType = DSStoreBackgroundDefault;
                NSDebugLLog(@"gwspace", @"║ ✓ BKGD: Default background (DefB)");
            } else if (strncmp(bytes, "ClrB", 4) == 0) {
                _backgroundType = DSStoreBackgroundColor;
                // External docs: 4CC "ClrB" + RGB in 6 bytes (2 bytes per channel, big-endian)
                if ([data length] >= 10) {
                    const uint8_t *cBytes = (const uint8_t *)bytes;
                    uint16_t rVal = (cBytes[4] << 8) | cBytes[5];
                    uint16_t gVal = (cBytes[6] << 8) | cBytes[7];
                    uint16_t bVal = (cBytes[8] << 8) | cBytes[9];
                    CGFloat r = rVal / 65535.0;
                    CGFloat g = gVal / 65535.0;
                    CGFloat b = bVal / 65535.0;
                    [_backgroundColor release];
                    _backgroundColor = [[NSColor colorWithCalibratedRed:r green:g blue:b alpha:1.0] retain];
                    NSDebugLLog(@"gwspace", @"║ ✓ BKGD: Color (ClrB) R=0x%04x G=0x%04x B=0x%04x (%.3f, %.3f, %.3f)", 
                          rVal, gVal, bVal, r, g, b);
                }
            } else if (strncmp(bytes, "PctB", 4) == 0) {
                _backgroundType = DSStoreBackgroundPicture;
                NSDebugLLog(@"gwspace", @"║ ✓ BKGD: Picture background (PctB)");
                
                // Use DSStore's method to resolve the background image path
                NSString *imagePath = [store backgroundImagePathForDirectory];
                if (imagePath && [imagePath length] > 0) {
                    [_backgroundImagePath release];
                    _backgroundImagePath = [imagePath copy];
                    NSDebugLLog(@"gwspace", @"║   Background image path: %@", _backgroundImagePath);
                } else {
                    NSDebugLLog(@"gwspace", @"║   ⚠ Could not resolve background image path");
                }
            }
        }
    } else {
        NSDebugLLog(@"gwspace", @"║ ○ No BKGD (background) entry");
    }
}

- (void)loadIconGridOptionsFromStore:(DSStore *)store
{
    // icgo: Icon grid options (8 bytes, probably two 32-bit integers)
    DSStoreEntry *entry = [store entryForFilename:@"." code:@"icgo"];
    if (entry && [[entry type] isEqualToString:@"blob"]) {
        NSData *data = (NSData *)[entry value];
        if ([data length] >= 8) {
            const uint8_t *bytes = (const uint8_t *)[data bytes];
            uint32_t val1 = (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3];
            uint32_t val2 = (bytes[4] << 24) | (bytes[5] << 16) | (bytes[6] << 8) | bytes[7];
            NSDebugLLog(@"gwspace", @"║ ✓ icgo (Icon Grid Options): %u, %u", val1, val2);
        }
    } else {
        NSDebugLLog(@"gwspace", @"║ ○ No icgo (icon grid options) entry");
    }
    
    // icsp: Icon spacing (8 bytes)
    entry = [store entryForFilename:@"." code:@"icsp"];
    if (entry && [[entry type] isEqualToString:@"blob"]) {
        NSData *data = (NSData *)[entry value];
        if ([data length] >= 8) {
            const uint8_t *bytes = (const uint8_t *)[data bytes];
            // Usually mostly zeros except last two bytes
            uint16_t spacing = (bytes[6] << 8) | bytes[7];
            NSDebugLLog(@"gwspace", @"║ ✓ icsp (Icon Spacing): %u", spacing);
        }
    } else {
        NSDebugLLog(@"gwspace", @"║ ○ No icsp (icon spacing) entry");
    }
}

- (void)loadListViewSettingsFromStore:(DSStore *)store
{
    // List view settings: Prefer modern plist formats over legacy binary
    // lsvp/lsvP: Modern binary plist (10.6+) - try first
    // lsvo: Legacy 76-byte binary format (pre-10.6) - fallback
    
    // Try modern binary plist formats first (lsvp, lsvP)
    DSStoreEntry *entry = [store entryForFilename:@"." code:@"lsvp"];
    if (!entry) {
        entry = [store entryForFilename:@"." code:@"lsvP"];
    }
    
    if (entry && [[entry type] isEqualToString:@"blob"]) {
        NSData *data = (NSData *)[entry value];
        NSDebugLLog(@"gwspace", @"║ ✓ lsvp/lsvP (List View Properties): %lu bytes", (unsigned long)[data length]);
        
        NSError *error = nil;
        NSDictionary *plist = [NSPropertyListSerialization propertyListWithData:data
                                                                        options:NSPropertyListImmutable
                                                                         format:NULL
                                                                          error:&error];
        if (plist && [plist isKindOfClass:[NSDictionary class]]) {
            NSDebugLLog(@"gwspace", @"║   Parsed plist keys: %@", [plist allKeys]);
            
            // Text size (font size for list entries)
            id textSizeObj = [plist objectForKey:@"textSize"];
            if (textSizeObj && [textSizeObj respondsToSelector:@selector(intValue)]) {
                _listTextSize = [textSizeObj intValue];
                _hasListTextSize = YES;
                NSDebugLLog(@"gwspace", @"║   Text size: %d", _listTextSize);
            }
            
            // Icon size (small icon size in list view)
            id iconSizeObj = [plist objectForKey:@"iconSize"];
            if (iconSizeObj && [iconSizeObj respondsToSelector:@selector(intValue)]) {
                _listIconSize = [iconSizeObj intValue];
                _hasListIconSize = YES;
                NSDebugLLog(@"gwspace", @"║   Icon size: %d", _listIconSize);
            }
            
            // Sort column - the column used for sorting
            id sortColumnObj = [plist objectForKey:@"sortColumn"];
            if (sortColumnObj && [sortColumnObj isKindOfClass:[NSString class]]) {
                [_sortColumn release];
                _sortColumn = [(NSString *)sortColumnObj copy];
                _hasSortColumn = YES;
                NSDebugLLog(@"gwspace", @"║   Sort column: %@", _sortColumn);
            }
            
            // Sort ascending
            id ascendingObj = [plist objectForKey:@"ascending"];
            if (ascendingObj && [ascendingObj respondsToSelector:@selector(boolValue)]) {
                _sortAscending = [ascendingObj boolValue];
                NSDebugLLog(@"gwspace", @"║   Sort ascending: %@", _sortAscending ? @"YES" : @"NO");
            } else {
                _sortAscending = YES;  // Default to ascending
            }
            
            // Column widths dictionary
            id columnsObj = [plist objectForKey:@"columns"];
            if (columnsObj && [columnsObj isKindOfClass:[NSArray class]]) {
                NSMutableDictionary *widths = [NSMutableDictionary dictionary];
                NSMutableDictionary *visible = [NSMutableDictionary dictionary];
                
                for (NSDictionary *col in (NSArray *)columnsObj) {
                    if ([col isKindOfClass:[NSDictionary class]]) {
                        NSString *identifier = [col objectForKey:@"identifier"];
                        if (identifier) {
                            // Column width
                            id widthObj = [col objectForKey:@"width"];
                            if (widthObj && [widthObj respondsToSelector:@selector(floatValue)]) {
                                [widths setObject:[NSNumber numberWithFloat:[widthObj floatValue]]
                                           forKey:identifier];
                            }
                            
                            // Column visibility
                            id visibleObj = [col objectForKey:@"visible"];
                            if (visibleObj && [visibleObj respondsToSelector:@selector(boolValue)]) {
                                [visible setObject:[NSNumber numberWithBool:[visibleObj boolValue]]
                                            forKey:identifier];
                            } else {
                                // Assume visible if not specified
                                [visible setObject:[NSNumber numberWithBool:YES] forKey:identifier];
                            }
                        }
                    }
                }
                
                if ([widths count] > 0) {
                    [_columnWidths release];
                    _columnWidths = [widths copy];
                    NSDebugLLog(@"gwspace", @"║   Column widths: %@", _columnWidths);
                }
                if ([visible count] > 0) {
                    [_columnVisible release];
                    _columnVisible = [visible copy];
                    NSDebugLLog(@"gwspace", @"║   Column visibility: %@", _columnVisible);
                }
            }
        } else {
            NSDebugLLog(@"gwspace", @"║   ⚠ Failed to parse as plist: %@", error);
        }
    } else {
        NSDebugLLog(@"gwspace", @"║ ○ No lsvp/lsvP (list view properties) entry");
    }
    
    // Check for legacy lsvo format (76 bytes)
    entry = [store entryForFilename:@"." code:@"lsvo"];
    if (entry && [[entry type] isEqualToString:@"blob"]) {
        NSData *data = (NSData *)[entry value];
        NSDebugLLog(@"gwspace", @"║ ✓ lsvo (List View Options - Legacy): %lu bytes", (unsigned long)[data length]);
        // TODO: Parse legacy format if needed for older DS_Store files
    }
}


- (void)loadSidebarWidthFromStore:(DSStore *)store
{
    DSStoreEntry *entry = [store entryForFilename:@"." code:@"fwsw"];
    if (entry && [[entry type] isEqualToString:@"long"]) {
        _sidebarWidth = [[entry value] intValue];
        _hasSidebarWidth = YES;
        NSDebugLLog(@"gwspace", @"║ ✓ fwsw (Sidebar Width): %d pixels", _sidebarWidth);
    } else {
        NSDebugLLog(@"gwspace", @"║ ○ No fwsw (sidebar width) entry");
    }
}

- (void)loadIconEntriesFromStore:(DSStore *)store filenames:(NSArray *)filenames
{
    NSDebugLLog(@"gwspace", @"║ --- Per-file entries (icon positions, comments, labels) ---");
    
    NSUInteger positionCount = 0;
    NSUInteger commentCount = 0;
    NSUInteger labelCount = 0;
    
    for (NSString *filename in filenames) {
        // Skip directory entry
        if ([filename isEqualToString:@"."]) continue;
        
        DSStoreIconInfo *info = nil;
        
        // Check for Iloc (icon location)
        DSStoreEntry *ilocEntry = [store entryForFilename:filename code:@"Iloc"];
        if (ilocEntry && [[ilocEntry type] isEqualToString:@"blob"]) {
            NSData *data = (NSData *)[ilocEntry value];
            if ([data length] >= 8) {
                const uint8_t *bytes = (const uint8_t *)[data bytes];
                
                // Iloc format per external docs:
                // - 16-byte blob: Two 4-byte big-endian signed integers for x,y
                // - Coordinates are CENTER of icon (not top-left)
                // - Origin at top-left of window content area
                // - Remaining 8 bytes: 6 bytes 0xff + 2 bytes 0x00
                int32_t x = (int32_t)((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]);
                int32_t y = (int32_t)((bytes[4] << 24) | (bytes[5] << 16) | (bytes[6] << 8) | bytes[7]);
                
                if (!info) {
                    info = [DSStoreIconInfo infoForFilename:filename];
                }
                info.position = NSMakePoint((CGFloat)x, (CGFloat)y);
                info.hasPosition = YES;
                positionCount++;
                
                NSDebugLLog(@"gwspace", @"║   Iloc '%@': (%d, %d) [icon center]", filename, x, y);
            }
        }
        
        // Check for cmmt (comments)
        DSStoreEntry *cmmtEntry = [store entryForFilename:filename code:@"cmmt"];
        if (cmmtEntry && [[cmmtEntry type] isEqualToString:@"ustr"]) {
            if (!info) {
                info = [DSStoreIconInfo infoForFilename:filename];
            }
            info.comments = (NSString *)[cmmtEntry value];
            commentCount++;
            NSDebugLLog(@"gwspace", @"║   cmmt '%@': \"%@\"", filename, info.comments);
        }
        
        // Check for lclr (label color)
        DSStoreEntry *lclrEntry = [store entryForFilename:filename code:@"lclr"];
        if (lclrEntry && [[lclrEntry type] isEqualToString:@"long"]) {
            if (!info) {
                info = [DSStoreIconInfo infoForFilename:filename];
            }
            int32_t colorValue = [[lclrEntry value] intValue];
            info.labelColor = (DSStoreLabelColor)colorValue;
            info.hasLabelColor = YES;
            labelCount++;
            
            NSString *colorName = @"none";
            switch (colorValue) {
                case 1: colorName = @"red"; break;
                case 2: colorName = @"orange"; break;
                case 3: colorName = @"yellow"; break;
                case 4: colorName = @"green"; break;
                case 5: colorName = @"blue"; break;
                case 6: colorName = @"purple"; break;
                case 7: colorName = @"grey"; break;
            }
            NSDebugLLog(@"gwspace", @"║   lclr '%@': %d (%@)", filename, colorValue, colorName);
        }
        
        // Store the info if we have any data
        if (info) {
            [_iconInfoDict setObject:info forKey:filename];
        }
    }
    
    NSDebugLLog(@"gwspace", @"║ Total icon positions found: %lu", (unsigned long)positionCount);
    NSDebugLLog(@"gwspace", @"║ Total comments found: %lu", (unsigned long)commentCount);
    NSDebugLLog(@"gwspace", @"║ Total label colors found: %lu", (unsigned long)labelCount);
}

#pragma mark - Icon Position Access

- (DSStoreIconInfo *)iconInfoForFilename:(NSString *)filename
{
    return [_iconInfoDict objectForKey:filename];
}

- (void)setIconInfo:(DSStoreIconInfo *)iconInfo forFilename:(NSString *)filename
{
    if (iconInfo && filename) {
        [_iconInfoDict setObject:iconInfo forKey:filename];
    }
}

- (NSDictionary *)allIconInfo
{
    return [NSDictionary dictionaryWithDictionary:_iconInfoDict];
}

- (BOOL)hasAnyIconPositions
{
    for (DSStoreIconInfo *info in [_iconInfoDict allValues]) {
        if (info.hasPosition) {
            return YES;
        }
    }
    return NO;
}

- (NSArray *)filenamesWithPositions
{
    NSMutableArray *result = [NSMutableArray array];
    for (NSString *filename in _iconInfoDict) {
        DSStoreIconInfo *info = [_iconInfoDict objectForKey:filename];
        if (info.hasPosition) {
            [result addObject:filename];
        }
    }
    return result;
}

#pragma mark - Coordinate Conversion

- (NSRect)gnustepWindowFrameForScreen:(NSScreen *)screen
{
    if (!_hasWindowFrame) {
        return NSZeroRect;
    }
    
    // IMPORTANT: .DS_Store fwi0 stores the CONTENT AREA rect (excluding titlebar/chrome)
    // 
    // .DS_Store format: origin at TOP-LEFT of screen
    // - _windowFrame.origin.y is the TOP edge of CONTENT area (distance from top of screen downward)
    // - Smaller y values = closer to top of screen
    // 
    // GNUstep format: origin at BOTTOM-LEFT of screen
    // - y is distance from bottom of screen upward
    // - Larger y values = closer to top of screen
    //
    // This method returns the CONTENT AREA rect in GNUstep coordinates.
    // The caller must convert to full window frame using [NSWindow frameRectForContentRect:]
    //
    // Conversion: gnustep_y = screenHeight - dsstore_top - content_height
    CGFloat screenHeight = [screen frame].size.height;
    
    // _windowFrame.origin.y contains the TOP edge of content area from .DS_Store
    CGFloat dsStoreTop = _windowFrame.origin.y;
    CGFloat contentHeight = _windowFrame.size.height;
    
    // Calculate bottom edge position of content area in GNUstep coordinates
    CGFloat gnustepY = screenHeight - dsStoreTop - contentHeight;
    
    NSRect result = NSMakeRect(_windowFrame.origin.x, gnustepY, 
                               _windowFrame.size.width, contentHeight);
    
    NSDebugLLog(@"gwspace", @"Coordinate conversion:");
    NSDebugLLog(@"gwspace", @"  .DS_Store content area: top=%.0f left=%.0f width=%.0f height=%.0f", 
          dsStoreTop, _windowFrame.origin.x, _windowFrame.size.width, contentHeight);
    NSDebugLLog(@"gwspace", @"  Screen height: %.0f", screenHeight);
    NSDebugLLog(@"gwspace", @"  GNUstep content rect: %@", NSStringFromRect(result));
    
    return result;
}

- (NSPoint)gnustepPositionForDSStorePoint:(NSPoint)dsPoint 
                           viewHeight:(CGFloat)viewHeight 
                           iconHeight:(CGFloat)iconHeight
{
    // Delegate to DSStore class method for .DS_Store interoperability coordinate conversion
    return [DSStore gnustepPointFromDSStorePoint:dsPoint viewHeight:viewHeight iconHeight:iconHeight];
}

#pragma mark - Writing / Saving

- (BOOL)save
{
  return [self saveToPath:[_directoryPath stringByAppendingPathComponent:@".DS_Store"]];
}

- (BOOL)saveToPath:(NSString *)dsStorePath
{
  if (!dsStorePath || [dsStorePath length] == 0) return NO;

  NSDebugLLog(@"gwspace", @"╔══════════════════════════════════════════════════════════════════╗");
  NSDebugLLog(@"gwspace", @"║             DS_STORE WRITE: %@", dsStorePath);
  NSDebugLLog(@"gwspace", @"╠══════════════════════════════════════════════════════════════════╣");

  /* Build the DSStore object with all current settings */
  DSStore *store = [DSStore createStoreAtPath:dsStorePath withEntries:nil];
  if (!store) {
    NSDebugLLog(@"gwspace", @"║ ✗ Failed to create DSStore for %@", dsStorePath);
    return NO;
  }

  [store load];  /* Load existing entries so we merge, not replace */

  /* --- Directory-level entries (filename = ".") --- */

  /* View style */
  if (_hasViewStyle) {
    NSString *styleStr = @"icnv";
    switch (_viewStyle) {
      case DSStoreViewStyleIcon:     styleStr = @"icnv"; break;
      case DSStoreViewStyleList:     styleStr = @"Nlsv"; break;
      case DSStoreViewStyleColumn:   styleStr = @"clmv"; break;
      case DSStoreViewStyleGallery:  styleStr = @"glyv"; break;
      case DSStoreViewStyleCoverflow:styleStr = @"Flwv"; break;
    }
    DSStoreEntry *e = [DSStoreEntry viewStyleEntryForFile:@"." style:styleStr];
    if (e) [store setEntry:e];
  }

  /* Icon size */
  if (_hasIconSize && _iconSize > 0 && _iconSize <= 512) {
    DSStoreEntry *e = [DSStoreEntry iconSizeEntryForFile:@"." size:_iconSize];
    if (e) [store setEntry:e];
  }

  /* Icon arrangement */
  if (_hasIconArrangement) {
    int arr = (_iconArrangement == DSStoreIconArrangementGrid) ? 1 : 0;
    DSStoreEntry *e = [DSStoreEntry iconArrangementEntryForFile:@"." arrangement:arr];
    if (e) [store setEntry:e];
  }

  /* Label position */
  if (_hasLabelPosition) {
    int pos = (_labelPosition == DSStoreLabelPositionBottom) ? 0 : 1;
    DSStoreEntry *e = [DSStoreEntry labelPositionEntryForFile:@"." position:pos];
    if (e) [store setEntry:e];
  }

  /* Grid spacing */
  if (_hasGridSpacing && _gridSpacing > 0) {
    DSStoreEntry *e = [DSStoreEntry gridSpacingEntryForFile:@"." spacing:(int)_gridSpacing];
    if (e) [store setEntry:e];
  }

  /* Background color */
  if (_backgroundType == DSStoreBackgroundColor && _backgroundColor) {
    CGFloat r, g, b, a;
    [_backgroundColor getRed:&r green:&g blue:&b alpha:&a];
    int ri = (int)(r * 65535.0);
    int gi = (int)(g * 65535.0);
    int bi = (int)(b * 65535.0);
    DSStoreEntry *e = [DSStoreEntry backgroundColorEntryForFile:@"."
                                                           red:ri
                                                         green:gi
                                                          blue:bi];
    if (e) [store setEntry:e];
  }

  /* Background image */
  if (_backgroundType == DSStoreBackgroundPicture && _backgroundImagePath) {
    DSStoreEntry *e = [DSStoreEntry backgroundImageEntryForFile:@"."
                                                     imagePath:_backgroundImagePath];
    if (e) [store setEntry:e];
  }

  /* Sidebar width */
  if (_hasSidebarWidth) {
    DSStoreEntry *e = [DSStoreEntry sidebarWidthEntryForFile:@"." width:_sidebarWidth];
    if (e) [store setEntry:e];
  }

  /* List view text size */
  if (_hasListTextSize) {
    DSStoreEntry *e = [DSStoreEntry textSizeEntryForFile:@"." size:_listTextSize];
    if (e) [store setEntry:e];
  }

  /* Sort column */
  if (_hasSortColumn && _sortColumn) {
    DSStoreEntry *e = [DSStoreEntry sortByEntryForFile:@"." sortBy:_sortColumn];
    if (e) [store setEntry:e];
  }

  /* --- Per-file entries --- */
  for (NSString *filename in _iconInfoDict) {
    DSStoreIconInfo *info = [_iconInfoDict objectForKey:filename];

    /* Icon position (Iloc) */
    if (info.hasPosition) {
      DSStoreEntry *e = [DSStoreEntry iconLocationEntryForFile:filename
                                                             x:(int)info.position.x
                                                             y:(int)info.position.y];
      if (e) [store setEntry:e];
    }

    /* Comment */
    if (info.comments) {
      DSStoreEntry *e = [DSStoreEntry commentsEntryForFile:filename
                                                  comments:info.comments];
      if (e) [store setEntry:e];
    }

    /* Label color */
    if (info.hasLabelColor) {
      DSStoreEntry *e = [DSStoreEntry labelColorEntryForFile:filename
                                                       color:(int)info.labelColor];
      if (e) [store setEntry:e];
    }
  }

  /* --- Write atomically --- */
  BOOL saved = [store save];
  if (saved) {
    NSDebugLLog(@"gwspace", @"║ ✓ Successfully wrote %@", dsStorePath);
  } else {
    NSDebugLLog(@"gwspace", @"║ ✗ Failed to write %@", dsStorePath);
  }

  NSDebugLLog(@"gwspace", @"╚══════════════════════════════════════════════════════════════════╝");

  return saved;
}

- (void)takeValuesFromViewerPrefs:(NSDictionary *)prefs
{
  [self takeValuesFromViewerPrefs:prefs preservingExisting:NO];
}

- (void)takeValuesFromViewerPrefs:(NSDictionary *)prefs
                preservingExisting:(BOOL)preserve
{
  if (!prefs) return;

  /* Window geometry */
  NSString *geo = [prefs objectForKey:@"geometry"];
  if (geo && !(preserve && _hasWindowFrame)) {
    NSRect frame = NSRectFromString(geo);
    if (frame.size.width > 0 && frame.size.height > 0) {
      _windowFrame = frame;
      _hasWindowFrame = YES;
    }
  }

  /* View type */
  NSString *vt = [prefs objectForKey:@"viewtype"];
  if (vt && !(preserve && _hasViewStyle)) {
    if ([vt isEqualToString:@"Icon"]) {
      _viewStyle = DSStoreViewStyleIcon;
    } else if ([vt isEqualToString:@"List"]) {
      _viewStyle = DSStoreViewStyleList;
    } else if ([vt isEqualToString:@"Browser"]) {
      _viewStyle = DSStoreViewStyleColumn;
    }
    _hasViewStyle = YES;
  }

  /* Icon size */
  id iconSizeObj = [prefs objectForKey:@"iconsize"];
  if (iconSizeObj && !(preserve && _hasIconSize)) {
    int sz = [iconSizeObj intValue];
    if (sz > 0 && sz <= 512) {
      _iconSize = sz;
      _hasIconSize = YES;
    }
  }

  /* Icon position (label position) */
  NSString *ip = [prefs objectForKey:@"iconspos"];
  if (ip && !(preserve && _hasLabelPosition)) {
    _labelPosition = [ip isEqualToString:@"bottom"] ? DSStoreLabelPositionBottom
                     : DSStoreLabelPositionRight;
    _hasLabelPosition = YES;
  }

  /* Icon arrangement */
  NSString *ia = [prefs objectForKey:@"iconsarr"];
  if (ia && !(preserve && _hasIconArrangement)) {
    _iconArrangement = [ia isEqualToString:@"grid"] ? DSStoreIconArrangementGrid
                       : DSStoreIconArrangementNone;
    _hasIconArrangement = YES;
  }

  /* Sidebar width */
  id sw = [prefs objectForKey:@"sidebarwidth"];
  if (sw && !(preserve && _hasSidebarWidth)) {
    _sidebarWidth = [sw intValue];
    _hasSidebarWidth = YES;
  }
}

- (void)resetToDefaults
{
  _hasWindowFrame = NO;
  _hasViewStyle = NO;
  _hasIconSize = NO;
  _hasIconArrangement = NO;
  _hasLabelPosition = NO;
  _hasGridSpacing = NO;
  _hasSidebarWidth = NO;
  _hasListTextSize = NO;
  _hasListIconSize = NO;
  _hasSortColumn = NO;

  _backgroundType = DSStoreBackgroundDefault;
  [_backgroundColor release];  _backgroundColor = nil;
  [_backgroundImagePath release];  _backgroundImagePath = nil;
  [_sortColumn release];  _sortColumn = nil;
  [_columnWidths release];  _columnWidths = nil;
  [_columnVisible release];  _columnVisible = nil;
  [_iconInfoDict removeAllObjects];

  _loaded = NO;
}

#pragma mark - Debugging

- (NSString *)debugDescription
{
    NSMutableString *desc = [NSMutableString string];
    [desc appendFormat:@"<DSStoreInfo: %@>\n", _directoryPath];
    [desc appendFormat:@"  loaded: %@\n", _loaded ? @"YES" : @"NO"];
    
    if (_hasWindowFrame) {
        [desc appendFormat:@"  windowFrame: %@\n", NSStringFromRect(_windowFrame)];
    }
    if (_hasViewStyle) {
        NSString *styleName = @"unknown";
        switch (_viewStyle) {
            case DSStoreViewStyleIcon: styleName = @"icon"; break;
            case DSStoreViewStyleList: styleName = @"list"; break;
            case DSStoreViewStyleColumn: styleName = @"column"; break;
            case DSStoreViewStyleGallery: styleName = @"gallery"; break;
            case DSStoreViewStyleCoverflow: styleName = @"coverflow"; break;
        }
        [desc appendFormat:@"  viewStyle: %@\n", styleName];
    }
    if (_hasIconSize) {
        [desc appendFormat:@"  iconSize: %d\n", _iconSize];
    }
    if (_hasIconArrangement) {
        [desc appendFormat:@"  iconArrangement: %@\n", 
         _iconArrangement == DSStoreIconArrangementNone ? @"none (free)" : @"grid"];
    }
    if (_hasLabelPosition) {
        [desc appendFormat:@"  labelPosition: %@\n",
         _labelPosition == DSStoreLabelPositionBottom ? @"bottom" : @"right"];
    }
    if (_backgroundColor) {
        [desc appendFormat:@"  backgroundColor: %@\n", _backgroundColor];
    }
    [desc appendFormat:@"  iconPositions: %lu files\n", (unsigned long)[self filenamesWithPositions].count];
    
    return desc;
}

- (void)logAllInfo
{
    NSDebugLLog(@"gwspace", @"╔══════════════════════════════════════════════════════════════════╗");
    NSDebugLLog(@"gwspace", @"║                   DS_STORE INFO SUMMARY                          ║");
    NSDebugLLog(@"gwspace", @"╠══════════════════════════════════════════════════════════════════╣");
    NSDebugLLog(@"gwspace", @"║ Directory: %@", _directoryPath);
    NSDebugLLog(@"gwspace", @"╟──────────────────────────────────────────────────────────────────╢");
    
    if (_hasWindowFrame) {
        NSDebugLLog(@"gwspace", @"║ Window Geometry: x=%.0f y=%.0f w=%.0f h=%.0f", 
              _windowFrame.origin.x, _windowFrame.origin.y,
              _windowFrame.size.width, _windowFrame.size.height);
    } else {
        NSDebugLLog(@"gwspace", @"║ Window Geometry: (not set)");
    }
    
    if (_hasViewStyle) {
        NSString *styleName = @"unknown";
        switch (_viewStyle) {
            case DSStoreViewStyleIcon: styleName = @"Icon"; break;
            case DSStoreViewStyleList: styleName = @"List"; break;
            case DSStoreViewStyleColumn: styleName = @"Column"; break;
            case DSStoreViewStyleGallery: styleName = @"Gallery"; break;
            case DSStoreViewStyleCoverflow: styleName = @"Coverflow"; break;
        }
        NSDebugLLog(@"gwspace", @"║ View Style: %@", styleName);
    } else {
        NSDebugLLog(@"gwspace", @"║ View Style: (not set, defaulting to Icon)");
    }
    
    if (_hasIconSize) {
        NSDebugLLog(@"gwspace", @"║ Icon Size: %d pixels", _iconSize);
    } else {
        NSDebugLLog(@"gwspace", @"║ Icon Size: (not set, using default)");
    }
    
    if (_hasIconArrangement) {
        NSDebugLLog(@"gwspace", @"║ Icon Arrangement: %@", 
              _iconArrangement == DSStoreIconArrangementNone ? 
              @"NONE (free positioning enabled)" : @"Grid (snapping enabled)");
    } else {
        NSDebugLLog(@"gwspace", @"║ Icon Arrangement: (not set)");
    }
    
    if (_hasLabelPosition) {
        NSDebugLLog(@"gwspace", @"║ Label Position: %@",
              _labelPosition == DSStoreLabelPositionBottom ? @"Bottom" : @"Right");
    }
    
    if (_backgroundColor) {
        NSDebugLLog(@"gwspace", @"║ Background: Color - %@", _backgroundColor);
    } else if (_backgroundImagePath) {
        NSDebugLLog(@"gwspace", @"║ Background: Image - %@", _backgroundImagePath);
    } else {
        NSDebugLLog(@"gwspace", @"║ Background: Default");
    }
    
    NSArray *positionedFiles = [self filenamesWithPositions];
    NSDebugLLog(@"gwspace", @"╟──────────────────────────────────────────────────────────────────╢");
    NSDebugLLog(@"gwspace", @"║ Icons with custom positions: %lu", (unsigned long)[positionedFiles count]);
    
    for (NSString *filename in positionedFiles) {
        DSStoreIconInfo *info = [_iconInfoDict objectForKey:filename];
        NSDebugLLog(@"gwspace", @"║   '%@' -> (%.0f, %.0f)", filename, info.position.x, info.position.y);
    }
    
    NSDebugLLog(@"gwspace", @"╚══════════════════════════════════════════════════════════════════╝");
}

@end
