//
// LNStandardVersionComparator.m
// Sparkle
//
// Created by Andy Matuschak on 12/21/07.
// Copyright 2007 Andy Matuschak. All rights reserved.
//

#import <Cocoa/Cocoa.h>
#import "LNStandardVersionComparator.h"

@implementation LNStandardVersionComparator

+(LNStandardVersionComparator*) defaultComparator {
  static LNStandardVersionComparator* defaultComparator = nil;

  if (defaultComparator == nil) defaultComparator = [[LNStandardVersionComparator alloc] init];
  return defaultComparator;
}

typedef enum {
  kNumberType,
  kStringType,
  kPeriodType
} SUCharacterType;

-(SUCharacterType) typeOfCharacter:(NSString*)character {
  if ([character isEqualToString:@"."]) {
    return kPeriodType;
  } else if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:[character characterAtIndex:0]]) {
    return kNumberType;
  } else {
    return kStringType;
  }
}

-(NSArray*) splitVersionString:(NSString*)version {
  NSString* character;
  NSMutableString* s;
  NSUInteger i, n;
  SUCharacterType oldType, newType;
  NSMutableArray* parts = [NSMutableArray array];

  if ([version length] == 0) {
    // Nothing to do here
    return parts;
  }
  s = [[[version substringToIndex:1] mutableCopy] autorelease];
  oldType = [self typeOfCharacter:s];
  n = [version length] - 1;
  for (i = 1; i <= n; ++i) {
    character = [version substringWithRange:NSMakeRange(i, 1)];
    newType = [self typeOfCharacter:character];
    if ((oldType != newType) || (oldType == kPeriodType)) {
      // We've reached a new segment
      NSString* aPart = [[[NSString alloc] initWithString:s] autorelease];
      [parts addObject:aPart];
      [s setString:character];
    } else {
      // Add character to string and continue
      [s appendString:character];
    }
    oldType = newType;
  }

  // Add the last part onto the array
  [parts addObject:[NSString stringWithString:s]];
  return parts;
}

-(NSComparisonResult) compareVersion:(NSString*)versionA toVersion:(NSString*)versionB;
{
  NSArray* partsA = [self splitVersionString:versionA];
  NSArray* partsB = [self splitVersionString:versionB];

  NSString* partA, * partB;
  NSUInteger i, n;
  int intA, intB;
  SUCharacterType typeA, typeB;

  n = MIN([partsA count], [partsB count]);
  for (i = 0; i < n; ++i) {
    partA = [partsA objectAtIndex:i];
    partB = [partsB objectAtIndex:i];

    typeA = [self typeOfCharacter:partA];
    typeB = [self typeOfCharacter:partB];

    // Compare types
    if (typeA == typeB) {
      // Same type; we can compare
      if (typeA == kNumberType) {
        intA = [partA intValue];
        intB = [partB intValue];
        if (intA > intB) {
          return NSOrderedDescending;
        } else if (intA < intB) {
          return NSOrderedAscending;
        }
      } else if (typeA == kStringType) {
        NSComparisonResult result = [partA compare:partB];
        if (result != NSOrderedSame) {
          return result;
        }
      }
    } else {
      // Not the same type? Now we have to do some validity checking
      if ((typeA != kStringType) && (typeB == kStringType)) {
        // typeA wins
        return NSOrderedDescending;
      } else if ((typeA == kStringType) && (typeB != kStringType)) {
        // typeB wins
        return NSOrderedAscending;
      } else {
        // One is a number and the other is a period. The period is invalid
        if (typeA == kNumberType) {
          return NSOrderedDescending;
        } else {
          return NSOrderedAscending;
        }
      }
    }
  }
  // The versions are equal up to the point where they both still have parts
  // Lets check to see if one is larger than the other
  if ([partsA count] != [partsB count]) {
    // Yep. Lets get the next part of the larger
    // n holds the index of the part we want.
    NSString* missingPart;
    SUCharacterType missingType;
    NSComparisonResult shorterResult, largerResult;

    if ([partsA count] > [partsB count]) {
      missingPart = [partsA objectAtIndex:n];
      shorterResult = NSOrderedAscending;
      largerResult = NSOrderedDescending;
    } else {
      missingPart = [partsB objectAtIndex:n];
      shorterResult = NSOrderedDescending;
      largerResult = NSOrderedAscending;
    }

    missingType = [self typeOfCharacter:missingPart];
    // Check the type
    if (missingType == kStringType) {
      // It's a string. Shorter version wins
      return shorterResult;
    } else {
      // It's a number/period. Larger version wins
      return largerResult;
    }
  }

  // The 2 strings are identical
  return NSOrderedSame;
}

@end
