# Copyright 2015 - 2018 Sertan Şentürk
#
# This file is part of tomato: https://github.com/sertansenturk/tomato/
#
# tomato is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation (FSF), either version 3 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 General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License v3.0
# along with this program. If not, see http://www.gnu.org/licenses/
#
# If you are using this extractor please cite the following thesis:
#
# Şentürk, S. (2016). Computational analysis of audio recordings and music
# scores for the description and discovery of Ottoman-Turkish makam music.
# PhD thesis, Universitat Pompeu Fabra, Barcelona, Spain.

import warnings

import musicbrainzngs as mb

from .. import __version__
from ..io import IO

mb.set_useragent("tomato", __version__, "compmusic.upf.edu")


class Work:
    @classmethod
    def from_musicbrainz(cls, mbid, get_recording_rels=True,
                         print_warnings=None):
        included_rels = (['artist-rels', 'recording-rels']
                         if get_recording_rels else ['artist-rels'])
        work = mb.get_work_by_id(mbid, includes=included_rels)['work']

        metadata = {
            'makam': [], 'form': [], 'usul': [], 'title': work['title'],
            'mbid': mbid, 'composer': dict(), 'lyricist': dict(),
            'url': 'http://musicbrainz.org/work/' + mbid, 'language': ''}

        # assign makam, form, usul attributes to data
        cls._assign_makam_form_usul(metadata, mbid, work)

        # language
        cls._assign_language(metadata, work)

        # composer and lyricist
        cls._assign_composer_lyricist(metadata, work)

        # add recordings
        cls._assign_recordings(metadata, work)

        # warnings
        cls._check_warnings(metadata, print_warnings)

        return metadata

    @classmethod
    def _check_warnings(cls, metadata, print_warnings=None):
        if print_warnings:
            cls._data_key_exists(metadata, dkey='makam')
            cls._data_key_exists(metadata, dkey='form')
            cls._data_key_exists(metadata, dkey='usul')
            cls._data_key_exists(metadata, dkey='composer')

            if 'language' in metadata.keys():  # language in MusicBrainz
                cls._check_lyricist(metadata)
            else:  # no lyrics information in MusicBrainz
                cls._check_language(metadata)

    @staticmethod
    def _check_language(metadata):
        if metadata['lyricist']:  # lyricist available
            warnings.warn(
                'http://musicbrainz.org/work/{0:s} Language of the vocal '
                'work is not entered!'.format(metadata['mbid']), stacklevel=2)
        else:
            warnings.warn('http://musicbrainz.org/work/{0:s} Language is not '
                          'entered!'.format(metadata['mbid']), stacklevel=2)

    @staticmethod
    def _check_lyricist(metadata):
        if metadata['language'] == "zxx":  # no lyrics
            if metadata['lyricist']:
                warnings.warn('http://musicbrainz.org/work/{0:s} Lyricist is '
                              'entered to the instrumental work!'.
                              format(metadata['mbid']), stacklevel=2)
        else:  # has lyrics
            if not metadata['lyricist']:
                warnings.warn('http://musicbrainz.org/work/{0:s} Lyricist is '
                              'not entered!'.format(metadata['mbid']),
                              stacklevel=2)

    @staticmethod
    def _data_key_exists(metadata, dkey):
        if not metadata[dkey]:
            warnings.warn('http://musicbrainz.org/work/{0:s} {1:s} is not '
                          'entered!'.format(metadata['mbid'], dkey.title()),
                          stacklevel=2)

    @staticmethod
    def _assign_recordings(metadata, work):
        metadata['recordings'] = []
        if 'recording-relation-list' in work.keys():
            for r in work['recording-relation-list']:
                metadata['recordings'].append(
                    {'mbid': r['recording']['id'],
                     'title': r['recording']['title']})

    @staticmethod
    def _assign_composer_lyricist(metadata, work):
        if 'artist-relation-list' in work.keys():
            for a in work['artist-relation-list']:
                if a['type'] in ['composer', 'lyricist']:
                    metadata[a['type']] = {'name': a['artist']['name'],
                                           'mbid': a['artist']['id']}

    @staticmethod
    def _assign_language(metadata, work):
        if 'language' in work.keys():
            metadata['language'] = work['language']

    @classmethod
    def _assign_makam_form_usul(cls, metadata, mbid, work):
        if 'attribute-list' in work.keys():
            w_attrb = work['attribute-list']
            for attr_name in ['makam', 'form', 'usul']:
                cls._assign_attribute(metadata, mbid, w_attrb, attr_name)

    @classmethod
    def _assign_attribute(cls, metadata, mbid, w_attrb, attrname):
        attr = [a['value'] for a in w_attrb
                if attrname.title() in a['attribute']]
        metadata[attrname] = [
            {'mb_attribute': m,
             'attribute_key': cls._get_key_from_musicbrainz_attribute(
                 m, attrname),
             'source': 'http://musicbrainz.org/work/' + mbid}
            for m in attr]

    @staticmethod
    def _get_key_from_musicbrainz_attribute(attr_str, attr_type):
        attr_dict = IO.load_music_data(attr_type)
        for attr_key, attr_val in attr_dict.items():
            if attr_val['dunya_name'] == attr_str:
                return attr_key

        raise ValueError("Unknown MusicBrainz key: %s" % attr_str)
