//
//  MPWGenericReference.m
//  MPWFoundation
//
//  Created by Marcel Weiher on 5/22/18.
//

#import "MPWGenericReference.h"
#import "AccessorMacros.h"

@interface MPWGenericReference()

@property (nonatomic, strong) NSArray *pathComponents;


@end

@implementation MPWGenericReference

@synthesize schemeName;

-(NSArray*)componentsOfPath:(NSString*)path
{
    NSArray *components = [path componentsSeparatedByString:@"/"];
    if ( components.count == 1 && [components.firstObject length] == 0) {
        components=@[];
    }
    return components;
}

-(instancetype)initWithPathComponents:(NSArray*)pathComponents scheme:(NSString*)scheme
{
    self=[super init];
    self.pathComponents=pathComponents;
    self.schemeName=scheme;
    return self;
}

CONVENIENCEANDINIT( reference, WithPath:(NSString*)path )
{
    return [self initWithPathComponents:[self componentsOfPath:path] scheme:nil];
}

-(BOOL)isRoot
{
    NSArray *components=self.pathComponents;
    return components.count == 2 && [components[0] length]==0 && [components[1] length]==0;
}

-(BOOL)isAbsolute
{
    NSArray *components=self.pathComponents;
    return components.count >0 && [components[0] length]==0;
}

-(BOOL)hasTrailingSlash
{
    return [self.pathComponents.lastObject length]==0;
}

-(instancetype)referenceByAppendingReference:(MPWGenericReference*)other
{
    NSArray *compinedPath=[[self pathComponents] arrayByAddingObjectsFromArray:[other relativePathComponents]];
    return [[[[self class] alloc] initWithPathComponents:compinedPath scheme:self.schemeName] autorelease];
}

-(NSArray*)relativePathComponents
{
    return [super relativePathComponents];      // shut up the compiler
}

-(NSString*)stringValue
{
    return [self path];
}

-(BOOL)isEqual:other
{
    return [[self pathComponents] isEqual:[other pathComponents]];
}

-(void)dealloc
{
    [_pathComponents release];
    [schemeName release];
    [super dealloc];
}

// FIXME: legacy/compatibility

-(NSString*)path
{
    return [self.pathComponents componentsJoinedByString:@"/"];
}

-(void)setPath:(NSString*)path
{
    self.pathComponents = [self componentsOfPath:path];
}

-(NSURL *)URL
{
    NSMutableString *s=[NSMutableString string];
    if ( [self schemeName]) {
        [s appendString:[self schemeName]];
        [s appendString:@":"];
    }
    [s appendString:[self path]];
    return [NSURL URLWithString:s];
}

-(NSString*)name
{
    return [self path];
}

-(NSString*)identifierName
{
    return [self path];
}



@end

#import "DebugMacros.h"


@implementation MPWReferenceTests


+classUnderTest
{
    return [MPWGenericReference class];
}

+ref:(NSString*)path
{
    return [[self classUnderTest] referenceWithPath:path];
}

+(void)testIdentifyRoot
{
    EXPECTTRUE([[self ref:@"/"] isRoot], @"isRoot");
    EXPECTFALSE([[self ref:@"/absolute"] isRoot], @"absolute isRoot");
    EXPECTFALSE([[self ref:@"relative"] isRoot], @"relative isRoot");
}

+(void)testIdentifyAbsolute
{
    EXPECTTRUE([[self ref:@"/"] isAbsolute], @"isRoot");
    EXPECTFALSE([[self ref:@""] isAbsolute], @"isRoot");
    EXPECTTRUE([[self ref:@"/absolute"] isAbsolute], @"absolute isRoot");
    EXPECTFALSE([[self ref:@"relative"] isAbsolute], @"relative isRoot");
    EXPECTFALSE([[self ref:@"relative/path"] isAbsolute], @"relative/path isRoot");
}

+(void)testIdentifyTrailingSlash
{
    EXPECTTRUE([[self ref:@"/"] hasTrailingSlash], @"/ hasTrailingSlash");
    EXPECTTRUE([[self ref:@"trail/"] hasTrailingSlash], @"trail/ hasTrailingSlash");
    EXPECTFALSE([[self ref:@"notrail"] hasTrailingSlash], @"notrail hasTrailingSlash");
    EXPECTFALSE([[self ref:@"relative/path"] hasTrailingSlash], @"relative/path hasTrailingSlash");
}

+(void)testReturnsSamePath
{
    IDEXPECT([[self ref:@"/"] path], @"/",@"path");
    IDEXPECT([[self ref:@"/absolute"] path], @"/absolute",@"path");
    IDEXPECT([[self ref:@"relative"] path], @"relative",@"path");
    IDEXPECT([[self ref:@"trail/"] path], @"trail/",@"path");
    IDEXPECT([[self ref:@"relative/path"] path], @"relative/path",@"path");
}

+(void)testCleanedPath
{
    IDEXPECT([[self ref:@"/absolute"] relativePathComponents], @[ @"absolute"] ,@"cleanedPathComponents");
    IDEXPECT([[self ref:@"relative"] relativePathComponents], @[@"relative"],@"path");
    IDEXPECT([[self ref:@"trail/"] relativePathComponents], @[@"trail"],@"path");
    IDEXPECT([[self ref:@"relative/path"] relativePathComponents], (@[@"relative",@"path"]),@"relative");
    IDEXPECT([[self ref:@"/"] relativePathComponents], @[] ,@"cleanedPathComponents");
    IDEXPECT([[self ref:@""] relativePathComponents], @[] ,@"cleanedPathComponents");
}

+(void)testURL {}

+(void)testAppendPath
{
    MPWGenericReference *base=[self ref:@"base"];
    MPWGenericReference *relative=[self ref:@"relative"];
    MPWGenericReference *composite=[base referenceByAppendingReference:relative];
    IDEXPECT([composite path], @"base/relative", @"simplest composition");
}

+(void)testEquality
{
    EXPECTTRUE( [[self ref:@"/"] isEqual: [self ref:@"/"]],@"root is equal to self");
}


+testSelectors
{
    return @[
             @"testIdentifyRoot",
             @"testIdentifyAbsolute",
             @"testIdentifyTrailingSlash",
             @"testURL",
             @"testReturnsSamePath",
             @"testCleanedPath",
             @"testAppendPath",
             @"testEquality",
             ];
}

@end
