/*
Controller.m:
You may freely copy, distribute, and reuse the code in this example. 
NeXT disclaims any warranty of any kind, expressed or  implied, as to its fitness for any 
particular use.

Controller for BlockSizer 
Written by Jack Greenfield
*/

#import "Controller.h"
#import	"ScrollViewExtras.h"

#import	<appkit/appkit.h>
#import	<store/IXStoreBlock.h>
#import	<store/IXStoreFile.h>

#define	FILENAME "/tmp/BlockSizer.store"

@implementation Controller

- appDidInit:sender
{
    /* Try to open the store file */
    if (store = [[IXStoreFile alloc] initFromFile:FILENAME forWriting:YES]) {
	[store enableExceptions:NO];
	[consoleText sprintf:"opened " FILENAME "\n"];
	[self size:self];
	return self;
    }

    /* Didn't find one -- create one instead */
    [self reset:self];
    return self;
}

- ping:sender
{
    unsigned blockSize;
    unsigned blockHandle;
    unsigned commitLimit;

    if (store) {
	blockSize = [sizeField intValue];
	blockHandle = [handleField intValue];
	if (!blockSize) {
	    if (blockHandle >= [store capacity]) {
		[handleField setIntValue:[store capacity]];
		return self;
	    }

	    if ([store blockExists:blockHandle]) {
		[store freeBlock:blockHandle];
		[consoleText sprintf:"freed block %d, ", blockHandle, blockSize];
		[consoleText sprintf:"resident size = %d\n", [store residentSize]];
	    }
	} else {
	    if ([store blockExists:blockHandle]) {
		[store resizeBlock:blockHandle toSize:blockSize];
		[consoleText sprintf:"resized block %d to size %d, ", 
		    blockHandle, blockSize];
		[consoleText sprintf:"resident size = %d\n", [store residentSize]];
	    } else {
		[store createBlock:&blockHandle ofSize:blockSize];
		[consoleText sprintf:"created block %d of size %d, ", 
		    blockHandle, blockSize];
		[consoleText sprintf:"resident size = %d\n", [store residentSize]];
	    }
	}

	if (++pingCount < [recordField intValue])
	    [self perform:_cmd with:sender afterDelay:0 cancelPrevious:NO];

	[handleField setIntValue:blockHandle + 1];
	commitLimit = [commitField intValue];
	if (commitLimit && (pingCount % commitLimit) == 0)
	    [self commit:self];
    }

    return self;
}

- size:sender
{
    unsigned totalSize;
    unsigned blockCount;
    unsigned oldSize;
    unsigned newSize;
    unsigned oldBlock;
    unsigned newBlock;
    struct stat	fileStats;

    [consoleText sprintf:"\n"];
    totalSize = 0;
    blockCount = [store capacity];
    if (blockCount > 1) {
	oldSize = 0;
	[consoleText sprintf:"store capacity = %d\n", blockCount];
	for (oldBlock = 1, newBlock = 1; newBlock <= blockCount; ++newBlock) {
	    newSize = [store sizeOfBlock:newBlock];
	    totalSize += newSize;
	    if (oldSize) {
		if (!newSize || oldSize != newSize || newBlock == blockCount) {
		    if (oldBlock < newBlock - 1) {
			[consoleText sprintf:"size of blocks %d through %d = %d\n", 
			    oldBlock, newBlock - 1, oldSize];
		    } else {
			[consoleText sprintf:"size of block %d = %d\n", oldBlock, oldSize];
		    }
    
		    oldBlock = newBlock;
		    oldSize = newSize;
		}
	    } else {
		if (newSize || newBlock == blockCount) {
		    if (oldBlock < newBlock - 1) {
			[consoleText sprintf:"blocks %d through %d do not exist\n", 
			    oldBlock, newBlock - 1];
		    } else {
			if (newBlock > 1)
			    [consoleText sprintf:"block %d does not exist\n", oldBlock];
		    }
	
		    oldBlock = newBlock;
		    oldSize = newSize;
		}
	    }
	}
    }

    [consoleText sprintf:"total of block sizes = %d\n\n", totalSize];
    [consoleText sprintf:"resident size = %d, ", [store residentSize]];
    [consoleText sprintf:"apparent size = %d\n", [store apparentSize]];
    stat(FILENAME, &fileStats);
    [consoleText sprintf:"%ld disk blocks allocated, ", fileStats.st_blocks];
    [consoleText sprintf:"file size = %ld\n", fileStats.st_size];
    [consoleText sprintf:"storage efficiency = %.2f%\n", 
	(100.0 * totalSize) / fileStats.st_size];
    [countField setIntValue:[store capacity]];
    if ([handleField intValue] > [store capacity])
	[handleField setIntValue:[store capacity]];

    [consoleText sprintf:"\n"];
    return self;
}

- run:sender
{
    pingCount = 0;
    if ([handleField intValue] > [store capacity])
	[handleField setIntValue:[store capacity]];

    [self ping:sender];
    return self;
}

- stop:sender
{
    pingCount = [recordField intValue];
    return self;
}

- step:sender
{
    pingCount = 0;
    [recordField setIntValue:1];
    if ([handleField intValue] > [store capacity])
	[handleField setIntValue:[store capacity]];

    [self ping:sender];
    return self;
}

- abort:sender
{
    [consoleText sprintf:"aborting... "];
    NXPing();
    [store abortTransaction];
    [consoleText sprintf:"done.\n"];
    return self;
}

- commit:sender
{
    [consoleText sprintf:"committing... "];
    NXPing();
    [store commitTransaction];
    [consoleText sprintf:"done.\n"];
    return self;
}

- compact:sender
{
    [store commitTransaction];
    [consoleText sprintf:"compacting... "];
    NXPing();
    [store compact];
    [consoleText sprintf:"done.\n"];
    return self;
}

- empty:sender
{
    [store commitTransaction];
    [consoleText sprintf:"emptying... "];
    NXPing();
    [store empty];
    [consoleText sprintf:"done.\n"];
    return self;
}

- reset:sender
{
    [consoleText clear:self];
    unlink(FILENAME);
    store = [[IXStoreFile alloc] initWithFile:FILENAME];
    [store enableExceptions:NO];
    [store startTransaction];
    [consoleText sprintf:"created " FILENAME "\n"];
    [self commit:self];
    [self size:self];
    return self;
}

- clear:sender
{
    [consoleText clear:self];
    return self;
}

@end
