#!/usr/bin/python

import logging
import optparse
import os
import platform
import signal
import sys
import threading
import time
from djrandom_client import daemonize
from djrandom_client import filescan
from djrandom_client import upload
from djrandom_client import utils
from djrandom_client import throttle

# Detect platform and selectively enable inotify/fsevents watchers.
watcher_support = True
if platform.system() == 'Darwin':
    import djrandom_client.osx_watcher as watcher
elif platform.system() == 'Linux':
    import djrandom_client.linux_watcher as watcher
else:
    watcher_support = False

log = logging.getLogger(__name__)


class FullScan(threading.Thread):
    """Do a recursive directory scan when starting."""

    def __init__(self, basedir, queue, base_delay, exit_when_done=False):
        threading.Thread.__init__(self)
        self.basedir = basedir
        self.base_delay = base_delay
        self.queue = queue
        self.exit_when_done = exit_when_done

    def run(self):
        while True:
            delay = self.base_delay
            try:
                filescan.recursive_scan(self.basedir, self.queue)
            except Exception, e:
                log.error('Error in file scan: %s' % e)
                # Retry 30 minutes after an error.
                delay = 1800
            if self.exit_when_done:
                self.queue.put(None)
                break
            time.sleep(delay)


def run_client(server_url, music_dir, api_key, run_once, bwlimit, enable_watcher):
    if bwlimit:
        throttle.set_rate_limit(bwlimit)

    # Warn on this condition, but don't die -- the directory might exist later!
    if not os.path.isdir(music_dir):
        log.error('The music_dir you specified does not exist')

    if enable_watcher and not watcher_support:
        log.warn('inotify/fsevents support not enabled on this platform')
        enable_watcher = False

    upl = upload.Uploader(server_url.rstrip('/'), api_key)
    upl.setDaemon(True)

    # Start the full filesystem scan in the background.
    if enable_watcher:
        scan_delay = 3 * 86400
    else:
        scan_delay = 9600
    scan = FullScan(music_dir, upl.queue, scan_delay, run_once)
    scan.setDaemon(True)

    if not run_once:
        # Run at a lower priority.
        os.nice(10)
        if os.path.exists('/usr/bin/ionice'):
            # Set 'idle' I/O scheduling class, we won't disturb other programs.
            os.system('/usr/bin/ionice -c 3 -p %d' % os.getpid())

        # Start the live filesystem watcher.
        if enable_watcher:
            wtch = watcher.Watcher(music_dir, upl.queue)

        # Install termination signal handlers.
        def _cleanup(signum, frame):
            if enable_watcher:
                log.info('stopping watcher...')
                wtch.stop()
            log.info('got signal %d, exiting...' % signum)
            upl.stop()
            sys.exit(0)
        signal.signal(signal.SIGINT, _cleanup)
        signal.signal(signal.SIGTERM, _cleanup)

    upl.start()
    scan.start()

    if run_once:
        upl.join()
    else:
        # It turns out that, even if we set up signal handlers in this
        # same main thread, they won't be executed if we're blocking on
        # a mutex (such as 'upl.join()' for example)... so we do this
        # silly idle loop in the main thread just to ensure that we
        # catch SIGTERM and SIGINT.
        while True:
            time.sleep(3600)


def main():
    parser = optparse.OptionParser(usage='%prog [<OPTIONS>]')
    parser.add_option('--api_key',
                      help='Your API key')
    parser.add_option('--once', action='store_true',
                      help='Scan music_dir once and then exit')
    parser.add_option('--music_dir',
                      default='~/Music',
                      help='Path to your music directory')
    parser.add_option('--server_url',
                      default='https://djrandom.incal.net/receiver',
                      help='URL to the API endpoint')
    parser.add_option('--bwlimit', type='int',
                      help='Bandwidth limit (in KBps, default unlimited)')
    parser.add_option('--no_realtime_watch', action='store_true',
                      help='Monitor music_dir in realtime')
    daemonize.add_standard_options(parser)
    utils.read_config_defaults(
        parser, os.path.join(os.getenv('HOME'), '.djrandom.conf'))
    parser.set_default(
        'pidfile', os.path.join(os.getenv('HOME'), '.djrandom.pid'))
    opts, args = parser.parse_args()
    if not opts.api_key:
        parser.error('You must specify an API Key')

    music_dir = os.path.expanduser(opts.music_dir)
    if args:
        parser.error('Too many arguments')

    # Perform a version check.
    if utils.check_version():
        print >>sys.stderr, 'A new release is available! Please update.'

    # Reading from the configuration file will set this variable to
    # a string, convert it back into boolean.
    do_realtime = not opts.no_realtime_watch

    daemonize.daemonize(opts, run_client, 
                        (opts.server_url, music_dir, opts.api_key,
                         opts.once, opts.bwlimit, do_realtime))


if __name__ == '__main__':
    main()