//-----------------------------------------------------------------------------
// SokoFiles.M
//
//	Utility routines for manipulating filenames.
//
// Copyright (c), 1997, Paul McCarthy.  All rights reserved.
// Copyright (c), 1997, Eric Sunshine.  All rights reserved.
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// $Id: SokoFiles.M,v 1.2 97/12/10 06:58:35 sunshine Exp $
// $Log:	SokoFiles.M,v $
//  Revision 1.2  97/12/10  06:58:35  sunshine
//  v10.1: Ported to OPENSTEP 4.1 for Mach, OPENSTEP 4.2 for Mach & Windows,
//  and Rhapsody Developer Release (RDR) for Mach and Windows (Yellow Box).
//  Now handles DOS-style and Microsoft UNC-style paths.
//  
//  Revision 1.1  97/11/13  02:55:26  zarnuk
//  v9
//-----------------------------------------------------------------------------
#import "SokoFiles.h"
#import	"SokoDefs.h"
extern "Objective-C" {
#import <Foundation/NSBundle.h>
#import <Foundation/NSCharacterSet.h>
#import <Foundation/NSFileManager.h>
#import <Foundation/NSPathUtilities.h>
#import <Foundation/NSScanner.h>
#import <Foundation/NSUserDefaults.h>
}
extern "C" {
#import <stdlib.h>
}

static NSString* const mazeExtension = @"sokomaze";
static NSString* const saveExtension = @"sokosave";

inline static NSString* get_def( NSString* key )
	{ return [[NSUserDefaults standardUserDefaults] stringForKey:key]; }

NSString* getMazeExtension()		{ return mazeExtension; }
NSString* getSaveExtension()		{ return saveExtension; }
NSString* getSaveDirectory()		{ return get_def( SAVE_DIR ); }
NSString* getMazeDirectory()		{ return get_def( MAZE_DIR ); }
NSString* getScoresFile()		{ return get_def( SCORES ); }
NSString* getFactoryScoresFile()	{ return FACTORY_SCORES; }


//-----------------------------------------------------------------------------
// collapse
//	Collapse a full-path to "$(SokoSave)/filename" if it resides in the
//	application wrapper.
//-----------------------------------------------------------------------------
NSString* collapse( NSString* filename )
    {
    NSString* s = filename;

    NSArray* rParts = [[[NSBundle mainBundle] resourcePath] pathComponents];
    NSArray* fParts = [filename pathComponents];
    unsigned int rLim = [rParts count];
    unsigned int fLim = [fParts count];

    unsigned int i;
    for (i = 0; i < rLim && i < fLim; i++)
	if (![[rParts objectAtIndex:i] isEqual:[fParts objectAtIndex:i]])
	    break;

    if (i == rLim)
	s = [NSString pathWithComponents:[[NSArray arrayWithObject:APPDIR]
		arrayByAddingObjectsFromArray:[fParts subarrayWithRange:
		NSMakeRange( i, fLim - i )]]];
    return s;
    }


//-----------------------------------------------------------------------------
// expand_macro
//-----------------------------------------------------------------------------
static NSString* expand_macro( NSString* input )
    {
    static NSCharacterSet* const s_dollar =
	[[NSCharacterSet characterSetWithCharactersInString:@"$"] retain];
    static NSCharacterSet* const s_open =
	[[NSCharacterSet characterSetWithCharactersInString:@"{("] retain];
    static NSCharacterSet* const s_close =
	[[NSCharacterSet characterSetWithCharactersInString:@")}"] retain];
    static NSCharacterSet* s_env = 0;
    if (s_env == 0)
	{
	NSMutableCharacterSet* s = 
		[[NSCharacterSet alphanumericCharacterSet] mutableCopy];
	[s addCharactersInString:@"_"];
	s_env = [s copy];
	}

    NSString* output = @"";
    NSScanner* scanner = [NSScanner scannerWithString:input];
    [scanner scanUpToCharactersFromSet:s_dollar intoString:&output];

    if (![scanner isAtEnd])
	{
	NSString* env = @"";
	[scanner scanCharactersFromSet:s_dollar intoString:0];
	if ([scanner scanCharactersFromSet:s_open intoString:0])
	    {
	    [scanner scanUpToCharactersFromSet:s_close intoString:&env];
	    [scanner scanCharactersFromSet:s_close intoString:0];
	    }
	else
	    [scanner scanCharactersFromSet:s_env intoString:&env];

	if (![env isEqualToString:@""])
	    {
	    if ([env isEqualToString:APPNAME])
		output = [output stringByAppendingString:
			[[NSBundle mainBundle] resourcePath]];
	    else
		{
		char const* s = getenv( [env cString] );
		if (s != 0)
		    output = [output stringByAppendingString:
			[NSString stringWithCString:s]];
		}
	    }
	}

    if (![scanner isAtEnd])
	output = [output stringByAppendingString:
		[input substringFromIndex:[scanner scanLocation]]];

    return output;
    }


//-----------------------------------------------------------------------------
// expand
//-----------------------------------------------------------------------------
NSString* expand( NSString* filename )
    {
    NSString* s = filename;
    NSCharacterSet* const dollar =
	[NSCharacterSet characterSetWithCharactersInString:@"$"];
    while ([s rangeOfCharacterFromSet:dollar].length != 0)
	s = expand_macro(s);
    return [s stringByExpandingTildeInPath];
    }


//-----------------------------------------------------------------------------
// saveFileNameForMazeName
//-----------------------------------------------------------------------------
NSString* saveFileNameForMazeName( NSString* mazeName )
    {
    return [getSaveDirectory() stringByAppendingPathComponent:
	[[[mazeName lastPathComponent] stringByDeletingPathExtension]
	stringByAppendingPathExtension:getSaveExtension()]];
    }


//-----------------------------------------------------------------------------
// mazeNameForLevel
//-----------------------------------------------------------------------------
NSString* mazeNameForLevel( int level )
    {
    return [[expand(getMazeDirectory()) stringByAppendingPathComponent:
	[NSString stringWithFormat:@"%02d", level]]
	stringByAppendingPathExtension:getMazeExtension()];
    }


//-----------------------------------------------------------------------------
// filenamePart
//-----------------------------------------------------------------------------
NSString* filenamePart( NSString* path )
    {
    return [path lastPathComponent];
    }


//-----------------------------------------------------------------------------
// directoryPart
//-----------------------------------------------------------------------------
NSString* directoryPart( NSString* path )
    {
    return [path stringByDeletingLastPathComponent];
    }


//-----------------------------------------------------------------------------
// mkdirs
//	NOTE *1*: Worked all the way back up to root of filesystem and were
//		unsuccesful in creating any intermediate directories.
//	NOTE *2*: Did not push anything new onto stack.
//-----------------------------------------------------------------------------
BOOL mkdirs( NSString* path )
    {
    NSFileManager* const manager = [NSFileManager defaultManager];
    NSMutableArray* const stack = [NSMutableArray arrayWithObject:path];
    unsigned int lim;
    while ((lim = [stack count]) != 0)
	{
	NSString* const dir = [stack lastObject];
	if (![manager fileExistsAtPath:dir])
	    {
	    if (![manager createDirectoryAtPath:dir attributes:0])
		{
		NSString* const up = [dir stringByDeletingLastPathComponent];
		NSString* const upup = [up stringByDeletingLastPathComponent];
		if ([upup isEqualToString:@""] || [upup isEqualToString:up])
		    break;			// NOTE *1*
		[stack addObject:up];
		}
	    }
	if ([stack count] == lim)		// NOTE *2*
	    [stack removeLastObject];
	}

    BOOL success = NO, isDir;
    if ([manager fileExistsAtPath:path isDirectory:&isDir])
	success = isDir;
    return success;
    }
