#
# $Id$
#
# Description:
#   This defines the model's for the "music" app in the mediaserv
#   project. These models represent things like 'tracks' 'albums' etc.
#

import datetime

# We import meta from django.core because that is where all the fields and like
# stuff is defined.
#
from django.core import meta

#############################################################################
#
# Here are the django "models" of objects that the mediaserv "music" app cares
# about
#

#############################################################################
#
class MusicRoot(meta.Model):
    """Simply because I wanted to store as much configuration information
    information in our database as seemed useful I have the MusicRoot class. It
    describes a path on the local file system were music files may be found.

    You can have more than one MusicRoot, obviously.
    """

    fields = (
        meta.CharField('directory', maxlength = 1024),
        meta.DateTimeField('last_scan_started', blank = True, null = True),
        meta.DateTimeField('last_scan_finished', blank = True, null = True),
        )

    admin = meta.Admin()
    
    #########################################################################
    #
    def __repr__(self):
        return self.directory

#############################################################################
#
class Artist(meta.Model):
    """An artist. They will have a one to many relationship with
    ArtistNames. They may have a simplified_name 
    name, and a many to many relationship with ArtistNames.

    Not sure what other information we will have under the Artist object but it
    seems to make sense to say that an artist is more than just a name.
    """

    fields = (
        meta.CharField('comment', null = True, blank = True, maxlength = 1024),
        meta.DateTimeField('date_added'),
        )

    admin = meta.Admin()
    
    #########################################################################
    #
    def __repr__(self):
        """Return the highest preference artist name, if this artist has any
        names. If they do not just return the id.
        """
        if self.get_artistname_count() > 0:
            return repr(self.get_artistname_list()[0])
        else:
            return str(self.id)

    #########################################################################
    #
    def get_simple_name(self):
        """Look through all the names that this artist has and return the first
        one that is marked as a simplified name.

        If we find no simplified names then return None.
        """
        if self.get_artistname_count() == 0:
            return None
        names = self.get_artistname_list()
        for name in names:
            if name.simple_char_set:
                return name
        return None

#############################################################################
#
class ArtistName(meta.Model):
    """An artist's name. An artist may have more than one name. Each name has a
    preference. The higher the preference the more this name is the one we
    should use if we have to present a single name. This name may also be one
    that is represented in a simple character set - suitable for devices like
    the rio receiver that can not display kanji.
    """
    fields = (
        meta.CharField('name', maxlength = 512, core = True, unique = True),
        meta.IntegerField('preference', default = 0),
        meta.BooleanField('simple_char_set'),
        meta.ForeignKey(Artist, edit_inline = meta.TABULAR, num_in_admin = 3),
        )

    ordering = ['-preference']

    #########################################################################
    #
    def __repr__(self):
        return self.name

#############################################################################
#
class Album(meta.Model):
    """An album is a collection of tracks. A track can only appear in one
    album.

    Do we want to bother with artist/album relationships? I guess not, just get
    the list of tracks, get the list of artists for the tracks and do a unique
    set of those.

    Note: We need to have a simple name/name relationship for albums but this
    is not nearly as complex as it was for artists. Albums are typically only
    known by one name. We just need a simplified version for devices like the
    rio receiver.

    Note: When you get the list of tracks associated with an album it should
    give you the list sorted in the order indicated by the tracks' track_number
    and disk_number.
    """

    fields = (
        meta.CharField('name', maxlength = 512),
        meta.CharField('simplified_name', maxlength = 512, blank = True,
                       null = True),
        meta.DateTimeField('date_added'),
        )
    
    #########################################################################
    #
    def __repr__(self):
        return self.name

#############################################################################
#
class PlayList(meta.Model):
    """Like an album this is a collection of tracks. Unlike an album, a track
    can occur more than once in a play list.

    Tracks in a play list need to have a defined order that is determined by
    the playlist. Not sure how to do this yet.
    """
    fields = (
        meta.CharField('name', maxlength = 512, unique = True),
        meta.CharField('simplified_name', maxlength = 512, blank = True,
                       null = True),
        )

    admin = meta.Admin()
    
    #########################################################################
    #
    def __repr__(self):
        return self.name

#############################################################################
#
class Genre(meta.Model):
    """For better or for worse tracks have a fixed single genre field.
    We will set the 'id' to be the same as the accepted standard set of genres.
    """
    fields = (
        meta.CharField('name', maxlength = 256, unique = True),
        meta.IntegerField('genre_id'),         # To map to the mp3 id field
        )

    #########################################################################
    #
    def __repr__(self):
        return self.name

#############################################################################
#
class Track(meta.Model):
    """A track refers to a single playable file of sound/music media. The basic
    fields are determined from what is available via the id3 tags.

    However things like the 'album' and 'artist' are relations to other object
    model instances.

    We also keep track of some additional field so that we can echo it back to
    iTunes potentially. Named things like 'last played time' and 'number of
    times played.'

    We also keep track of the encoding format of the track. Some players can
    not play some encodings.

    A track may also have a 'limited_name' field which is a representation of
    the track's name in simple ASCII so that devices which can not display rich
    character sets can display a simplified (aka romanized for Japanese track
    names) instead of displaying gobbedly gook.
    """

    fields = (
        meta.CharField('title', maxlength = 512),
        meta.CharField('filename', maxlength = 1024, db_index = True),
        meta.CharField('simplified_name', maxlength = 256, blank = True,
                       null = True),
        meta.IntegerField('year', null = True),
        meta.IntegerField('play_time'),
        meta.IntegerField('bit_rate'),
        meta.BooleanField('vbr'),
        meta.IntegerField('track_number', default = 0),
        meta.IntegerField('disc_number', default = 0),
        meta.IntegerField('play_count', default = 0), # tied to iTunes
        meta.DateTimeField('last_scanned', blank = True, null = True),
        meta.DateTimeField('last_played', null = True), # tied to iTunes
        meta.IntegerField('bpm','beats per minute', null = True),  # iTunes
        meta.CharField('grouping', maxlength = 512, null = True,
                       blank = True),                 # tied to iTunes
        meta.CharField('comments', maxlength = 1024, null = True,
                       blank = True), # tied to iTunes
        meta.ForeignKey(Artist, blank = True, null = True),
        meta.ForeignKey(Album, blank = True, null = True),
        meta.ManyToManyField(PlayList, blank = True, null = True),
        meta.ForeignKey(Genre, blank = True, null = True),
        meta.ForeignKey(MusicRoot),
        )

    ordering = ['track_number', 'disc_number']
        
    #########################################################################
    #
    def __repr__(self):
        return self.title

    #########################################################################
    #
    def play_time_string(self):
        """Convert the playtime we have that is in seconds to a friendlier
        human readable string. This was cribbed from
        Eye3D.tag.getPlayTimeString()
        """
        total = self.play_time
        h = total / 3600
        m = (total % 3600) / 60
        s = (total % 3600) % 60
        if h:
            timeStr = "%d:%.2d:%.2d" % (h, m, s)
        else:
            timeStr = "%d:%.2d" % (m, s)
        return timeStr
        
#############################################################################
#
class Artwork(meta.Model):
    """iTunes lets us associate multiple pieces of artwork with tracks.
    I am thinking of letting albums also have artwork associated with them (who
    would see this, though? Only the web interface so far.)

    A track may have more than one piece of art associated with it.
    """

    fields = (
        meta.ImageField('image'),
        # Maybe we should store some of the file's attributes in our structure?
        # Image size? encoding?
        meta.ManyToManyField(Track),
        )

    admin = meta.Admin()
    
    #########################################################################
    #
##    def __repr__(self):
##        return self.file_name
    
