import os
import re
from io import BytesIO
import base64
import hashlib
import mutagen
from PIL import Image

import util
import variables as var
from media.item import BaseItem, item_builders, item_loaders, item_id_generators, ValidationFailedError
import constants

'''
type : file
    id
    path
    title
    artist
    duration
    thumbnail
    user
'''


def file_item_builder(**kwargs):
    return FileItem(kwargs['path'])


def file_item_loader(_dict):
    return FileItem("", _dict)


def file_item_id_generator(**kwargs):
    return hashlib.md5(kwargs['path'].encode()).hexdigest()


item_builders['file'] = file_item_builder
item_loaders['file'] = file_item_loader
item_id_generators['file'] = file_item_id_generator


class FileItem(BaseItem):
    def __init__(self, path, from_dict=None):
        if not from_dict:
            super().__init__()
            self.path = path
            self.title = ""
            self.artist = ""
            self.thumbnail = None
            self.id = hashlib.md5(path.encode()).hexdigest()
            if os.path.exists(self.uri()):
                self._get_info_from_tag()
                self.ready = "yes"
                self.duration = util.get_media_duration(self.uri())
            self.keywords = self.title + " " + self.artist
        else:
            super().__init__(from_dict)
            self.artist = from_dict['artist']
            self.thumbnail = from_dict['thumbnail']
            try:
                self.validate()
            except ValidationFailedError:
                self.ready = "failed"

        self.type = "file"

    def uri(self):
        return var.music_folder + self.path if self.path[0] != "/" else self.path

    def is_ready(self):
        return True

    def validate(self):
        if not os.path.exists(self.uri()):
            self.log.info(
                "file: music file missed for %s" % self.format_debug_string())
            raise ValidationFailedError(constants.strings('file_missed', file=self.path))

        if self.duration == 0:
            self.duration = util.get_media_duration(self.uri())
            self.version += 1  # 0 -> 1, notify the wrapper to save me
        self.ready = "yes"
        return True

    def _get_info_from_tag(self):
        match = re.search(r"(.+)\.(.+)", self.uri())
        assert match is not None

        file_no_ext = match[1]
        ext = match[2]

        try:
            im = None
            path_thumbnail = file_no_ext + ".jpg"
            if os.path.isfile(path_thumbnail):
                im = Image.open(path_thumbnail)

            if ext == "mp3":
                # title: TIT2
                # artist: TPE1, TPE2
                # album: TALB
                # cover artwork: APIC:
                tags = mutagen.File(self.uri())
                if 'TIT2' in tags:
                    self.title = tags['TIT2'].text[0]
                if 'TPE1' in tags:  # artist
                    self.artist = tags['TPE1'].text[0]

                if im is None:
                    if "APIC:" in tags:
                        im = Image.open(BytesIO(tags["APIC:"].data))

            elif ext == "m4a" or ext == "m4b" or ext == "mp4" or ext == "m4p":
                # title: ©nam (\xa9nam)
                # artist: ©ART
                # album: ©alb
                # cover artwork: covr
                tags = mutagen.File(self.uri())
                if '©nam' in tags:
                    self.title = tags['©nam'][0]
                if '©ART' in tags:  # artist
                    self.artist = tags['©ART'][0]

                if im is None:
                    if "covr" in tags:
                        im = Image.open(BytesIO(tags["covr"][0]))

            if im:
                self.thumbnail = self._prepare_thumbnail(im)
        except:
            pass

        if not self.title:
            self.title = os.path.basename(file_no_ext)

    @staticmethod
    def _prepare_thumbnail(im):
        im.thumbnail((100, 100), Image.ANTIALIAS)
        buffer = BytesIO()
        im = im.convert('RGB')
        im.save(buffer, format="JPEG")
        return base64.b64encode(buffer.getvalue()).decode('utf-8')

    def to_dict(self):
        dict = super().to_dict()
        dict['type'] = 'file'
        dict['path'] = self.path
        dict['title'] = self.title
        dict['artist'] = self.artist
        dict['thumbnail'] = self.thumbnail
        return dict

    def format_debug_string(self):
        return "[file] {descrip} ({path})".format(
            descrip=self.format_title(),
            path=self.path
        )

    def format_song_string(self, user):
        return constants.strings("file_item",
                                 title=self.title,
                                 artist=self.artist if self.artist else '??',
                                 user=user
                                 )

    def format_current_playing(self, user):
        display = constants.strings("now_playing", item=self.format_song_string(user))
        if self.thumbnail:
            thumbnail_html = '<img width="80" src="data:image/jpge;base64,' + \
                             self.thumbnail + '"/>'
            display += "<br />" + thumbnail_html

        return display

    def format_title(self):
        title = self.title if self.title else self.path
        if self.artist:
            return self.artist + " - " + title
        else:
            return title

    def display_type(self):
        return constants.strings("file")

@@ -3697,24 +3697,1288 @@
 ovr%22%5D%5B0%5D))%0A%0A
+            elif ext == %22opus%22:%0A                # title: 'title'%0A                # artist: 'artist'%0A                # album: 'album'%0A                # cover artwork: 'metadata_block_picture', and then:%0A                ##                          %7C%0A                ##                          %7C%0A                ##                          v%0A                ##            Decode string as base64 binary%0A                ##                          %7C%0A                ##                          v%0A                ##      Open that binary as a mutagen.flac.Picture%0A                ##                          %7C%0A                ##                          v%0A                ##              Extract binary image data%0A                tags = mutagen.File(self.uri())%0A                if 'title' in tags:%0A                    self.title = tags%5B'title'%5D%5B0%5D%0A                if 'artist' in tags:%0A                    self.artist = tags%5B'artist'%5D%5B0%5D%0A%0A                if im is None:%0A                    if 'metadata_block_picture' in tags:%0A                        pic_as_base64 = tags%5B'metadata_block_picture'%5D%5B0%5D%0A                        as_flac_picture = mutagen.flac.Picture(base64.b64decode(pic_as_base64))%0A                        im = Image.open(BytesIO(as_flac_picture.data))%0A%0A
             

Add opus metadata capabilities