#!/usr/bin/env python

import logging

from collections import defaultdict
from collections import OrderedDict
from errno import ENOENT, EPERM
from stat import S_IFDIR, S_IFLNK, S_IFREG
from sys import argv, exit
from time import time, ctime

from fuse import FUSE, FuseOSError, Operations, LoggingMixIn, fuse_get_context

import re
from lxml import html
from urllib2 import urlopen
import StringIO
import gzip
import datetime

if not hasattr(__builtins__, 'bytes'):
	bytes = str

class FSElement:
	def __init__(self, name, size, timestamp):
		self.name = name
		self.size = size
		self.timestamp = timestamp
	
	def getAttrs(self):
		return {}

class FSFile(FSElement):
	def __init__(self, name, size, timestamp):
		FSElement.__init__(self, name, size, timestamp)
		self.data = ''
		self.fds = {}

	def getAttrs(self):
		uid, gid, pid = fuse_get_context()
		return dict(
			st_mode=(S_IFREG | 0444),
			st_ctime = self.timestamp,
			st_mtime = self.timestamp,
			st_atime = self.timestamp,
			st_nlink = 1,
			st_size = self.size,
			st_uid = uid,
			st_gid = gid)


class FSDirectory(FSElement):
	def __init__(self, name):
		FSElement.__init__(self, name, 1, time())
		self.children = OrderedDict()

	def getAttrs(self):
		uid, gid, pid = fuse_get_context()
		return dict(
			st_mode=(S_IFDIR | 0755),
			st_ctime = self.timestamp,
			st_mtime = self.timestamp,
			st_atime = self.timestamp,
			st_nlink = 2,
			st_uid = uid,
			st_gid = gid)
	
	def getChild(self, pathParts):
		child = self.children.get(pathParts[0])
		if len(pathParts) == 1:
			return child

		return child.getChild(pathParts[1::])
	
	def addChild(self, pathParts, child):
		if len(pathParts) == 0:
			self.children[child.name] = child
			return
		
		childDirectory = self.children.setdefault(pathParts[0], FSDirectory(pathParts[0]))
		childDirectory.addChild(pathParts[1::], child)


class ADTFS(LoggingMixIn, Operations):
	'Aminet Download Tool filesystem'

	def __init__(self):
		self.fd = 0
        
		self.rootDir = FSDirectory("")

		self.mirror = 'http://de.aminet.net/pub/aminet'
		print "before download", datetime.datetime.now().time()
		response = urlopen(self.mirror + '/info/adt/ADT_LOCAL.gz')
		compressedFile = StringIO.StringIO(response.read())
		print "after download", datetime.datetime.now().time()
		print "before parse", datetime.datetime.now().time()
		prevDirectory = ""
		for adtLine in gzip.GzipFile(fileobj=compressedFile):
			lineParts = adtLine.split("@")
			if len(lineParts) == 8:
				timestamp = int(lineParts[0])
				directory = lineParts[1]
				filename = lineParts[2]
				filesize = int(lineParts[3])
				if not prevDirectory == directory:
					pathParts = directory.split('/')
					fsDirectory = FSDirectory(pathParts[1])
					self.rootDir.addChild([pathParts[0]], fsDirectory)
				fsFile = FSFile(filename, filesize, timestamp)
				fsDirectory.children[fsFile.name] = fsFile
				fsReadmeFile = FSFile(filename.rsplit('.', 1)[0] + ".readme", 4096, timestamp) 
				fsDirectory.children[fsReadmeFile.name] = fsReadmeFile
				prevDirectory = directory
		print "after parse", datetime.datetime.now().time()

	def getFSElement(self, path):
		if path == '/':
			return self.rootDir
		else:
			pathParts = path.split('/')[1::]
			return self.rootDir.getChild(pathParts)

	def getattr(self, path, fh=None):
		element = self.getFSElement(path)
		if element:
			return element.getAttrs()
		else:
			raise FuseOSError(ENOENT)

	def getxattr(self, path, name, position=0):
		return ''

	def listxattr(srlf, path):
		print "listxattr: " + path
		return []

	def open(self, path, flags):
		self.fd += 1
		return self.fd

	def read(self, path, size, offset, fh):
		fsFile = self.getFSElement(path)
		if fsFile:
			if not fsFile.data:
				response = urlopen(self.mirror + path)
				fsFile.data = response.read()
			
			return fsFile.data[offset:offset + size]

		raise FuseOSError(ENOENT)

	def readdir(self, path, fh):
		fsDirectory = self.getFSElement(path)
		if fsDirectory:
			return ['.', '..'] + fsDirectory.children.keys()

		raise FuseOSError(ENOENT)

	def chown(self, path, uid, gid):
		raise FuseOSError(EPERM)

	def create(self, path, mode):
		raise FuseOSError(EPERM)

	def mkdir(self, path, mode):
		raise FuseOSError(EPERM)

	def readlink(self, path):
		print "readlink: " + path

	def removexattr(self, path, name):
		raise FuseOSError(EPERM)

	def rename(self, old, new):
		raise FuseOSError(EPERM)

	def rmdir(self, path):
		raise FuseOSError(EPERM)

	def setxattr(self, path, name, value, options, position=0):
		raise FuseOSError(EPERM)

	def statfs(self, path):
		return dict(f_bsize=512, f_blocks=4096, f_bavail=2048)

	def symlink(self, target, source):
		raise FuseOSError(EPERM)

	def truncate(self, path, length, fh=None):
		raise FuseOSError(EPERM)

	def unlink(self, path):
		raise FuseOSError(EPERM)

	def utimens(self, path, times=None):
		raise FuseOSError(EPERM)

	def write(self, path, data, offset, fh):
		raise FuseOSError(EPERM)

if __name__ == '__main__':
	if len(argv) != 2:
		print('usage: %s <mountpoint>' % argv[0])
		exit(1)

	logging.getLogger().setLevel(logging.DEBUG)
	fuse = FUSE(ADTFS(), argv[1], foreground=True)
