/*
 * YAFFS: Yet another FFS. A NAND-flash specific file system. 
 * yaffsdev.c
 *
 * Copyright (C) 2002 Aleph One Ltd.
 *   for Toby Churchill Ltd and Brightstar Engineering
 *
 * Created by Charles Manning <charles@aleph1.co.uk>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 */
 
#include "yaffsinterface.h"
#include "yportenv.h"

#if YAFFS_FILEEM	
#include "yaffs_fileem.h"
#else
#include "yaffs_nandemul.h" 
#endif

#include "yaffs_guts.h"
#include <stdlib.h>

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>



unsigned yaffs_traceMask = 0xFFFFFFFF;

yaffs_Device device;


char *testStr = "this is a test string";

char *testStr2 = "abcdefghijklmnopqrstuvwxyz1234567890";

void TestTimexxx(yaffs_Device *dev)
{
	yaffs_Object *f;
	int x;
	
	
	printf("Start\n");
	

	f = yaffs_FindObjectByName(yaffs_Root(dev),"Name1");
	if(f)
	{
		printf("Found\n");
	}
	else
	{
		f = yaffs_MknodFile(yaffs_Root(dev),"Name1",0,0,0);
		printf("Created\n");
	}
	
	
	x = yaffs_RenameObject(yaffs_Root(dev),"Name1",NULL,"Rename");
		
}


void TestTimeasasas(yaffs_Device *dev)
{
	yaffs_Object *f; 
	int x;
	int i;
	int b;
	char data[200];
	int written;
	
	
	printf("Start\n");
	

	f = yaffs_FindObjectByName(yaffs_Root(dev),"Name1");
	if(f)
	{
		printf("Found\n");
	}
	else
	{
		f = yaffs_MknodFile(yaffs_Root(dev),"Name1",0,0,0);
		printf("Created\n");
	}
	
	
	x = yaffs_RenameObject(yaffs_Root(dev),"Name1",NULL,"Rename");
	
	
	
	for(i = 0; i < 10000; i+=20)
	{
	
		b++;
		if(b & 1)
			written = yaffs_WriteDataToFile(f,testStr,i,strlen(testStr));
		else
			written = yaffs_WriteDataToFile(f,testStr2,i,strlen(testStr2));
	}
	
	
	yaffs_ApplyToDirectoryChildren(yaffs_Root(dev),yaffs_DumpObject);
	
	printf("Flush\n");
	
	yaffs_FlushFile(f,1);

	yaffs_ApplyToDirectoryChildren(yaffs_Root(dev),yaffs_DumpObject);
	
	yaffs_ReadDataFromFile(f,data,1000,50);
	data[50] = 0;
	
	printf("Read data is \"%s\"\n",data);
	
	yaffs_DumpObject(f);

	printf("Resize to 3000\n");	
	yaffs_ResizeFile(f,3000);
	printf("Resize to 2048\n");	
	yaffs_ResizeFile(f,2048);
	
	yaffs_DumpObject(f);

	yaffs_FlushFile(f,1);
	
	
		

}

void TestTimeBigDeletes(yaffs_Device *dev)
{
	yaffs_Object *f;
	yaffs_Object *sl;
	yaffs_Object *lnf;
	
	yaffs_Object *hl1;
	yaffs_Object *hl2;
	yaffs_Object *hl3;
	yaffs_Object *d, *df;
	
	int x;
	int i;
	int b;
	char data[200];
	
	char * alias;
	int written;
	
	
	printf("Exisiting objects\n");
	yaffs_ApplyToDirectoryChildren(yaffs_Root(dev),yaffs_DumpObject);
	printf("Exisiting objects in lost+found\n");
	lnf = yaffs_FindObjectByName(yaffs_Root(dev),YAFFS_LOSTNFOUND_NAME);
	yaffs_ApplyToDirectoryChildren(lnf,yaffs_DumpObject);

	printf("Start\n");
	
	

	f = yaffs_FindObjectByName(yaffs_Root(dev),"Name1");
	if(f)
	{
		printf("Found\n");
	}
	else
	{
		f = yaffs_MknodFile(yaffs_Root(dev),"Name1",0,0,0);
		printf("Created\n");
	}
	
	for(i = 0; i < 100000; i+=20)
	{ 
	
		b++;
		if(b & 1)
			written = yaffs_WriteDataToFile(f,testStr,i,strlen(testStr));
		else
			written = yaffs_WriteDataToFile(f,testStr2,i,strlen(testStr2));
	}
	
	yaffs_FlushFile(f,1);
	yaffs_DeleteFile(f);

	f = yaffs_FindObjectByName(yaffs_Root(dev),"Name1");
	if(f)
	{
		printf("Found\n");
	}
	else
	{
		f = yaffs_MknodFile(yaffs_Root(dev),"Name1",0,0,0);
		printf("Created\n");
	}
	
	for(i = 0; i < 100000; i+=20)
	{ 
	
		b++;
		if(b & 1)
			written = yaffs_WriteDataToFile(f,testStr,i,strlen(testStr));
		else
			written = yaffs_WriteDataToFile(f,testStr2,i,strlen(testStr2));
	}
	
	yaffs_FlushFile(f,1);
	yaffs_DeleteFile(f);

	f = yaffs_FindObjectByName(yaffs_Root(dev),"Name1");
	if(f)
	{
		printf("Found\n");
	}
	else
	{
		f = yaffs_MknodFile(yaffs_Root(dev),"Name1",0,0,0);
		printf("Created\n");
	}
	
	for(i = 0; i < 100000; i+=20)
	{ 
	
		b++;
		if(b & 1)
			written = yaffs_WriteDataToFile(f,testStr,i,strlen(testStr));
		else
			written = yaffs_WriteDataToFile(f,testStr2,i,strlen(testStr2));
	}
	
	yaffs_FlushFile(f,1);
	yaffs_DeleteFile(f);
	
}

void TestTime(yaffs_Device *dev)
{
	yaffs_Object *f;
	yaffs_Object *sl;
	yaffs_Object *lnf;
	
	yaffs_Object *hl1;
	yaffs_Object *hl2;
	yaffs_Object *hl3;
	yaffs_Object *d, *df;
	
	int x;
	int i;
	int b;
	char data[200];
	
	char * alias;
	int written;
	
	
	printf("Exisiting objects\n");
	yaffs_ApplyToDirectoryChildren(yaffs_Root(dev),yaffs_DumpObject);
	printf("Exisiting objects in lost+found\n");
	lnf = yaffs_FindObjectByName(yaffs_Root(dev),YAFFS_LOSTNFOUND_NAME);
	yaffs_ApplyToDirectoryChildren(lnf,yaffs_DumpObject);

	printf("Start\n");
	
	
	// Test the problem of:
	// Create file
	// Delete file
	// Create file with same name
	// Delete file <== crash

	f = yaffs_FindObjectByName(yaffs_Root(dev),"Name1");
	if(f)
	{
		printf("Found\n");
	}
	else
	{
		f = yaffs_MknodFile(yaffs_Root(dev),"Name1",0,0,0);
		printf("Created\n");
	}
	yaffs_Unlink(yaffs_Root(dev),"Name1");


	f = yaffs_FindObjectByName(yaffs_Root(dev),"Name1");
	if(f)
	{
		printf("Found\n");
	}
	else
	{
		f = yaffs_MknodFile(yaffs_Root(dev),"Name1",0,0,0);
		printf("Created\n");
	}
	yaffs_Unlink(yaffs_Root(dev),"Name1");
	
	
	
	// Other tests
	
	f = yaffs_FindObjectByName(yaffs_Root(dev),"Name1");
	if(f)
	{
		printf("Found\n");
	}
	else
	{
		f = yaffs_MknodFile(yaffs_Root(dev),"Name1",0,0,0);
		printf("Created\n");
	}
	
	
	x = yaffs_RenameObject(yaffs_Root(dev),"Name1",NULL,"Rename");
	
// Write a file with overwrite, then read it back and save outside yaffs
// This is to test that write caching works.

	for(i = 0; i < 100; i++)
	{ 
		unsigned char x[500];
	
		memset(x,i,500);
		
		written = yaffs_WriteDataToFile(f,x,i*500,500);		
	}

	for(i = 0; i < 100; i++)
	{ 
		unsigned char x;
	
		x = i+5;
		
		written = yaffs_WriteDataToFile(f,&x,i*500 + 2,1);		
	}
	
	
	{
		int h;
		
		h = open("bork",O_RDWR | O_CREAT | O_TRUNC,0666);
		for(i = 0; i < 100; i++)
		{ 
			unsigned char x[500];
	
			yaffs_ReadDataFromFile(f,x,i*500,500);
			write(h,x,500);
		}
		close(h);
		
		
	}
	
// Big write to fill disk

	written = 1;
	for(i = 0; i < 1000000 && written > 0; i++)
	{ 
		unsigned char x[500];
	
		memset(x,i,500);
		
		written = yaffs_WriteDataToFile(f,x,i*500,500);		
	}
	if(written <= 0)
	{
		printf("YAFFS full\n");
	}
	
	// some short reads
	for(i = 1000; i < 50000; i+=2)
	{ 
		yaffs_ReadDataFromFile(f,data,i,20);
	}
	
		
	yaffs_ReadDataFromFile(f,data,1000,50); 
	data[50] = 0;
	
	printf("Read data is \"%s\"\n",data);

	yaffs_ApplyToDirectoryChildren(yaffs_Root(dev),yaffs_DumpObject);
	yaffs_ReadDataFromFile(f,data,1000,50);
	data[50] = 0;
	
	printf("Read data is \"%s\"\n",data);
	
	printf("Flush\n");
	
	yaffs_FlushFile(f,1);
	yaffs_ReadDataFromFile(f,data,1000,50);
	data[50] = 0;
	
	printf("Read data is \"%s\"\n",data);
	
	printf("File length is %d\n",yaffs_GetObjectFileLength(f));
	
	sl = yaffs_MknodSymLink(yaffs_Root(dev),"sym-link",0,0,0,"/tmp/alias");
	yaffs_ReadDataFromFile(f,data,1000,50);
	data[50] = 0;
	
	printf("Read data is \"%s\"\n",data);


	yaffs_ApplyToDirectoryChildren(yaffs_Root(dev),yaffs_DumpObject);
	
	printf("\n\nsymlink alias is \"%s\"\n",alias = yaffs_GetSymlinkAlias(sl));
	
	free(alias);
	
	printf("Unlink symlink %d\n",yaffs_Unlink(yaffs_Root(dev),"sym-link"));
	
	
	yaffs_ReadDataFromFile(f,data,1000,50);
	data[50] = 0;
	
	printf("Read data is \"%s\"\n",data);
	
	yaffs_DumpObject(f);

	printf("Resize 3000\n");	
	yaffs_ResizeFile(f,3000);

	printf("Resize 2050\n");	
	yaffs_ResizeFile(f,2050);
	printf("Resize 2049\n");	
	yaffs_ResizeFile(f,2049);
	printf("Resize 2048\n");	
	yaffs_ResizeFile(f,2048);


	printf("Resize 2000\n");	
	yaffs_ResizeFile(f,2000);
	
	yaffs_DumpObject(f);
	
	lnf = yaffs_FindObjectByName(yaffs_Root(dev),YAFFS_LOSTNFOUND_NAME);
	
	

	yaffs_FlushFile(f,1);
		

	printf("Unlink file: %d\n",yaffs_Unlink(yaffs_Root(dev),"Rename"));
	
	yaffs_DeleteFile(f);
	
	yaffs_ApplyToDirectoryChildren(yaffs_Root(dev),yaffs_DumpObject);
	
	// Create a directory and play with it
	

	printf("Find or Create directory and play with it\n");
	d =  yaffs_FindObjectByName(yaffs_Root(dev),"direct");
	if(!d)
	{
		d = yaffs_MknodDirectory(yaffs_Root(dev),"direct",0,0,0);
	}
	yaffs_ApplyToDirectoryChildren(yaffs_Root(dev),yaffs_DumpObject);
	yaffs_ApplyToDirectoryChildren(d,yaffs_DumpObject);
	
	printf("Make file in directory\n");
	
	df = yaffs_MknodFile(d,"file-in-directory",0,0,0);
	yaffs_ApplyToDirectoryChildren(d,yaffs_DumpObject);
	
	
	// Do some stuff with hardlinks
	//
	// NB Deleting hardlinked objects can mess up pointers to hardlinks.
	// The mechanism is as follows:
	// * If you unlink a file,softlink or directory that has one or more hardlinks,
	// then the object is renamed to one of the hardlinks and that hardlink is unlinked.
	// This means that a pointer to a hardlink so deleted will point to an invalid address.
	// Thus, make sure that pointers to hardlinks are immediately dereferenced.


	printf("Hard link tests\n");
		
	f = yaffs_MknodFile(yaffs_Root(dev),"Name1",0,0,0);
	hl1 = yaffs_Link(yaffs_Root(dev),"HardLink 1",f);
	hl2 = yaffs_Link(yaffs_Root(dev),"HardLink 2",f);
	hl3 = yaffs_Link(yaffs_Root(dev),"HardLink 3",hl2);

	printf("\n\nHard links created\n");
	yaffs_ApplyToDirectoryChildren(yaffs_Root(dev),yaffs_DumpObject);
	
	yaffs_Unlink(yaffs_Root(dev),"HardLink 1");
	printf("\n\nHard link deleted\n");
	yaffs_ApplyToDirectoryChildren(yaffs_Root(dev),yaffs_DumpObject);
	
	yaffs_Unlink(yaffs_Root(dev),"Name1");
	printf("\n\nHard linked file deleted\n");
	yaffs_ApplyToDirectoryChildren(yaffs_Root(dev),yaffs_DumpObject);
	
	yaffs_Unlink(yaffs_Root(dev),"HardLink 2");
	printf("\n\nHard link 2 deleted\n");
	yaffs_ApplyToDirectoryChildren(yaffs_Root(dev),yaffs_DumpObject);
	
	yaffs_Unlink(yaffs_Root(dev),"HardLink 3");

	printf("\n\nHard link 3 deleted\n");
	yaffs_ApplyToDirectoryChildren(yaffs_Root(dev),yaffs_DumpObject);
	
	// NB We don't allow unlinking or rename of the root or lost+found
	// We allow setting attributes, but these must not be written to
	// NAND since they are not real objects.
	
	printf("Attempt to rename lost+found - should have failed\n");
	x = yaffs_RenameObject(yaffs_Root(dev),YAFFS_LOSTNFOUND_NAME,NULL,"Renamed");
	yaffs_ApplyToDirectoryChildren(yaffs_Root(dev),yaffs_DumpObject);

	f = yaffs_MknodFile(yaffs_Root(dev),"pfile",0,0,0);
	if(f)
	{
		yaffs_WriteDataToFile(f,testStr,0,strlen(testStr));
	}

	yaffs_Link(yaffs_Root(dev),"phl4",f);
}

void TestTimeDeleteFocussed(yaffs_Device *dev)
{
	yaffs_Object *f;
	yaffs_Object *lnf;
	
	
	int x;
	int i;
	int b;
	int written;
	
	
	printf("Exisiting objects\n");
	yaffs_ApplyToDirectoryChildren(yaffs_Root(dev),yaffs_DumpObject);
	printf("Exisiting objects in lost+found\n");
	lnf = yaffs_FindObjectByName(yaffs_Root(dev),YAFFS_LOSTNFOUND_NAME);
	yaffs_ApplyToDirectoryChildren(lnf,yaffs_DumpObject);

	printf("Start\n");
	
	
	

	f = yaffs_FindObjectByName(yaffs_Root(dev),"Name1");
	if(f)
	{
		printf("Found\n");
	}
	else
	{
		f = yaffs_MknodFile(yaffs_Root(dev),"Name1",0,0,0);
		printf("Created\n");
	}
	
	
	x = yaffs_RenameObject(yaffs_Root(dev),"Name1",NULL,"Rename");
	
	for(i = 0; i < 100000; i+=20)
	{ 
	
		b++;
		if(b & 1)
			written = yaffs_WriteDataToFile(f,testStr,i,strlen(testStr));
		else
			written = yaffs_WriteDataToFile(f,testStr2,i,strlen(testStr2));
	}
	
	

	yaffs_FlushFile(f,1);
		

	printf("Unlink file: %d\n",yaffs_Unlink(yaffs_Root(dev),"Rename"));
	
	yaffs_ApplyToDirectoryChildren(yaffs_Root(dev),yaffs_DumpObject);
	
}

void TestTimeTnodeFocussed(yaffs_Device *dev)
{
	yaffs_Object *f;
	yaffs_Object *lnf;
	
	
	int x;
	int i;
	int b;
	int written;
	
	
	printf("Exisiting objects\n");
	yaffs_ApplyToDirectoryChildren(yaffs_Root(dev),yaffs_DumpObject);
	printf("Exisiting objects in lost+found\n");
	lnf = yaffs_FindObjectByName(yaffs_Root(dev),YAFFS_LOSTNFOUND_NAME);
	yaffs_ApplyToDirectoryChildren(lnf,yaffs_DumpObject);

	printf("Start\n");
	
	
	

	f = yaffs_FindObjectByName(yaffs_Root(dev),"Name1");
	if(f)
	{
		printf("Found\n");
	}
	else
	{
		f = yaffs_MknodFile(yaffs_Root(dev),"Name1",0,0,0);
		printf("Created\n");
	}
	
	
	x = yaffs_RenameObject(yaffs_Root(dev),"Name1",NULL,"Rename");
	
	for(i = 0; i < 10000; i+=20)
	{
	
		b++;
		if(b & 1)
			written = yaffs_WriteDataToFile(f,testStr,0,strlen(testStr));
		else
			written = yaffs_WriteDataToFile(f,testStr2,0,strlen(testStr2));
	}
	
}
void TestTimeBackgroundDeleteFocussed(yaffs_Device *dev)
{
	yaffs_Object *f;
	yaffs_Object *lnf;
	
	
	int x;
	int i,j;
	int b;
	int written;
	
	
	printf("Exisiting objects\n");
	yaffs_ApplyToDirectoryChildren(yaffs_Root(dev),yaffs_DumpObject);
	printf("Exisiting objects in lost+found\n");
	lnf = yaffs_FindObjectByName(yaffs_Root(dev),YAFFS_LOSTNFOUND_NAME);
	yaffs_ApplyToDirectoryChildren(lnf,yaffs_DumpObject);

	printf("Start\n");
	
	
	for(j = 0; j < 20; j++)
	{
		printf("Run %d\n",j);

		f = yaffs_FindObjectByName(yaffs_Root(dev),"Name1");
		if(f)
		{
			printf("Found\n");
		}
		else
		{
			f = yaffs_MknodFile(yaffs_Root(dev),"Name1",0,0,0);
			printf("Created\n");
		}
	
		printf("@@@@@@@ Run %d, object %d\n",j,f->objectId);
	
		for(i = 0; i < 1000000; i+=20)
		{
	
			written = yaffs_WriteDataToFile(f,testStr,i,100);
		}
		
		yaffs_FlushFile(f,1);
		
		yaffs_DeleteFile(f);
	}
	
}

int main(int argc,char *argv[])
{

	int nBlocks;
	
#if YAFFS_FILEEM	
	nBlocks =(2 * 1024 * 1024) / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK) ;
	device.writeChunkToNAND = yaffs_FEWriteChunkToNAND;
	device.readChunkFromNAND = yaffs_FEReadChunkFromNAND;
	device.eraseBlockInNAND = yaffs_FEEraseBlockInNAND;
	device.initialiseNAND = yaffs_FEInitialiseNAND;

	printf("Testing on file emulation\n");
#else
	nBlocks = (2 * 1024 * 1024) / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK);
	device.writeChunkToNAND = nandemul_WriteChunkToNAND;
	device.readChunkFromNAND = nandemul_ReadChunkFromNAND;
	device.eraseBlockInNAND = nandemul_EraseBlockInNAND;
	device.initialiseNAND = nandemul_InitialiseNAND;
	
	printf("Testing on RAM emulation\n");
#endif

#ifdef YAFFS_START
	device.startBlock = YAFFS_START;  // Don't use block 0
	device.endBlock = YAFFS_END;
#else
	device.startBlock = 1;  // Don't use block 0
	device.endBlock = nBlocks;
#endif

	device.nShortOpCaches = 10;


	yaffs_GutsInitialise(&device);
	
	// yaffs_GutsTest();
	
	TestTimeBackgroundDeleteFocussed(&device);
	
	printf("Cache hits %d\n",device.cacheHits);
	printf("Retired blocks %d\n",device.nRetiredBlocks);
	
	printf("Deletions %d\n",device.nDeletions);
	printf("Unmarked deletions %d\n",device.nUnmarkedDeletions);
	
	exit(0);
}
