# -*- coding: utf-8 -*-

import replaygain

import os
import hashlib
import mutagen
import logging
import math
import traceback


"""
list of supported easy tags in mutagen version 1.20
['albumartistsort', 'musicbrainz_albumstatus', 'lyricist', 'releasecountry',
'date', 'performer', 'musicbrainz_albumartistid', 'composer', 'encodedby',
'tracknumber', 'musicbrainz_albumid', 'album', 'asin', 'musicbrainz_artistid',
'mood', 'copyright', 'author', 'media', 'length', 'version', 'artistsort',
'titlesort', 'discsubtitle', 'website', 'musicip_fingerprint', 'conductor',
'compilation', 'barcode', 'performer:*', 'composersort', 'musicbrainz_discid',
'musicbrainz_albumtype', 'genre', 'isrc', 'discnumber', 'musicbrainz_trmid',
'replaygain_*_gain', 'musicip_puid', 'artist', 'title', 'bpm', 'musicbrainz_trackid',
'arranger', 'albumsort', 'replaygain_*_peak', 'organization']
"""

class AirtimeMetadata:

    def __init__(self):

        self.airtime2mutagen = {\
        "MDATA_KEY_TITLE": "title", \
        "MDATA_KEY_CREATOR": "artist", \
        "MDATA_KEY_SOURCE": "album", \
        "MDATA_KEY_GENRE": "genre", \
        "MDATA_KEY_MOOD": "mood", \
        "MDATA_KEY_TRACKNUMBER": "tracknumber", \
        "MDATA_KEY_BPM": "bpm", \
        "MDATA_KEY_LABEL": "organization", \
        "MDATA_KEY_COMPOSER": "composer", \
        "MDATA_KEY_ENCODER": "encodedby", \
        "MDATA_KEY_CONDUCTOR": "conductor", \
        "MDATA_KEY_YEAR": "date", \
        "MDATA_KEY_URL": "website", \
        "MDATA_KEY_ISRC": "isrc", \
        "MDATA_KEY_COPYRIGHT": "copyright", \
        }

        self.mutagen2airtime = {\
        "title": "MDATA_KEY_TITLE", \
        "artist": "MDATA_KEY_CREATOR", \
        "album": "MDATA_KEY_SOURCE", \
        "genre": "MDATA_KEY_GENRE", \
        "mood": "MDATA_KEY_MOOD", \
        "tracknumber": "MDATA_KEY_TRACKNUMBER", \
        "bpm": "MDATA_KEY_BPM", \
        "organization": "MDATA_KEY_LABEL", \
        "composer": "MDATA_KEY_COMPOSER", \
        "encodedby": "MDATA_KEY_ENCODER", \
        "conductor": "MDATA_KEY_CONDUCTOR", \
        "date": "MDATA_KEY_YEAR", \
        "website": "MDATA_KEY_URL", \
        "isrc": "MDATA_KEY_ISRC", \
        "copyright": "MDATA_KEY_COPYRIGHT", \
        }

        self.logger = logging.getLogger()

    def get_md5(self, filepath):
        """
        Returns an md5 of the file located at filepath. Returns an empty string
        if there was an error reading the file.
        """
        try:
            f = open(filepath, 'rb')
            m = hashlib.md5()
            m.update(f.read())
            md5 = m.hexdigest()
        except Exception, e:
            return ""

        return md5

    ## mutagen_length is in seconds with the format (d+).dd
    ## return format hh:mm:ss.uuu
    def format_length(self, mutagen_length):
        t = float(mutagen_length)
        h = int(math.floor(t / 3600))
        t = t % 3600
        m = int(math.floor(t / 60))

        s = t % 60
        # will be ss.uuu
        s = str(s)
        seconds = s.split(".")
        s = seconds[0]

        # have a maximum of 6 subseconds.
        if len(seconds[1]) >= 6:
            ss = seconds[1][0:6]
        else:
            ss = seconds[1][0:]

        length = "%s:%s:%s.%s" % (h, m, s, ss)

        return length

    def save_md_to_file(self, m):
        try:
            airtime_file = mutagen.File(m['MDATA_KEY_FILEPATH'], easy=True)

            for key in m:
                if key in self.airtime2mutagen:
                    value = m[key]

                    if value is not None:
                        value = unicode(value)
                    else:
                        value = unicode('');

                        #if len(value) > 0:
                    self.logger.debug("Saving key '%s' with value '%s' to file", key, value)
                    airtime_file[self.airtime2mutagen[key]] = value

            airtime_file.save()
        except Exception, e:
            self.logger.error('Trying to save md')
            self.logger.error('Exception: %s', e)
            self.logger.error('Filepath %s', m['MDATA_KEY_FILEPATH'])

    def truncate_to_length(self, item, length):
        if isinstance(item, int):
            item = str(item)
        if isinstance(item, basestring):
            if len(item) > length:
                return item[0:length]
            else:
                return item

    def get_md_from_file(self, filepath):
        """
        Returns None if error retrieving metadata. Otherwise returns a dictionary
        representing the file's metadata
        """

        self.logger.info("getting info from filepath %s", filepath)

        md = {}

        replay_gain_val = replaygain.calculate_replay_gain(filepath)
        self.logger.info('ReplayGain calculated as %s for %s' % (replay_gain_val, filepath))
        md['MDATA_KEY_REPLAYGAIN'] = replay_gain_val

        try:

            md5 = self.get_md5(filepath)
            md['MDATA_KEY_MD5'] = md5

            file_info = mutagen.File(filepath, easy=True)
        except Exception, e:
            self.logger.error("failed getting metadata from %s", filepath)
            self.logger.error("Exception %s", e)
            return None


        #check if file has any metadata
        if file_info is None:
            return None

        for key in file_info.keys() :
            if key in self.mutagen2airtime:
                val = file_info[key]
                try:
                    if val is not None and len(val) > 0 and val[0] is not None and len(val[0]) > 0:
                        md[self.mutagen2airtime[key]] = val[0]
                except Exception, e:
                    self.logger.error('Exception: %s', e)
                    self.logger.error("traceback: %s", traceback.format_exc())
        if 'MDATA_KEY_TITLE' not in md:
            #get rid of file extension from original name, name might have more than 1 '.' in it.
            original_name = os.path.basename(filepath)
            original_name = original_name.split(".")[0:-1]
            original_name = ''.join(original_name)
            md['MDATA_KEY_TITLE'] = original_name

        #incase track number is in format u'4/11'
        #need to also check that the tracknumber is even a tracknumber (cc-2582)
        if 'MDATA_KEY_TRACKNUMBER' in md:
            try:
                md['MDATA_KEY_TRACKNUMBER'] = int(md['MDATA_KEY_TRACKNUMBER'])
            except Exception, e:
                pass

            if isinstance(md['MDATA_KEY_TRACKNUMBER'], basestring):
                try:
                    md['MDATA_KEY_TRACKNUMBER'] = int(md['MDATA_KEY_TRACKNUMBER'].split("/")[0], 10)
                except Exception, e:
                    del md['MDATA_KEY_TRACKNUMBER']

        #make sure bpm is valid, need to check more types of formats for this tag to assure correct parsing.
        if 'MDATA_KEY_BPM' in md:
            if isinstance(md['MDATA_KEY_BPM'], basestring):
                try:
                    md['MDATA_KEY_BPM'] = int(md['MDATA_KEY_BPM'])
                except Exception, e:
                    del md['MDATA_KEY_BPM']

        #following metadata is truncated if needed to fit db requirements.
        if 'MDATA_KEY_GENRE' in md:
            md['MDATA_KEY_GENRE'] = self.truncate_to_length(md['MDATA_KEY_GENRE'], 64)

        if 'MDATA_KEY_TITLE' in md:
            md['MDATA_KEY_TITLE'] = self.truncate_to_length(md['MDATA_KEY_TITLE'], 512)

        if 'MDATA_KEY_CREATOR' in md:
            md['MDATA_KEY_CREATOR'] = self.truncate_to_length(md['MDATA_KEY_CREATOR'], 512)

        if 'MDATA_KEY_SOURCE' in md:
            md['MDATA_KEY_SOURCE'] = self.truncate_to_length(md['MDATA_KEY_SOURCE'], 512)

        if 'MDATA_KEY_MOOD' in md:
            md['MDATA_KEY_MOOD'] = self.truncate_to_length(md['MDATA_KEY_MOOD'], 64)

        if 'MDATA_KEY_LABEL' in md:
            md['MDATA_KEY_LABEL'] = self.truncate_to_length(md['MDATA_KEY_LABEL'], 512)

        if 'MDATA_KEY_COMPOSER' in md:
            md['MDATA_KEY_COMPOSER'] = self.truncate_to_length(md['MDATA_KEY_COMPOSER'], 512)

        if 'MDATA_KEY_ENCODER' in md:
            md['MDATA_KEY_ENCODER'] = self.truncate_to_length(md['MDATA_KEY_ENCODER'], 255)

        if 'MDATA_KEY_CONDUCTOR' in md:
            md['MDATA_KEY_CONDUCTOR'] = self.truncate_to_length(md['MDATA_KEY_CONDUCTOR'], 512)

        if 'MDATA_KEY_YEAR' in md:
            md['MDATA_KEY_YEAR'] = self.truncate_to_length(md['MDATA_KEY_YEAR'], 16)

        if 'MDATA_KEY_URL' in md:
            md['MDATA_KEY_URL'] = self.truncate_to_length(md['MDATA_KEY_URL'], 512)

        if 'MDATA_KEY_ISRC' in md:
            md['MDATA_KEY_ISRC'] = self.truncate_to_length(md['MDATA_KEY_ISRC'], 512)

        if 'MDATA_KEY_COPYRIGHT' in md:
            md['MDATA_KEY_COPYRIGHT'] = self.truncate_to_length(md['MDATA_KEY_COPYRIGHT'], 512)
        #end of db truncation checks.

        try:
            md['MDATA_KEY_BITRATE'] = getattr(file_info.info, "bitrate", 0)
            md['MDATA_KEY_SAMPLERATE'] = getattr(file_info.info, "sample_rate", 0)

            md['MDATA_KEY_DURATION'] = self.format_length(getattr(file_info.info, "length", 0.0))

            md['MDATA_KEY_MIME'] = ""
            if len(file_info.mime) > 0:
                md['MDATA_KEY_MIME'] = file_info.mime[0]
        except Exception as e:
            self.logger.warn(e)

        if "mp3" in md['MDATA_KEY_MIME']:
            md['MDATA_KEY_FTYPE'] = "audioclip"
        elif "vorbis" in md['MDATA_KEY_MIME']:
            md['MDATA_KEY_FTYPE'] = "audioclip"
        else:
            self.logger.error("File %s of mime type %s does not appear to be a valid vorbis or mp3 file." % (filepath, md['MDATA_KEY_MIME']))
            return None

        return md