#!/usr/bin/python
#
# Copyright (C) 2004 marduk enterprises <marduk@python.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

"""This module contains classes useful for manipulating the iriver
IH-100 style databases.  The main class is the IRdb class and it
expects/uses the File and FileCollection classes.
"""

__revision__ = '0.3'

import os
import ID3
from struct import pack

# Try to import the vorbis package, but no biggie if we don't have it.
try:
    import vorbis
except ImportError:
    vorbis = None

class FileCollection:
    """Collection of files on filesystem"""

    def __init__(self, root):
        self.root = root
    
    def exists(self, fname):
        """Return true if file actually exists on the filesystem
        (at root).
        """

        return os.path.exists(os.path.join(self.root, fname))
        
    def files(self):
        """This is a generator that yields the files in the filesystem."""

        for iteration in os.walk(self.root):
            # Note I use _file to get around the reserved word.  I could use f
            # or something like that but i find _file more descriptive
            files = iteration[2]
            _dir = iteration[0]
            for _file in files:
                yield File( self.root, os.path.join( _dir, _file ) )

class File:
    """A file on the filesystem."""

    def __init__( self, root, path ):
        self.path = path
        self.fix_path = self.path.replace( '/', '\\' )
        self.fix_path = self.fix_path[ len( root ): ]
        self.data = None
        if self.fix_path[0] != '\\': 
            self.fix_path = "\\%s" % self.fix_path
        try:
            self.data = ID3.ID3( self.path )
        except ID3.InvalidTagError:
            if vorbis:
                try:
                    self.data = vorbis.VorbisFile( self.path )
                except vorbis.VorbisError:
                    pass
        
class IRdb:
    """iriver database. right now you can only create a new one and add
    files to it.  In the future you will be able to open an existing one
    and extract info from it.  Note when using add your _files should probably
    come from the same root (or FileCollection)
    """

    def __init__(self):
        """constructor"""
        self.fp = None
        self.access_mode = None
        
    def new(self, fp):
        """Create a new iriver db with given file object"""

        self.fp = fp
        self.fp.write(pack( '32s', 'iRivDB Ver 0.12' ) )
        self.fp.write(pack( '32s','iRiver iHP-100 DB File') )
        
        self.fnum = 0
        self.offset = 0xa0c0
        
    def add(self, _file):
        """Add File object _file to the file object. Only call this after you
            have created a new() database"""

        if not _file.data:
            # There is no tag info in this filename.
            return
        self.fnum = self.fnum + 1
        self.fp.seek( 0x80 + 4*(self.fnum -1) )
        self.fp.write(pack( 'l', self.offset - 0xa0c0 ))
        self.fp.seek(self.offset)
        
        fnlen = len(_file.fix_path) + 1
        song_name = _file.data.get('TITLE','Unknown')
        snlen = len(song_name) + 1
        artist_name = _file.data.get('ARTIST','Unknown')
        arlen = len(artist_name) + 1
        album_name = _file.data.get('ALBUM','Unknown')
        allen = len(album_name) + 1
        genre = _file.data.get('GENRE','Unknown')
        gelen = len(genre) + 1
        
        self.fp.write(pack( 'hhhhh', fnlen, snlen, arlen, allen, gelen ))
        record = "%s\x00%s\x00%s\x00%s\x00%s\x00" % (
           _file.fix_path, song_name, artist_name, album_name, genre )
        self.fp.write(record)
        
        self.offset = self.fp.tell()
    
    def close(self):
        """Close a new()-ly created db file"""

        self.fp.seek(0x40)
        self.fp.write(pack( 'l', self.fnum )) # file total
        self.fp.seek(0xa080)
        self.fp.write(pack( '32s', 'Designed by iRiver') )
        self.fp.close()
        self.fp = None
        self.access_mode = None
        
    #def __del__(self):
    #    if self.access_mode == 'w':
    #        self.close()

def main(argv):
    """main script function"""

    try:
        root = argv[1]
    except IndexError:
        root = "."
    db_filename = os.path.join( root, "iRivNavi.iDB" )
    try:
        os.remove(db_filename)
    except OSError:
        pass
    filetree = FileCollection(root)
    db = IRdb()
    fp = open( db_filename, 'wb' )
    db.new(fp)
    for _file in filetree.files():
        print _file.fix_path
        if _file.data:
            print " \__%s - %s" % ( _file.data.get("ARTIST"),
                    _file.data.get("TITLE") )
        db.add(_file)
    db.close()
    return 0

if __name__ == '__main__':
    import sys
    sys.exit(main(sys.argv))
