import { UINT32_LE } from 'token-types';
import { CommonTagMapper } from '../common/GenericTagMapper.js';
import { CaseInsensitiveTagMap } from '../common/CaseInsensitiveTagMap.js';
import { decodeString } from '../common/Util.js';

import type { INativeTagMap } from '../common/GenericTagTypes.js';
import type { INativeMetadataCollector } from '../common/MetadataCollector.js';
import type { IRating, ITag } from '../type.js';
import type { ICustomDataTag, IIdentifierTag, IPopularimeter } from './FrameParser.js';

/**
 * ID3v2.3/ID3v2.4 tag mappings
 */
const id3v24TagMap: INativeTagMap = {
  // id3v2.3
  TIT2: 'title',
  TPE1: 'artist',
  'TXXX:Artists': 'artists',
  TPE2: 'albumartist',
  TALB: 'album',
  TDRV: 'date', // [ 'date', 'year' ] ToDo: improve 'year' mapping
  /**
   * Original release year
   */
  TORY: 'originalyear',
  TPOS: 'disk',
  TCON: 'genre',
  APIC: 'picture',
  TCOM: 'composer',
  USLT: 'lyrics',
  TSOA: 'albumsort',
  TSOT: 'titlesort',
  TOAL: 'originalalbum',
  TSOP: 'artistsort',
  TSO2: 'albumartistsort',
  TSOC: 'composersort',
  TEXT: 'lyricist',
  'TXXX:Writer': 'writer',
  TPE3: 'conductor',
  // 'IPLS:instrument': 'performer:instrument', // ToDo
  TPE4: 'remixer',
  'IPLS:arranger': 'arranger',
  'IPLS:engineer': 'engineer',
  'IPLS:producer': 'producer',
  'IPLS:DJ-mix': 'djmixer',
  'IPLS:mix': 'mixer',
  TPUB: 'label',
  TIT1: 'grouping',
  TIT3: 'subtitle',
  TRCK: 'track',
  TCMP: 'compilation',
  POPM: 'rating',
  TBPM: 'bpm',
  TMED: 'media',
  'TXXX:CATALOGNUMBER': 'catalognumber',
  'TXXX:MusicBrainz Album Status': 'releasestatus',
  'TXXX:MusicBrainz Album Type': 'releasetype',
  /**
   * Release country as documented: https://picard.musicbrainz.org/docs/mappings/#cite_note-0
   */
  'TXXX:MusicBrainz Album Release Country': 'releasecountry',
  /**
   * Release country as implemented // ToDo: report
   */
  'TXXX:RELEASECOUNTRY': 'releasecountry',
  'TXXX:SCRIPT': 'script',
  TLAN: 'language',
  TCOP: 'copyright',
  WCOP: 'license',
  TENC: 'encodedby',
  TSSE: 'encodersettings',
  'TXXX:BARCODE': 'barcode',
  'TXXX:ISRC': 'isrc',
  TSRC: 'isrc',
  'TXXX:ASIN': 'asin',
  'TXXX:originalyear': 'originalyear',
  'UFID:http://musicbrainz.org': 'musicbrainz_recordingid',
  'TXXX:MusicBrainz Release Track Id': 'musicbrainz_trackid',
  'TXXX:MusicBrainz Album Id': 'musicbrainz_albumid',
  'TXXX:MusicBrainz Artist Id': 'musicbrainz_artistid',
  'TXXX:MusicBrainz Album Artist Id': 'musicbrainz_albumartistid',
  'TXXX:MusicBrainz Release Group Id': 'musicbrainz_releasegroupid',
  'TXXX:MusicBrainz Work Id': 'musicbrainz_workid',
  'TXXX:MusicBrainz TRM Id': 'musicbrainz_trmid',
  'TXXX:MusicBrainz Disc Id': 'musicbrainz_discid',
  'TXXX:ACOUSTID_ID': 'acoustid_id',
  'TXXX:Acoustid Id': 'acoustid_id',
  'TXXX:Acoustid Fingerprint': 'acoustid_fingerprint',
  'TXXX:MusicIP PUID': 'musicip_puid',
  'TXXX:MusicMagic Fingerprint': 'musicip_fingerprint',
  WOAR: 'website',

  // id3v2.4
  // ToDo: In same sequence as defined at http://id3.org/id3v2.4.0-frames
  TDRC: 'date', // date YYYY-MM-DD
  TYER: 'year',
  TDOR: 'originaldate',
  // 'TMCL:instrument': 'performer:instrument',
  'TIPL:arranger': 'arranger',
  'TIPL:engineer': 'engineer',
  'TIPL:producer': 'producer',
  'TIPL:DJ-mix': 'djmixer',
  'TIPL:mix': 'mixer',
  TMOO: 'mood',

  // additional mappings:
  SYLT: 'lyrics',

  TSST: 'discsubtitle',
  TKEY: 'key',
  COMM: 'comment',
  TOPE: 'originalartist',

  // Windows Media Player
  'PRIV:AverageLevel': 'averageLevel',
  'PRIV:PeakLevel': 'peakLevel',

  // Discogs
  'TXXX:DISCOGS_ARTIST_ID': 'discogs_artist_id',
  'TXXX:DISCOGS_ARTISTS': 'artists',
  'TXXX:DISCOGS_ARTIST_NAME': 'artists',
  'TXXX:DISCOGS_ALBUM_ARTISTS': 'albumartist',
  'TXXX:DISCOGS_CATALOG': 'catalognumber',
  'TXXX:DISCOGS_COUNTRY': 'releasecountry',
  'TXXX:DISCOGS_DATE': 'originaldate',
  'TXXX:DISCOGS_LABEL': 'label',
  'TXXX:DISCOGS_LABEL_ID': 'discogs_label_id',
  'TXXX:DISCOGS_MASTER_RELEASE_ID': 'discogs_master_release_id',
  'TXXX:DISCOGS_RATING': 'discogs_rating',
  'TXXX:DISCOGS_RELEASED': 'date',
  'TXXX:DISCOGS_RELEASE_ID': 'discogs_release_id',
  'TXXX:DISCOGS_VOTES': 'discogs_votes',

  'TXXX:CATALOGID': 'catalognumber',
  'TXXX:STYLE': 'genre',

  'TXXX:REPLAYGAIN_TRACK_PEAK': 'replaygain_track_peak',
  'TXXX:REPLAYGAIN_TRACK_GAIN': 'replaygain_track_gain',
  'TXXX:REPLAYGAIN_ALBUM_PEAK': 'replaygain_album_peak',
  'TXXX:REPLAYGAIN_ALBUM_GAIN': 'replaygain_album_gain',
  'TXXX:MP3GAIN_MINMAX': 'replaygain_track_minmax',
  'TXXX:MP3GAIN_ALBUM_MINMAX': 'replaygain_album_minmax',
  'TXXX:MP3GAIN_UNDO': 'replaygain_undo',

  MVNM: 'movement',
  MVIN: 'movementIndex',
  PCST: 'podcast',
  TCAT: 'category',
  TDES: 'description',
  TDRL: 'releasedate',
  TGID: 'podcastId',
  TKWD: 'keywords',
  WFED: 'podcasturl',

  GRP1: 'grouping'
};

export class ID3v24TagMapper extends CaseInsensitiveTagMap {

  public static toRating(popm: IPopularimeter): IRating {

    return {
      source: popm.email,
      rating: popm.rating > 0 ? (popm.rating - 1) / 254 * CommonTagMapper.maxRatingScore : undefined
    };
  }

  public constructor() {
    super(['ID3v2.3', 'ID3v2.4'], id3v24TagMap);
  }

  /**
   * Handle post mapping exceptions / correction
   * @param tag to post map
   * @param warnings Wil be used to register (collect) warnings
   */
  protected postMap(tag: ITag, warnings: INativeMetadataCollector): void {

    switch (tag.id) {

      case 'UFID': {
        // decode MusicBrainz Recording Id
        const idTag= tag.value as IIdentifierTag;
        if (idTag.owner_identifier === 'http://musicbrainz.org') {
          tag.id += `:${idTag.owner_identifier}`;
          tag.value = decodeString(idTag.identifier, 'latin1'); // latin1 == iso-8859-1
        }
      }
        break;

      case 'PRIV': {
        const customTag = tag.value as ICustomDataTag;
        switch(customTag.owner_identifier) {
          // decode Windows Media Player
          case 'AverageLevel':
          case 'PeakValue':
            tag.id += `:${customTag.owner_identifier}`;
            tag.value = customTag.data.length === 4 ? UINT32_LE.get(customTag.data, 0) : null;
            if (tag.value === null) {
              warnings.addWarning('Failed to parse PRIV:PeakValue');
            }
            break;
          default:
            warnings.addWarning(`Unknown PRIV owner-identifier: ${customTag.data}`);
        }
      }
        break;

      case 'POPM':
        tag.value = ID3v24TagMapper.toRating(tag.value as IPopularimeter);
        break;

      default:
        break;
    }
  }
}
