#!/usr/bin/env python

import os
import fnmatch
import shutil

from soundforest import normalized
from soundforest.cli import Script, ScriptCommand, ScriptError
from soundforest.playlist import m3uPlaylist, m3uPlaylistDirectory, PlaylistError

from pytunes import iTunesError
from pytunes.client import iTunes, iTunesError

DEFAULT_DIRECTORY = os.path.expanduser('~/Music/Playlists')


class PlaylistProcessorCommand(ScriptCommand):
    def setup(self, args):
        self.client = iTunes()
        self.playlists = []

        if 'playlists' in args and args.playlists:
            for playlist in self.client.playlists:
                path = playlist.path
                for match in args.playlists:
                    if fnmatch.fnmatch(playlist.path,path):
                        self.playlists.append(playlist)
            if 'smart_playlists' in args and args.smart_playlists:
                for playlist in self.client.smart_playlists:
                    path = playlist.path
                    for match in args.playlists:
                        if fnmatch.fnmatch(playlist.path,path):
                            self.playlists.append(playlist)
        else:
            self.playlists.extend(self.client.playlists)
            if 'smart_playlists' in args and args.smart_playlists:
                self.playlists.extend(self.client.smart_playlists)


class ListCommand(PlaylistProcessorCommand):
    def run(self, args):
        super(ListCommand, self).setup(args)

        if not args.playlists:
            for playlist in self.playlists:
                self.script.message(playlist.path)

        else:
            for playlist in self.playlists:

                years = {}
                for track in playlist:
                    if args.yearly:
                        if track.year not in years:
                            years[track.year] = []

                        years[track.year].append(track)

                    elif args.format:
                        try:
                            self.script.message(args.format % track)
                        except KeyError as e:
                            self.script.exit(1,'Error formatting track {0}: {1}'.format(track.path, e))

                    else:
                        self.script.message(track)

                if args.yearly:
                    for year in sorted(years.keys()):
                        years[year].sort(lambda x,y: cmp(x.artist,y.artist))

                        first = True
                        for track in years[year]:

                            if args.format:
                                try:
                                    self.script.message(args.format % track)
                                except KeyError as e:
                                    self.script.exit(1,'Error formatting track {0}: {1}'.format(track.path, e))

                            elif first:
                                self.script.message('%(year)4s\t%(artist)s\t%(name)s' % track)
                                first = False
                            else:
                                self.script.message('\t%(artist)s\t%(name)s' % track)


class ExportFilesCommand(PlaylistProcessorCommand):
    def run(self, args):
        super(ExportFilesCommand, self).setup(args)

        for playlist in self.playlists:
            self.script.log.debug('Processing: {0}'.format(playlist))

            path = os.path.join(args.directory, playlist.path)

            if not os.path.isdir(path):
                try:
                    os.makedirs(path)
                except OSError as e:
                    self.script.exit(1, 'Error creating playlist folder {0}: {1}'.format(path, e))

            tracks = []
            if args.yearly:
                years = {}
                for track in playlist:
                    if track.year not in years:
                        years[track.year] = []
                    years[track.year].append(track)

                for year in sorted(years.keys()):
                    years[year].sort(lambda x,y: cmp(x.artist,y.artist))

                    first = True
                    for track in years[year]:
                        name = '{0} - {1}.{2}'.format(track.artist, track.name, track.extension)
                        filename = os.path.join('{0}'.format(year,name.replace(os.sep,'')))
                        tracks.append( (filename, track) )

            elif args.format:
                index = 1
                for track in playlist:
                    try:
                        name = '{0}'.format(args.format % track)
                        filename = '{0:03d} {1}.{2}'.format(
                            index,
                            os.path.join(path, name.replace(os.sep,'')),
                            track.extension
                        )
                        tracks.append( (filename, track) )
                    except KeyError as e:
                        self.script.exit(1,'Error formatting track {0}: {1}'.format(track.path, e))
                    index += 1

            else:
                index = 1
                for track in playlist:
                    filename = '{0:03d} {1}'.format(index, track.path)
                    tracks.append( (filename, track) )

            for filename,original in tracks:
                filename = os.path.join(path, filename)
                self.script.log.debug('Processing {0}'.format(filename))
                if os.path.isfile(filename):
                    continue

                file_dir = os.path.dirname(filename)
                if not os.path.isdir(file_dir):
                    try:
                        os.makedirs(file_dir)
                    except OSError as e:
                        self.script.exit(1, 'Error creating directory {0}: {1}'.format(filename, e) )

                try:
                    self.script.message('Writing {0}'.format(filename))
                    shutil.copyfile(original.path, filename)
                except OSError as e:
                    self.script.exit(1, 'Error writing file {0}: {1}'.format(filename, e) )


class ExportCommand(PlaylistProcessorCommand):
    def run(self, args):
        super(ExportCommand, self).setup(args)

        if not os.path.isdir(args.directory):
            try:
                os.makedirs(args.directory)
            except OSError as e:
                self.exit(1, 'Error creating directory {0}: {1}'.format(args.directory, e))
            except IOError as e:
                self.exit(1, 'Error creating directory {0}: {1}'.format(args.directory, e))

        for playlist in self.playlists:

            try:
                folder = os.path.dirname(os.path.join(args.directory, playlist.path))
            except AttributeError:
                folder = None

            m3u = m3uPlaylist(playlist.name,folder=folder)
            for track in playlist:
                m3u.append(track.path)

            if args.ignore_empty and len(m3u)==0:
                self.script.log.debug('Ignore empty playlist: {0}'.format(m3u))
                continue

            try:
                m3u.write()
            except PlaylistError as e:
                self.script.log.info('Error writing playlist {0}: {1}'.format(m3u.path, e))
                continue

            self.script.log.info('Exported: {0} {1:d} songs'.format(m3u.path, len(m3u)))


class ImportCommand(PlaylistProcessorCommand):
    def run(self, args):
        super(ImportCommand, self).setup(args)

        if not os.path.isdir(args.directory):
            self.script.exit(1,'No such directory: {0}'.format(args.directory))

        for playlist in m3uPlaylistDirectory(path=args.directory):
            playlist.read()
            ipl = iTunesPlaylist(playlist.name)

            for track  in playlist:
                ipl.add(track)

            self.script.log.info('Imported: {0} {1} songs'.format(playlist.path, len(playlist)))


class CreateCommand(PlaylistProcessorCommand):
    def run(self, args):
        super(CreateCommand, self).setup(args)

        for name in args.names:
            self.client.create_playlist(name)


class RemoveCommand(PlaylistProcessorCommand):
    def run(self, args):
        super(RemoveCommand, self).setup(args)

        for name in args.names:
            try:
                self.client.delete_playlist(name)
            except iTunesError as e:
                self.error('Error removing playlist {0}: {1}'.format(name, e))


script = Script('itunes-playlist','Import and export itunes playlists')

c = script.add_subcommand(ListCommand('list', 'List playlists'))
c.add_argument('-s','--smart-playlists', action='store_true', help='Include smart playlists')
c.add_argument('-y','--yearly', action='store_true', help='Order listed tracks by year')
c.add_argument('-f','--format', help='List formatting string')
c.add_argument('playlists', nargs='*', help='Playlists to process')

c = script.add_subcommand(ExportFilesCommand('export-files', 'Export playlists as files'))
c.add_argument('-s','--smart-playlists', action='store_true', help='Include smart playlists')
c.add_argument('-y','--yearly', action='store_true', help='Order exported tracks by year')
c.add_argument('-f','--format', help='Filename formatting string')
c.add_argument('playlists', nargs='*', help='Playlists to process')

c = script.add_subcommand(ExportCommand('export', 'Export m3u playlists'))
script.add_argument('-D','--directory', default=DEFAULT_DIRECTORY, help='Playlist directory for m3u files')
c.add_argument('-s','--smart-playlists', action='store_true', help='Include smart playlists')
c.add_argument('-E','--ignore-empty', action='store_true', help='Ignore empty playlists')
c.add_argument('playlists', nargs='*', help='Playlists to process')

c = script.add_subcommand(ImportCommand('import', 'Import playlists'))
c.add_argument('-s','--smart-playlists', action='store_true', help='Include smart playlists')
c.add_argument('playlists', nargs='*', help='Playlists to process')

c = script.add_subcommand(CreateCommand('create', 'Create playlists'))
c.add_argument('names', nargs='*', help='Names of playlists to create')

c = script.add_subcommand(RemoveCommand('remove', 'Remove playlists'))
c.add_argument('names', nargs='*', help='Names of playlists to create')

script.run()

