#!/usr/bin/env python
"""
Update itunes library against a music tree from command line

This command expects trees to be configured to soundforest databases.
"""

import os
import time
import stat

from soundforest import normalized, SoundforestError
from soundforest.cli import Script, ScriptError
from soundforest.tree import Tree, Album, Track

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

# How often we report something
PROGRESS_INTERVAL = 1000

script = Script('itunes-update', 'Update itunes library')
script.add_argument('-i', '--itunes-directory', help='iTunes directory')
script.add_argument('-c', '--codec', help='Music library default codec')
script.add_argument('-l', '--music-path', help='Music library path')
script.add_argument('-p', '--position', type=int, help='Start from given position in library')
script.add_argument('-m', '--metadata', action='store_true', help='Update metadata')
args = script.parse_args()

client = iTunes()
script.log.info('Loading iTunes library playlist')
if args.position:
    try:
        client.library.jump(args.position)
    except ValueError:
        script.exit(1,
            'Invalid position: {0} ({1:d} entries)'.format((args.position, len(client.library)))
        )
try:
    tree = iTunesMusicTree(itunes_path=args.itunes_directory, tree_path=args.music_path)
    tree.load()
    tree_paths = tree.realpaths

except iTunesError as e:
    script.exit(1, e)

except SoundforestError as e:
    script.exit(1, e)

processed = 0
progress_interval = PROGRESS_INTERVAL
progress_start = time.time()
start = time.time()
itunes_files = {}
script.log.info('Checking library files against itunes')

for entry in client.library:
    processed += 1
    if processed % progress_interval == 0:
        progress_time = float(time.time()-progress_start)
        progress_rate = float(progress_interval/progress_time)
        script.log.info('Index {0:d} ({1:d} entries per second)'.format(processed, progress_rate))
        progress_start = time.time()

    try:
        path = normalized(os.path.realpath(entry.path))

    except AttributeError:
        script.log.info('Removing invalid entry (no path defined)')
        client.library.delete(entry)
        continue

    if not tree_paths.has_key(path):
        if not os.path.isfile(path):
            script.log.info('Removing non-existing: "}0}"'.format(path))
            client.library.delete(entry)

        else:
            script.log.info('File outside tree: {0}'.format(path))

    elif not itunes_files.has_key(path):
        itunes_files[path] = entry
        if args.metadata:
            mtime = os.stat(path).st_mtime
            if int(entry.modification_date.strftime('%s')) >= mtime:
                continue

            song = Track(entry.path)
            if entry.syncTags(song):
                client.library.__next = entry.index

    else:
        script.log.info('Removing duplicate: {0}'.format(entry.path))
        client.library.delete(entry)

loadtime = float(time.time()-start)
script.log.info('Checked {0:d} files in {1:4.2f} seconds'.format(processed, loadtime))

start = time.time()
processed = 0
script.log.info('Checking itunes against library files')

if args.position is None:

    for path in sorted(tree_paths.keys()):

        if not itunes_files.has_key(path):
            script.log.info('Adding: {0}'.format(path))
            try:
                client.library.add(path)
            except ValueError as e:
                print(e)

        processed += 1
        if processed % 1000 == 0:
            script.log.debug('Processed: {0:d} entries'.format(processed))

loadtime = float(time.time()-start)
script.log.info('Checked {0:d} files in {1:2.2f} seconds'.format(processed, loadtime))
