"""jack.metadata: metadata server"""

# jack.metadata: metadata server for use in
# jack - extract audio from a CD and encode it using 3rd party software
# Copyright (C) 1999-2003  Arne Zellentin <zarne@users.sf.net>

# 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 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
import re

from operator import itemgetter

import jack.functions
import jack.utils
import jack.misc
import jack.freedb
import jack.musicbrainz
import jack.version
import jack.discid

from jack.globals import *


names_available = None          # metadata info is available
dir_created = None              # dirs are only renamed if we have created them
NUM, LEN, START, COPY, PRE, CH, RIP, RATE, NAME = list(range(9))
filenames = []

metadata_servers = {
    'gnudb': {
        'host': "gnudb.gnudb.org",
        'id': jack.version.name + " " + jack.version.version,
        'api': "cddb",
    },
    'freedb': {
        'host': "gnudb.gnudb.org",
        'id': jack.version.name + " " + jack.version.version,
        'api': "cddb",
    },
    'musicbrainz': {
        'host': "musicbrainz.org",
        'id': jack.version.name + " " + jack.version.version,
        'api': "musicbrainzngs",
    },
}

metadata_apis = {
    'cddb': {
        'form_file_extension': ".freedb",
        'names_fn': jack.freedb.freedb_names,
    },
    'musicbrainzngs': {
        'form_file_extension': ".musicbrainz",
        'names_fn': jack.musicbrainz.musicbrainz_names,
    },
}

def get_metadata_api(server):
    "get the api used for the selected metadata server"
    return metadata_servers[server]['api']

def get_metadata_host(server):
    "get the host used for the selected metadata server"
    return metadata_servers[server]['host']

def get_metadata_form_file(api):
    "get the filename for caching metadata"
    return jack.version.name + metadata_apis[api]['form_file_extension']

def interpret_db_file(all_tracks, todo, metadata_form_file, verb, dirs=0, warn=None):
    "read metadata file and rename dir(s)"
    global names_available, dir_created
    metadata_rename = 0
    err, track_names, cd_id, mb_query_data = metadata_names(metadata_id(all_tracks), all_tracks, todo, metadata_form_file, verb=verb, warn=warn)
    if (not err) and dirs:
        metadata_rename = 1

# The user wants us to use the current dir, unconditionally.

        if cf['_claim_dir']:
            dir_created = jack.utils.split_dirname(os.getcwd())[-1]
            jack.functions.progress("all", "mkdir", dir_created)
            cf['_claim_dir'] = 0

        if cf['_rename_dir'] and dir_created:
            new_dirs, new_dir = jack.utils.mkdirname(track_names, cf['_dir_template'])
            old_dir = os.getcwd()
            old_dirs = jack.utils.split_dirname(old_dir)
            dirs_created = jack.utils.split_dirname(dir_created)

# only do the following if we are where we think we are and the dir has to be
# renamed.

            if jack.utils.check_path(dirs_created, old_dirs) and not jack.utils.check_path(dirs_created, new_dirs):
                jack.utils.rename_path(dirs_created, new_dirs)
                info("cwd now " + repr(os.getcwd()))
                jack.functions.progress("all", 'ren', str(dir_created + "-->" + new_dir))

    if not err:
        cd = track_names[0]
        year = genre = None
        if len(cd) > 2:
            if cd[2]:
                year = cd[2][:4]
        if len(cd) > 3:
            genre = repr(cd[3])
        filenames.append('')  # FIXME: possibly put the dir here, but in no
        # case remove this since people access filenames with i[NUM] which
        # starts at 1
        num = 1
        for i in track_names[1:]:
            replacelist = {" ": " ",
                           "n": cf['_rename_num'] % num,
                           "l": cd[1],
                           "t": i[1],
                           "y": year,
                           "g": genre}
            if cf['_various']:
                replacelist["a"] = i[0]
                newname = jack.misc.multi_replace(cf['_rename_fmt_va'], replacelist, "rename_fmt_va", warn=(num == 1))
            else:
                replacelist["a"] = cd[0]
                newname = jack.misc.multi_replace(cf['_rename_fmt'], replacelist, "rename_fmt", warn=(num == 1))
            exec("newname = newname" + cf['_char_filter'])
            newname =  jack.utils.unusable_charmap(newname)
            filenames.append(newname)
            num += 1
        names_available = 1
    else:
        metadata_rename = 0
    return err, track_names, metadata_rename, mb_query_data
# / end of interpret_db_file /#


def metadata_id(tracks, warn=0):
    from jack.globals import START, MSF_OFFSET, CDDA_BLOCKS_PER_SECOND
    "calculate disc-id for FreeDB or MusicBrainz"
    cdtoc = []
    if not tracks:
        if warn:
            warning("no tracks! No disc inserted? No/wrong ripper?")
        return 0

    first_track = 1
    track_offsets = []
    for i in tracks:
        track_offsets.append(i[START] + MSF_OFFSET)
        last_track = i[NUM]
        num_sectors = i[START] + i[LEN] + MSF_OFFSET

    jack.discid.init()
    disc = jack.discid.put(first_track, last_track, num_sectors, track_offsets)
    cd_id = {'cddb': disc.freedb_id, 'musicbrainzngs': disc.id}

    return cd_id


def metadata_template(tracks, names=""):
    api = get_metadata_api(cf['_metadata_server'])
    if api == 'cddb':
        return jack.freedb.freedb_template(tracks, names="")
    elif api == 'musicbrainzngs':
        return jack.musicbrainz.musicbrainz_template(tracks, names="")
    else:
        error("unknown api %s", api)


def metadata_query(cd_id, tracks, file):
    api = get_metadata_api(cf['_metadata_server'])
    if api == 'cddb':
        return jack.freedb.freedb_query(cd_id, tracks, file)
    elif api == 'musicbrainzngs':
        return jack.musicbrainz.musicbrainz_query(cd_id, tracks, file)
    else:
        error("unknown api %s", api)


def metadata_names(cd_id, tracks, todo, name, verb=0, warn=1):
    "select metadata source and return metadata"

    available = []
    for api in metadata_apis.keys():
        form_file = get_metadata_form_file(api)
        if not os.path.exists(form_file):
            continue

        args = (cd_id, tracks, todo, form_file)
        data = metadata_apis[api]['names_fn'](*args, verb=0, warn=0)
        if data[0]:  # error
            debug(f"could not read {form_file}, error {data[0]}")
            continue

        entry = {
                "api": api,
                "form_file": form_file,
                "preferred": api == get_metadata_api(cf['_metadata_server']),
                "mtime": os.path.getmtime(form_file),
                "args": args,
        }
        available.append(entry)

    if len(available) == 0:
        return True, None, None, None  # error

    available.sort(reverse=True, key=itemgetter('mtime'))
    selected = available[0]
    if not selected['preferred']:
        info(f"Using {selected['form_file']}")

    return metadata_apis[selected['api']]['names_fn'](*selected['args'],
            verb=verb, warn=warn)


def split_albumtitle(album_title):
    '''split legacy album title into real album title and medium numbers and medium title, compatible to MusicBrainz'''

    # for instance: David Guetta's "Nothing but the Beat (Disc 1: Vocal Album)" should be split
    # into album title "Nothing but the Beat", medium position 1, medium count -1 (unknown), medium title "Vocal Album"
    # More commonly: Coldplay's "Live In Buenos Aires (CD 2)" should split into "Live In Buenos Aires", 2, -1, None

    # FIXME, medium title extraction is not yet implented

    r1 = re.compile(r'( \(disc[ ]*| \(side[ ]*| \(cd[^a-z0-9]*)([0-9]*|One|Two|A|B)([^a-z0-9])', re.IGNORECASE)
    mo = r1.search(album_title)
    medium_position = None
    if mo != None:
        medium_position = album_title[mo.start(2):mo.end(2)].lstrip("0")
        album_title = album_title[0:mo.start(1)]
        if medium_position == "One" or medium_position == "one" or medium_position == "A":
            medium_position = "1"
        elif medium_position == "Two" or medium_position == "two" or medium_position == "B":
            medium_position = "2"
        return album_title, int(medium_position), -1, None

    return album_title, 0, 0, None

# user says additional info is in the EXTT fields

def metadata_lookup(tracks, cd_id):
    api = get_metadata_api(cf['_metadata_server'])
    if api == 'cddb':
        error("metadata lookup is supported for MusicBrainz only")
    elif api == 'musicbrainzngs':
        return jack.musicbrainz.musicbrainz_lookup(tracks, cd_id)
    else:
        error("unknown api %s", api)
