import copy
#import proc, bin

#the first thing that i have to do if i want to imitate unix , its the filesystem.
#since in unix , everything is a file , those class are the most important part

#for now , mounting is not supported , it's just a snapshot of a unix virtual filesystem
#that work entirely from memory !!! i brainstorm about how i can imitate a drive mounting
#but for now it will work without

class nod(object):
	def __init__(self,fs,mod,user,grp,name,parent,fil):
		self.fs = fs		#this is the main inode object
		self.mod = mod		#of course it's not exactly the same
		self.user = user	#as UFS or EXT fs , but it will do the job
		self.grp = grp		#this fs is maiden for simplicity not security
		self.name = name
		self.parent = parent
		self.fil = fil

	def filetype(self):
		return self.mod[0]	#will return filetype

	def getright(self,user,grp,mode):
		#while every file have a symbolic mode that rule who can do what in the
		#unix filesystem , this part of the code become reducdant , that's why
		#maiden a function
		if (user == self.user or user == 'root') and mode in self.mod[1:4]:
			return True
		elif (grp == self.grp) and mode in self.mod[4:7]:
			return True
		elif mode in self.mod[7:10]:
			return True
		else:
			return False

	def chmod(self,user,grp,numod):
		#changing the symbole apply only on the inode
		if self.getright(user,grp,'w'):
			self.mod = numod

	def chown(self,user,grp,nuown):
		#changing user apply only on the inode
		if self.getright(user,grp,'w'):
			self.user = nuown

	def chgrp(self,user,grp,nugrp):
		#changing group apply only on the inode
		if self.getright(user,grp,'w'):
			self.user = nugrp

	def read(self,user,grp):
		#reading file is done differently here,
		#instead reading block or given length , this
		#function return entire copy of the file in an
		#object , thuse modifying the given object wont
		#happen in the real file.
		filetype = self.filetype()
		if filetype == 'l':
			#in tha case its a symbolic link
			#need to redirect the command to the targeted file
			return self.fs.files[self.fil].read(user,grp)
		elif filetype == 'b' or filetype == 'c':
			#a block device or special caracter device
			#will execute a given function on read
			return self.fil.read()
		elif filetype == 'p':
			#a pipe will receive write instruction then
			#store them to a fifo , read function will return
			#first write and remove it
			if type(self.data) != list:
				self.data = []
			elif self.getright(user,grp,'r'):
				if len(self.data) != 0:
					return self.data.pop(0)
				else:
					return None
			else:
				return False
		else:
			#the other cas , a regular file and directory
			#juste copy the data
			if self.getright(user,grp,'r'):
				return copy.deepcopy(self.fil)
			else:
				return False

	def write(self,user,grp,data):
		#same as above , this function write a copy of the given
		#object in the real file
		filetype = self.filetype()
		if filetype == 'l':
			return self.fs.files[self.fil].write(user,grp,data)
		elif filetype == 'b' or filetype == 'c':
			self.fil.write(data)
		elif filetype == 'p':
			if type(self.data) != list:
				self.data = []
			elif self.getright(user,grp,'r'):
				self.data.append(data)
				return True
			else:
				return False
		else:
			if self.getright(user,grp,'w'):
				self.fil = copy.deepcopy(data)
				return True
			else:
				return False

class block_device(object):
	def __init__(self):
		pass

	def write(self,data):
		pass

	def read(self):
		pass

class character_device(object):
	def __init__(self):
		pass

	def write(self,data):
		pass

	def read(self):
		pass

class fs(object):
	def __init__(self,root=None):
		if root:
			self.files = root
		else:
			self.files = {'':nod(self,'drwxr-xr-x','root','root','/','/',{})}

	def get_nod(self,parent):
		if self.files.has_key(parent):			#get the nod in the file list
			return self.files[parent]		#using access name , then return
		else:						#nod object or False if not exist
			return False

	def validate_parent(self,parent):
		tmp = parent.split('/')			#in the case that parent dir
		tmp.pop(0)					#contain a symbolic linl
		n = ''						#exemple , if /usr/local/share is
		for d in tmp:					#a link of of /usr/share , and you
			n = n + '/' + d				#you enter /usr/local/share/man , then
			nod = self.get_nod(n)			#the real parent dir is /usr/share/man
			if nod:					#entering /usr/local/share/man will result
				if nod.filetype() == 'l':	#an error , then this function will separate
					n = nod.fil		#individual dir and replace then with linked
			else:					#destination if needed , will return REAL
				print n				#access name from virtual one
				return False
		return n

	def get_access_name(self,parent,name):
		parent = self.validate_parent(parent)		#just create access name from parent
		return parent + '/' + name			#and name



	def chg_type_in_mode_str(self,mode,nutype):
		return nutype + mode[1:10]			#change file type in the sym while keeping mod

	def mknod(self,user,grp,parent,name,Type='-',mode='-rw-r--r--',data=None):
		parent = self.validate_parent(parent)		#build nod if self.files , then add it's access
		nuname = self.get_access_name(parent,name)	#name in the required dir
		numode = self.chg_type_in_mode_str(mode,Type)
		parent_dir = self.get_nod(parent)
		if parent_dir and parent_dir.filetype() == 'd' and not self.files.has_key(nuname):
			if parent_dir.getright(user,grp,'rw'):
				tmp = parent_dir.read(user,grp)
				tmp[name] = nuname
				parent_dir.write(user,grp,tmp)
				if Type == 'p' and data == None:
					data = []
				self.files[nuname] = nod(self,numode,user,grp,name,parent,data)
				return True
			else:
				return False
		else:
			return False

	def rmnod(self,user,grp,parent,name):
		parent = self.validate_parent(parent)
		nuname = self.get_access_name(parent,name)
		parent_dir = self.get_nod(parent)
		name_nod = self.get_nod(nuname)
		if parent_dir and parent_dir.getright(user,grp,'rw') and name_nod and name_nod.getright(user,grp,'w'):
			if name_nod.filetype() != 'd':
				tmp = parent_dir.read(user,grp)
				del tmp[name]
				parent_dir.write(user,grp,tmp)
				del self.files[nuname]
			else:
				return False
		else:
			return False

	def mkdir(self,user,grp,parent,name,mode='drwxr-xr-x'):
		return self.mknod(user,grp,parent,name,'d',mode,{})	#create directory

	def rmdir(self,user,grp,parent,name):
		parent = self.validate_parent(parent)
		nuname = self.get_access_name(parent,name)
		parent_dir = self.get_nod(parent)
		name_dir = self.get_nod(nuname)
		if parent_dir and name_dir and parent_dir.getright(user,grp,'rw') and name_dir.getright(user,grp,'w'):
			tmp = name_dir.read(user,grp)
			if len(tmp) == 0:
				tmp = parent_dir.read(user,grp)
				del tmp[name]
				parent_dir.write(user,grp,tmp)
				del self.files[nuname]
			else:
				return False
		else:
			return False


	def sln(self,user,grp,parent,name,data,mode='lrwxr-xr-x'):
		data = self.validate_parent(data)			#make symbolic link from
		return self.mknod(user,grp,parent,name,'l',mode,data)	#a given file

	def ls(self,user,grp,parent):
		parent = self.validate_parent(parent)			#give all file in a list from
		nod = self.get_nod(parent)				#given directory
		if nod and nod.filetype() == 'd':
			tmp = nod.read(user,grp).keys()
			tmp.sort()
			return tmp
		return False

	def cp(self,user,grp,srcparent,srcname,destparent,destname):
		src = self.get_nod(self.get_access_name(srcparent,srcname))
		if src and src.filetype() == 'd':		#i should not allow to cpy directory using cp()
			return False
		if src and src.getright(user,grp,'r'):		#you must have read access to the file first
			self.mknod(user,grp,destparent,destname,src.filetype(),src.mod,src.read(user,grp))
			return True
		else:
			return False

#############################################################################################################
#############################################################################################################
#############################################################################################################
#############################################################################################################
#############################################################################################################
#############################################################################################################
#############################################################################################################
class dev_null(character_device):
	def __init__(self):
		pass

	def read(self):
		return ''

	def write(self,data):
		return True

class dev_zero(character_device):
	def __init__(self):
		pass

	def read(self):
		return str(0)

	def write(self,data):
		return True

class dev_random(character_device):
	def __init__(self):
		import random
		self.random = random

	def read(self):
		return str(self.random.randint(0,0xffffffff))

	def write(self,data):
		self.random.seed(int(data))
		return True

def build_root():
	euh = fs()
	euh.mkdir('root','root','','bin')
	euh.mkdir('root','root','','dev')
	euh.mkdir('root','root','','etc')	
	euh.mkdir('root','root','','root')
	euh.mkdir('root','root','','sbin')
	euh.mkdir('root','root','','tmp')
	euh.mkdir('root','root','','usr')
	euh.mkdir('root','root','','var')
	euh.mknod('root','root','/dev','null','c','crw-rw-rw-',dev_null())
	euh.mknod('root','root','/dev','zero','c','crw-rw-rw-',dev_zero())
	euh.mknod('root','root','/dev','random','c','crw-rw-rw-',dev_random())
	euh.mknod('root','root','/dev','tty','c','crw-rw-rw-',dev_random())
	euh.sln('root','root','/dev','pty','/dev/tty')
	euh.mkdir('root','root','/usr','bin')
	euh.mkdir('root','root','/usr','local')
	euh.mkdir('root','root','/usr','share')
	euh.sln('root','root','/usr/local','share','/usr/share')
	euh.mkdir('root','root','/usr/share','man')
	euh.mkdir('root','root','/usr','home','drwxrwxrwx')

	euh.mknod('root','root','/usr/home','in','-','-rw-rw-rw-')
	euh.mknod('root','root','/usr/home','out','-','-rw-rw-rw-')
	euh.mknod('root','root','/usr/home','err','-','-rw-rw-rw-')

	import bin
	euh.mknod('root','root','/bin','ls','-','-rwxrwxrwx',bin.ls)

	return euh

#############################################################################################################
#############################################################################################################
#############################################################################################################
#############################################################################################################
#############################################################################################################
#############################################################################################################
#############################################################################################################

if __name__=="__main__":
	euh = build_root()
	all = euh.files.keys()
	all.sort()
	print all

#the filesystem can be pickled in a file
#>>> import vfs
#>>> import pickle
#>>> tmp = vfs.build_root()
#>>> fil = open('test.pkl','wb')
#>>> pickle.dump(tmp.files,fil)
#>>> fil.close()

#then unpickled for other use
#>>> import vfs
#>>> import pickle
#>>> fil = open('test.pkl','rb')
#>>> tmp = fs.fs(pickle.load(fil))#############################################################################################################
#############################################################################################################
#############################################################################################################
#############################################################################################################
#############################################################################################################
#############################################################################################################
#############################################################################################################
#>>> tmp.ls('root','root','')
#['bin', 'dev', 'etc', 'root', 'sbin', 'tmp', 'usr', 'var']
#>>> fil.close()





