##
# 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.
##

import os, time
import gtk
import Disk

class DiskList:
    """This class contains all the functionality for maintain the list 
    of disk (e.g. CD-ROMs and DVDs). This reading from/saving to file, 
    adding (scanning) of new disks and similar actions."""

    def __init__ (self):
        """Creates a new empty DiskList object and a flag for tracking
        modifications."""

        self.content = []
        self.modified = 0
        
        # this typle contains all media types which can be assigned to the stored disks
        # (each entry is a subtuble with ID and description text)
        self.media_types = ( 
            ('CD', 'CD'),
            ('DVD', 'DVD'),
            ('DISK', 'Disk'),
            ('ZIP', 'ZIP disk'),
            ('CARD', 'Memory card'),
            ('TAPE', 'Tape'),
        )

    def read_disk_list (self, filename):
        """This method reads the content of the DiskList from the
        specified file. If the file is not available, an empty list
        will be created (on first startup)."""

        print "Reading disk list from '" + filename + "' ..."
        try:
            file = open (filename, "r")
            allLines = file.readlines ()

            # parse all lines of file
            for currLine in allLines:
                if len (currLine) == 0:
                    # stop parsing
                    break            
                # the first charcter of line determines the "data type"
                if currLine[0] == 'C':
                    # this is a new disk definiton ('C' prefix)
                    # (use default media type 'CD', old databases does not have a type)
                    newDisk = Disk.Disk (currLine[1:len(currLine)-1], 'CD', '', None, [])
                    self.content.append (newDisk)
                    
                    # process GTK event queue (otherwise the GUI looks dead on huge archives)
                    # (i know it's ugly to have GUI code here :-)
                    gtk.main_iteration (False)
                else:
                    lastDisk = self.content[len (self.content)-1]
                    
                    if currLine[0] == 'T':
                        # this is the media type ('T' prefix) for last disk
                        lastDisk.type = currLine[1:len(currLine)-1]
                    else:
                        if currLine[0] == 'R':
                            # this is a remark (comment) ('R' prefix) for last disk
                            lastDisk.remark = currLine[1:len(currLine)-1]
                        else:
                            if currLine[0] == 'A':
                                # this is the timestamp (when it was added) ('A' prefix) for last disk
                                lastDisk.timestamp = int (currLine[1:len(currLine)-1])
                            else:
                                if currLine[0] == 'D':
                                    # this is a new directory ('D' prefix) for last disk
                                    lastDisk.content.append ([currLine[1:len(currLine)-1],[]])
                                else:
                                    if currLine[0] == 'F':
                                        # this is a new file ('F' prefix) for the last directory
                                        lastDisk.content[len (lastDisk.content)-1][1].append ([currLine[1:len(currLine)-1], None])
                                    else:
                                        if currLine[0] == 'S':
                                            # this is the size in bytes of the last added file ('S' prefix)
                                            filesize = long (currLine[1:len(currLine)-1])
                                            currentDirectoryContent = lastDisk.content[len (lastDisk.content)-1][1]
                                            currentDirectoryContent[len (currentDirectoryContent)-1][1] = filesize
            file.close ()
        except IOError, args:
            print "Failed to read disk list from file ..."
            print "Exception: " + str (args)
            print "Using an empty disk list ..."

    def save_disk_list (self, filename):
        """This method saves the content of the disk list to the
        specified file. On success the method returns 0, othwerise -1."""

        print "Saving disk list to '" + filename + "' ..."
        try:
            file = open (filename, "w")

            for disk in self.content:
                # disk name starts with 'C' prefix
                file.write ("C" + disk.name + "\n")
                
                # process GTK event queue (otherwise the GUI looks dead on huge archives)
                # (i know it's ugly to have GUI code here :-)
                gtk.main_iteration (False)
                
                # disk media type starts with 'T' prefix (is optional)
                if len (disk.type) > 0:
                    file.write ("T" + disk.type + "\n")

                # disk remark starts with 'R' prefix (is optional)
                if len (disk.remark) > 0:
                    file.write ("R" + disk.remark + "\n")

                # disk added timestamp starts with 'A' prefix (is optional)
                if disk.timestamp != None:
                    file.write ("A" + str (disk.timestamp) + "\n")

                for dir in disk.content:
                    # directory name starts with 'D' prefix
                    file.write ("D" + dir[0] + "\n")
                    
                    for dirFile in dir[1]:
                        # file name starts with 'F' prefix
                        file.write ("F" + dirFile[0] + "\n")
                        
                        # file size starts with 'S' prefix (is optional)
                        if dirFile[1] != None:
                            file.write ("S" + str (dirFile[1]) + "\n")
                        
            file.flush ()
            file.close ()
        except IOError, args:
            print "Failed to write disk list to file ..."
            print "Exception: " + str (args)
            return -1

        return 0

    def add_disk (self, name, type, remark, mount_point):
        """This method adds a new disk to this list. The content of the
        disk will be scanned before. On errors -1 will be returned."""

        try:
            # scan the disk
            diskContent = []
            self.scan_directory (mount_point, diskContent)
            
            # check for content
            if diskContent == []:
                print "Error: Failed to add disk, there are no files (probably not mounted) ..."
                return -1

            # create disk, remember timestamp and add to list
            newDisk = Disk.Disk (name, type, remark, int (time.time ()), diskContent)
            self.add_scanned_disk (newDisk)

        except OSError, args:
            print "Failed to read disk content ..."
            print "Exception: " + str (args)
            return -1

    def scan_directory (self, diskPath, content, dirName = "/"):
        """This method scans recursivly the directory specified by
        'diskPath+"/"+dirName' and appends the found files to
        the content list. See Disk class doc for list format."""
        
        currentDirContent = []

        # process all files in directory (exception handling required, 
        # sometimes it's not possible to access the directory, e.g. 
        # system folders on Win32 NTFS partitions)
        try:
            filesInDir = os.listdir (diskPath + "/" + dirName)
        except OSError, args:
            filesInDir = []
            print "WARNING: Failed to read content of directory '" + diskPath + "/" + dirName + "' ..."
            print "Exception: " + str (args)
        
        filesInDir.sort ()
        for file in filesInDir:
            
            # process GTK event queue (otherwise the GUI looks dead while adding huge disks)
            # (i know it's ugly to have GUI code here :-)
            gtk.main_iteration (False)
            
            # sometimes there are empty strings for some reason :-)
            if file == "":
                continue

            # ignore all links, they can cause problems (e.g. recursive links on Woody CD's)
            if os.path.islink (diskPath + "/" + dirName + "/" + file):
                continue

            if os.path.isdir (diskPath + "/" + dirName + "/" + file):
                # this is a sub-directory => so scan and append it
                scanDir = "/" + file;
                if dirName != "/":
                    scanDir = dirName + "/" + file
                self.scan_directory (diskPath, content, scanDir)
            else:
                # this is a file => append to list
                # this is a file => append name, size and date to list
                fStat = os.stat (diskPath + "/" + dirName + "/" + file)
                currentDirContent.append ([file, fStat.st_size])

        # add this directory to content
        content.append ([dirName, currentDirContent])

    def add_scanned_disk (self, disk):
        """This method adds the specified disk (has allready conent) in alphabetical
        order (ignore case) to list to the disk list."""

        fDiskAdded = 0
        for i in range (0, len (self.content)):
            if (disk.name.lower () < self.content[i].name.lower ()):
                self.content.insert (i, disk)
                fDiskAdded = 1
                break;

        if fDiskAdded == 0:
            self.content.append (disk)

        self.modified = 1
        print "Disk '" + disk.name + "' was added to list ..."

    def remove_disk (self, name):
        """This method removes the disk specified by name from list.
        On success 0 will be returned, otherwise -1."""

        for disk in self.content:
            if name == disk.name:
                self.content.remove (disk)
                self.modified = 1
                print "Disk '" + name + "' was removed from list ..."
                return 0
        return -1
