//
//  MPT_Demo.m
//  AUCoderDemo
//
//  Created by Andrew Salamon on 10/5/07.
//  Copyright 2007 Machine Perception Laboratory, University of California San Diego.. All rights reserved.
//

#import "MPT_Demo.h"

@interface MPT_Demo(PrivateMethods)
- (NSDate *)readDemoStartDate;
- (BOOL)writeDemoStartDate;
- (int)obfuscate:(int)num;
- (int)deobfuscate:(int)num;
@end

static MPT_Demo *sharedDemo = nil;

@implementation MPT_Demo
+ (MPT_Demo *)sharedDemo
{
	if( !sharedDemo )
	{
		sharedDemo = [[MPT_Demo alloc] init];
	}
	return sharedDemo;
}

- (id)init
{
	if( self = [super init] )
	{
		[self setDemoLength:30];
		[self setFilename:@".mplab"];
		[self setAllowedPaths:[NSArray arrayWithObjects:@"/Library", @"/Users/Shared", @"~/Library", nil]];
	}
	return self;
}

- (void)dealloc
{
	[filename release];
	[allowedPaths release];
	[startDate release];
	[super dealloc];
}

- (BOOL)startDemo
{
	return [self writeDemoStartDate];
}

- (BOOL)isDemo
{
	NSDate *start = [self readDemoStartDate];
	return (start != nil);
}

- (BOOL)hasDemoEnded
{
	NSDate *start = [self readDemoStartDate];
	if( !start )
	{
		// This should mean that we haven't yet written the start date to a file
		// If we fail to write the file, then return YES, meaning we can't run as a demo
		return ![self writeDemoStartDate];
	}
	
	NSTimeInterval duration = [[NSDate date] timeIntervalSinceDate:start];
	if( duration < 0.0 ) return YES; // If the current date is before the start something is wrong
	
	if( duration > demoLength ) return YES;
	
	return NO;
}

- (NSDate *)readDemoStartDate
{
	if( startDate ) return startDate;
	
	NSEnumerator *pathEnum = [allowedPaths objectEnumerator];
	NSString *path;
	while( path = [pathEnum nextObject] )
	{
		NSString *file = [path stringByAppendingPathComponent:[self filename]];
		NSString *intString = [NSString stringWithContentsOfFile:file];
		if( intString )
		{
			int dateNum = [intString intValue];
			dateNum = [self deobfuscate:dateNum];
			NSDate *start = [NSDate dateWithTimeIntervalSinceReferenceDate:dateNum];
			if( start )
			{
				startDate = [start copy];
				return start;
			}
		}
	}
	return nil;
}

- (BOOL)writeDemoStartDate
{
	int dateInt = (int)[NSDate timeIntervalSinceReferenceDate];
	dateInt = [self obfuscate:dateInt];
	NSString *intString = [NSString stringWithFormat:@"%d", dateInt];
	NSEnumerator *pathEnum = [allowedPaths objectEnumerator];
	NSString *path;

	while( path = [pathEnum nextObject] )
	{
		NSString *file = [path stringByAppendingPathComponent:[self filename]];
		if( [intString writeToFile:file atomically:YES] )
			return YES;
	}
	return NO;
}


- (NSString *)filename
{
    return filename;
}

- (void)setFilename:(NSString *)value
{
    if( filename != value )
    {
        [filename release];
        filename = [value copy];
    }
}

- (NSArray *)allowedPaths
{
    return allowedPaths;
}

- (NSArray *)normalizePaths:(NSArray *)paths
{
	NSMutableArray *newPaths = [NSMutableArray array];
	NSEnumerator *pathEnum = [paths objectEnumerator];
	NSString *path;
	while( path = [pathEnum nextObject] )
	{
		path = [[[path stringByExpandingTildeInPath] stringByResolvingSymlinksInPath] stringByStandardizingPath];
		[newPaths addObject:path];
	}
	return [[newPaths copy] autorelease];
}

- (void)setAllowedPaths:(NSArray *)value
{
    if( allowedPaths != value )
    {
        [allowedPaths release];
        allowedPaths = [[self normalizePaths:value] retain];
    }
}

- (unsigned int)demoLength
{
	return demoLength / (60 * 60 *24);
}
- (void)setDemoLength:(unsigned int)days
{
	demoLength = days * (60 * 60 *24);
}

- (void)notifyUserDemoEnded
{
	NSAlert *alert = [NSAlert alertWithMessageText:@"The CERT Demo period has ended." defaultButton:@"Quit" alternateButton:nil otherButton:nil informativeTextWithFormat:@"Please contact MPLab if you have any questions:\nhttp://mplab.ucsd.edu"];
	[alert runModal];
}

- (void)notifyUserDemoNotAllowed
{
	NSAlert *alert = [NSAlert alertWithMessageText:@"Unable to start the CERT Demo." defaultButton:@"Quit" alternateButton:nil otherButton:nil informativeTextWithFormat:@"Please contact MPLab if you have any questions:\nhttp://mplab.ucsd.edu"];
	[alert runModal];
}

- (void)notifyUserDemoLeft
{
	NSDate *demoDate = [self readDemoStartDate];
	demoDate = [demoDate addTimeInterval:demoLength];
	NSCalendarDate *calDate = [NSCalendarDate dateWithTimeIntervalSince1970:[demoDate timeIntervalSince1970]];
	NSAlert *alert = [NSAlert alertWithMessageText:@"This is a demo verision of CERT." defaultButton:@"Continue" alternateButton:nil otherButton:nil informativeTextWithFormat:@"The demo period will end %@.", [calDate descriptionWithCalendarFormat:@"%A %B %e, %Y at %I:%M %p"]];
	[alert runModal];
}

// These obfuscation methods are an attempt to write a rotating (or circular) bitwise shift.
static int mask7 = 1 + 2 + 4 + 8 + 16 + 32 + 64;
static int rt = 7;
static int bitCnt = sizeof(int) * 8;

- (int)obfuscate:(int)num
{
    int mask = mask7;
    int hold = num & mask; // save the 7 right most bits
    int clearmask = mask7 << (bitCnt - rt);
    clearmask = ~clearmask; // bitwise complement
    int tmp1 = num >> rt;
	tmp1 = tmp1 & clearmask; // Clear the leftmost 7 bits which might have been set to 1 by the shift
    int hold2 = hold << (bitCnt - rt); // move the saved bits to the other end
    int tmp2 = hold2 | tmp1; // paste them together
	
    return tmp2;
}

- (int)deobfuscate:(int)num
{
    int clearmask = ~mask7;
    int mask = mask7 << (bitCnt - rt);
    int hold = num & mask;
    hold = hold >> (bitCnt - rt);
	hold = hold & mask7;
    num = num << rt;
    num = num & clearmask;
    num = num | hold;
	
    return num;
}
@end
