# Written by John Hoffman and Uoti Urpala
# see LICENSE.txt for license information
from bencode import bencode, bdecode
from BT1.btformats import check_info
from os.path import exists, isfile
from hashlib import sha1
import sys, os
 
try:
    True
except:
    True = 1
    False = 0
 
NOISY = False
 
def _errfunc(x):
    print ":: "+x
 
def parsedir(directory, parsed, files, blocked,
             exts = ['.torrent'], return_metainfo = False, errfunc = _errfunc):
    if NOISY:
        errfunc('checking dir')
    dirs_to_check = [directory]
    new_files = {}
    new_blocked = {}
    torrent_type = {}
    while dirs_to_check:    # first, recurse directories and gather torrents
        directory = dirs_to_check.pop()
        newtorrents = False
        for f in os.listdir(directory):
            newtorrent = None
            for ext in exts:
                if f.endswith(ext):
                    newtorrent = ext[1:]
                    break
            if newtorrent:
                newtorrents = True
                p = os.path.join(directory, f)
                new_files[p] = [(os.path.getmtime(p), os.path.getsize(p)), 0]
                torrent_type[p] = newtorrent
        if not newtorrents:
            for f in os.listdir(directory):
                p = os.path.join(directory, f)
                if os.path.isdir(p):
                    dirs_to_check.append(p)
 
    new_parsed = {}
    to_add = []
    added = {}
    removed = {}
    # files[path] = [(modification_time, size), hash], hash is 0 if the file
    # has not been successfully parsed
    for p,v in new_files.items():   # re-add old items and check for changes
        oldval = files.get(p)
        if not oldval:          # new file
            to_add.append(p)
            continue
        h = oldval[1]
        if oldval[0] == v[0]:   # file is unchanged from last parse
            if h:
                if blocked.has_key(p):  # parseable + blocked means duplicate
                    to_add.append(p)    # other duplicate may have gone away
                else:
                    new_parsed[h] = parsed[h]
                new_files[p] = oldval
            else:
                new_blocked[p] = 1  # same broken unparseable file
            continue
        if parsed.has_key(h) and not blocked.has_key(p):
            if NOISY:
                errfunc('removing '+p+' (will re-add)')
            removed[h] = parsed[h]
        to_add.append(p)
 
    to_add.sort()
    for p in to_add:                # then, parse new and changed torrents
        new_file = new_files[p]
        v,h = new_file
        if new_parsed.has_key(h): # duplicate
            if not blocked.has_key(p) or files[p][0] != v:
                errfunc('**warning** '+
                    p +' is a duplicate torrent for '+new_parsed[h]['path'])
            new_blocked[p] = 1
            continue
 
        if NOISY:
            errfunc('adding '+p)
        try:
            ff = open(p, 'rb')
            d = bdecode(ff.read())
            check_info(d['info'])
            h = sha1(bencode(d['info'])).digest()
            new_file[1] = h
            if new_parsed.has_key(h):
                errfunc('**warning** '+
                    p +' is a duplicate torrent for '+new_parsed[h]['path'])
                new_blocked[p] = 1
                continue
 
            a = {}
            a['path'] = p
            f = os.path.basename(p)
            a['file'] = f
            a['type'] = torrent_type[p]
            i = d['info']
            l = 0
            nf = 0
            if i.has_key('length'):
                l = i.get('length',0)
                nf = 1
            elif i.has_key('files'):
                for li in i['files']:
                    nf += 1
                    if li.has_key('length'):
                        l += li['length']
            a['numfiles'] = nf
            a['length'] = l
            a['name'] = i.get('name', f)
            def setkey(k, d = d, a = a):
                if d.has_key(k):
                    a[k] = d[k]
            setkey('failure reason')
            setkey('warning message')
            setkey('announce-list')
            if return_metainfo:
                a['metainfo'] = d
        except:
            errfunc('**warning** '+p+' has errors')
            new_blocked[p] = 1
            continue
        try:
            ff.close()
        except:
            pass
        if NOISY:
            errfunc('... successful')
        new_parsed[h] = a
        added[h] = a
 
    for p,v in files.items():       # and finally, mark removed torrents
        if not new_files.has_key(p) and not blocked.has_key(p):
            if NOISY:
                errfunc('removing '+p)
            removed[v[1]] = parsed[v[1]]
 
    if NOISY:
        errfunc('done checking')
    return (new_parsed, new_files, new_blocked, added, removed)