//
//  Track.swift
//  Tag for iTunes
//
//  Created by Kim Wittenburg on 30.05.15.
//  Copyright (c) 2015 Kim Wittenburg. All rights reserved.
//

import Cocoa

/// Represents a track of the
/// [Search API](https://www.apple.com/itunes/affiliates/resources/documentation/itunes-store-web-service-search-api.html).
public class Track: iTunesType {

    // MARK: Types
    
    public enum Tag: String {
        case Name = "name", Artist = "artist", Year = "year", TrackNumber = "trackNumber", TrackCount = "trackCount", DiscNumber = "discNumber", DiscCount = "discCount", Genre = "genre", AlbumName = "album", AlbumArtist = "albumArtist"
        case SortName = "sortName", SortArtist = "sortArtist", SortAlbumName = "sortAlbum", SortAlbumArtist = "sortAlbumArtist", Composer = "composer", SortComposer = "sortComposer", Comment = "comment"
        
        /// Returns `true` for tags that are returned from the
        /// [Search API](https://www.apple.com/itunes/affiliates/resources/documentation/itunes-store-web-service-search-api.html).
        public var isReturnedBySearchAPI: Bool {
            switch self {
            case .Name, .Artist, .Year, .TrackNumber, .TrackCount, .DiscNumber, .DiscCount, .Genre, .AlbumName, .AlbumArtist:
                return true
            default:
                return false
            }
        }
        
        public var clearable: Bool {
            switch self {
            case .Year, .TrackNumber, .TrackCount, .DiscNumber, .DiscCount:
                return false
            default:
                return true
            }
        }

        /// Returns a string identifying the respective tag that can be displayed
        /// to the user.
        public var localizedName: String {
            return NSLocalizedString("Tag: \(self.rawValue)", comment: "")
        }
        
        /// Returns an array of all tags.
        public static var allTags: [Tag] {
            return [.Name, .Artist, .Year, .TrackNumber, .TrackCount, .DiscNumber, .DiscCount, .Genre, .AlbumName, .AlbumArtist, .SortName, .SortArtist, .SortAlbumName, .SortAlbumArtist, .Composer, .SortComposer, .Comment]
        }

    }
    
    // MARK: Properties
    
    public let id: iTunesId
    
    public let name: String
    
    public let censoredName: String
    
    public let artistName: String
    
    public let releaseDate: NSDate
    
    public let trackNumber: Int
    
    public let trackCount: Int
    
    public let discNumber: Int
    
    public let discCount: Int
    
    public let genre: String
    
    public internal(set) weak var album: Album!
    
    /// These tracks will be changed, if `save()` is called.
    public var associatedTracks = [iTunesTrack]()
    
    // MARK: Initializers
    
    public required init(data: [iTunesAPI.Field: AnyObject]) {
        id = data[.TrackId] as! UInt
        name = data[.TrackName] as! String
        censoredName = data[.TrackCensoredName] as! String
        artistName = data[.ArtistName] as! String
        releaseDate = iTunesAPI.sharedDateFormatter.dateFromString(data[.ReleaseDate] as! String)!
        trackNumber = data[.TrackNumber] as! Int
        trackCount = data[.TrackCount] as! Int
        discNumber = data[.DiscNumber] as! Int
        discCount = data[.DiscCount] as! Int
        genre = data[.PrimaryGenreName] as! String
    }
    
    public static var requiredFields: [iTunesAPI.Field] {
        return [.TrackId, .TrackName, .TrackCensoredName, .ArtistName, .ReleaseDate, .TrackNumber, .TrackCount, .DiscNumber, .DiscCount, .PrimaryGenreName]
    }
    
    // MARK: Methods
    
    /// Saves the track. This means that its properties are applied to every
    /// `iTunesTrack` in its `associatedTracks`.
    ///
    /// This method respects the user's preferences (See `Preferences` class).
    public func save() {
        saveToTracks(associatedTracks)
    }
    
    /// Applies the receiver's properties to the specified tracks.
    ///
    /// This method respects the user's preferences (See `Preferences` class).
    public func saveToTracks(tracks: [iTunesTrack]) {
        for track in tracks {
            saveToTrack(track)
        }
    }
    
    /// Applies the receiver's properties to the specified `track`.
    ///
    /// This method respects the user's preferences (See `Preferences` class).
    public func saveToTrack(track: iTunesTrack) {
        let components = NSCalendar.currentCalendar().components(.Year, fromDate: releaseDate)
        let trackName = Preferences.sharedPreferences.useCensoredNames ? censoredName : name
        saveTag(.Name, toTrack: track, value: trackName)
        saveTag(.Artist, toTrack: track, value: artistName)
        saveTag(.Year, toTrack: track, value: components.year)
        saveTag(.TrackNumber, toTrack: track, value: trackNumber)
        saveTag(.TrackCount, toTrack: track, value: trackCount)
        saveTag(.DiscNumber, toTrack: track, value: discNumber)
        saveTag(.DiscCount, toTrack: track, value: discCount)
        saveTag(.Genre, toTrack: track, value: genre)
        let albumName = Preferences.sharedPreferences.useCensoredNames ? album.censoredName : album.name
        saveTag(.AlbumName, toTrack: track, value: albumName)
        saveTag(.AlbumArtist, toTrack: track, value: album.artistName)
        saveTag(.SortName, toTrack: track, value: nil)
        saveTag(.SortArtist, toTrack: track, value: nil)
        saveTag(.SortAlbumName, toTrack: track, value: nil)
        saveTag(.SortAlbumArtist, toTrack: track, value: nil)
        saveTag(.Composer, toTrack: track, value: nil)
        saveTag(.SortComposer, toTrack: track, value: nil)
        saveTag(.Comment, toTrack: track, value: nil)
        
        // TODO: Deal with artworks
//        if Preferences.sharedPreferences.clearArtworks {
//            track.artworks().removeAllObjects()
//        }
    }
    
    private func saveTag(tag: Tag, toTrack track: iTunesTrack, value: AnyObject?) {
        switch Preferences.sharedPreferences.tagSavingBehaviors[tag]! {
        case .Save:
            track.setValue(value, forKey: tag.rawValue)
        case .Clear:
            track.setValue("", forKey: tag.rawValue)
        case .Ignore:
            break
        }
    }
    
    /// Returns `true` if all `associatedTrack`s contain the same values as the
    /// `Track` instance.
    public var saved: Bool {
        let components = NSCalendar.currentCalendar().components(.Year, fromDate: releaseDate)
        for track in associatedTracks {
            let trackName = Preferences.sharedPreferences.useCensoredNames ? censoredName : name
            let albumName = Preferences.sharedPreferences.useCensoredNames ? album.censoredName : album.name
            let options = Preferences.sharedPreferences.caseSensitive ? [] : NSStringCompareOptions.CaseInsensitiveSearch
            guard track.name.compare(trackName, options: options, range: nil, locale: nil) == .OrderedSame else {
                return false
            }
            guard track.album.compare(albumName, options: options, range: nil, locale: nil) == .OrderedSame else {
                return false
            }
            guard track.artist == artistName && track.year == components.year && track.trackNumber == trackNumber && track.trackCount == trackCount && track.discNumber == discNumber && track.discCount == discCount && track.genre == genre && track.albumArtist == album.artistName && track.composer == "" else {
                return false
            }
        }
        return true
    }
    
}

extension Track: Hashable {
    
    public var hashValue: Int {
        return Int(id)
    }
    
}

public func ==(lhs: Track, rhs: Track) -> Bool {
    return lhs.id == rhs.id
}
