#!/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 = int(progress_interval / progress_time)
        script.log.info('Index {0:d} ({1:d} entries per second)'.format(processed, progress_rate))
        progress_start = time.time()

    try:
        if entry.path is None:
            try:
                client.library.delete(entry)
            except iTunesError as e:
                print(e)
            continue
    except TypeError as e:
        print('Error processing entry {0}: {1}'.format(entry, e))
        continue

    try:
        path = normalized(os.path.realpath(entry.path))
    except AttributeError:
        script.log.info('Removing invalid entry (no path defined)')
        try:
            client.library.delete(entry)
        except iTunesError as e:
            print(e)
        continue

    if path not in tree_paths:
        if not os.path.isfile(path):
            script.log.info('Removing non-existing: "}0}"'.format(path))
            try:
                client.library.delete(entry)
            except iTunesError as e:
                print(e)
        else:
            script.log.info('File outside tree: {0}'.format(path))

    elif path not in itunes_files:
        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))
        try:
            client.library.delete(entry)
        except iTunesError as e:
            print(e)

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 path not in itunes_files:
            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))
