#!/usr/bin/env python
import ConfigParser
import argparse
import cPickle as pickle
import os
import shutil
import sys
import tempfile
import urlparse
from multiprocessing import cpu_count

import tagging
import transcode
import redactedapi
from _version import __version__


def create_description(torrent, flac_dir, format, permalink):
    # Create an example command to document the transcode process.
    cmds = transcode.transcode_commands(format,
                                        transcode.needs_resampling(flac_dir),
                                        transcode.resample_rate(flac_dir),
            'input.flac', 'output' + transcode.encoders[format]['ext'])

    description = [
        'Transcode of [url=%s]%s[/url]' % (permalink, permalink),
        '',
        'Transcode process:',
        '',
        '[code]%s[/code]' % ' | '.join(cmds),
        '',
        'Created using [url=https://redacted.ch/forums.php?action=viewthread&threadid=35524]REDBetter (iw00t fork)[/url]'
        ''
        ]
    return description

def formats_needed(group, torrent, supported_formats):
    same_group = lambda t: t['media'] == torrent['media'] and\
                           t['remasterYear'] == torrent['remasterYear'] and\
                           t['remasterTitle'] == torrent['remasterTitle'] and\
                           t['remasterRecordLabel'] == torrent['remasterRecordLabel'] and\
                           t['remasterCatalogueNumber'] == torrent['remasterCatalogueNumber']

    others = filter(same_group, group['torrents'])
    current_formats = set((t['format'], t['encoding']) for t in others)
    missing_formats = [format for format, details in [(f, redactedapi.formats[f]) for f in supported_formats]\
                           if (details['format'], details['encoding']) not in current_formats]
    allowed_formats = redactedapi.allowed_transcodes(torrent)
    return [format for format in missing_formats if format in allowed_formats]

def border_msg(msg):
    width = 0
    for line in msg.splitlines():
        length = len(line)
        if length > width:
            width = length

    dash = "-" * (width - 1)
    return "+{dash}+\n{msg}\n+{dash}+".format(dash=dash,msg=msg)

def main():
    parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, prog='redactedbetter')
    parser.add_argument('release_urls', nargs='*', help='the URL where the release is located')
    parser.add_argument('-s', '--single', action='store_true', help='only add one format per release (useful for getting unique groups)')
    parser.add_argument('-j', '--threads', type=int, help='number of threads to use when transcoding',
            default=max(cpu_count() - 1, 1))
    parser.add_argument('--config', help='the location of the configuration file', \
            default=os.path.expanduser('~/.redactedbetter/config'))
    parser.add_argument('--cache', help='the location of the cache', \
            default=os.path.expanduser('~/.redactedbetter/cache'))
    parser.add_argument('-U', '--no-upload', action='store_true', help='don\'t upload new torrents (in case you want to do it manually)')
    parser.add_argument('-E', '--no-24bit-edit', action='store_true', help='don\'t try to edit 24-bit torrents mistakenly labeled as 16-bit')
    parser.add_argument('--version', action='version', version='%(prog)s ' + __version__)

    args = parser.parse_args()

    config = ConfigParser.RawConfigParser()
    try:
        open(args.config)
        config.read(args.config)
    except:
        if not os.path.exists(os.path.dirname(args.config)):
            os.makedirs(os.path.dirname(args.config))
        config.add_section('redacted')
        config.set('redacted', 'username', '')
        config.set('redacted', 'password', '')
        config.set('redacted', 'session_cookie', '')
        config.set('redacted', 'data_dir', '')
        config.set('redacted', 'output_dir', '')
        config.set('redacted', 'torrent_dir', '')
        config.set('redacted', 'formats', 'flac, v0, 320')
        config.set('redacted', 'media', ', '.join(redactedapi.lossless_media))
        config.set('redacted', '24bit_behaviour','0')
        config.set('redacted', 'piece_length', '18')
        config.write(open(args.config, 'w'))
        print 'Please edit the configuration file: %s' % args.config
        sys.exit(2)
    finally:
        username = config.get('redacted', 'username')
        password = config.get('redacted', 'password')
        try:
            session_cookie = os.path.expanduser(config.get('redacted', 'session_cookie'))
        except ConfigParser.NoOptionError:
            session_cookie = None
        do_24_bit = config.get('redacted', '24bit_behaviour')
        data_dir = os.path.expanduser(config.get('redacted', 'data_dir'))
        try:
            output_dir = os.path.expanduser(config.get('redacted', 'output_dir'))
        except ConfigParser.NoOptionError:
            output_dir = None
        if not output_dir:
            output_dir = data_dir
        torrent_dir = os.path.expanduser(config.get('redacted', 'torrent_dir'))
        supported_formats = [format.strip().upper() for format in config.get('redacted', 'formats').split(',')]

        try:
            media_config = config.get('redacted', 'media')
            if not media_config:
                supported_media = redactedapi.lossless_media
            else:
                supported_media = set([medium.strip().lower() for medium in media_config.split(',')])
                if not supported_media.issubset(set(redactedapi.lossless_media)):
                    print 'Unsupported media type "%s", edit your configuration' % (supported_media - redactedapi.lossless_media).pop()
                    print 'Supported types are:', ', '.join(redactedapi.lossless_media)
                    sys.exit(2)
        except ConfigParser.NoOptionError:
            supported_media = redactedapi.lossless_media

    upload_torrent = not args.no_upload

    print 'Logging in to RED...'
    api = redactedapi.RedactedAPI(username, password, session_cookie)

    try:
        seen = pickle.load(open(args.cache))
    except:
        seen = set()
        pickle.dump(seen, open(args.cache, 'wb'))

    print 'Searching for transcode candidates...'
    if args.release_urls:
        print 'You supplied one or more release URLs, ignoring your configuration\'s media types.'
        candidates = [(int(query['id']), int(query['torrentid'])) for query in\
                [dict(urlparse.parse_qsl(urlparse.urlparse(url).query)) for url in args.release_urls]]
    else:
        candidates = api.snatched(skip=seen, media=supported_media)

    for groupid, torrentid in candidates:
        group = api.request('torrentgroup', id=groupid)
        if group != None:
            torrent = [t for t in group['torrents'] if t['id'] == torrentid][0]

            artist = "";
            if len(group['group']['musicInfo']['artists']) > 1:
                artist = "Various Artists"
            else:
                artist = group['group']['musicInfo']['artists'][0]['name']

            year = str(torrent['remasterYear'])
            if year == "0":
                year = str(group['group']['year'])

            releaseartist = "Release artist(s): %s" % artist
            releasename   = "Release name     : %s" % redactedapi.unescape(group['group']['name'])
            releaseyear   = "Release year     : %s" % year
            releaseurl    = "Release URL      : %s" % api.release_url(group, torrent)

            print("\n\n")
            print(border_msg(releaseartist.encode("utf-8") + "\n" + releasename.encode("utf-8") + "\n" + releaseyear.encode("utf-8") + "\n" + releaseurl.encode("utf-8")))

            if not torrent['filePath']:
                flac_file = os.path.join(data_dir, redactedapi.unescape(torrent['fileList']).split('{{{')[0])
                if not os.path.exists(flac_file):
                    print "Path not found - skipping: %s" % flac_file
                    continue
                flac_dir = os.path.join(data_dir, "%s (%s) [FLAC]" % (
                redactedapi.unescape(group['group']['name']), group['group']['year']))
                if not os.path.exists(flac_dir):
                    os.makedirs(flac_dir)
                shutil.copy(flac_file, flac_dir)
            else:
                flac_dir = os.path.join(data_dir, redactedapi.unescape(torrent['filePath']))

            flac_dir = flac_dir.encode(sys.getfilesystemencoding())
            if int(do_24_bit):
                try:
                    if transcode.is_24bit(flac_dir) and torrent['encoding'] != '24bit Lossless':
                        # A lot of people are uploading FLACs from Bandcamp without realizing
                        # that they're actually 24 bit files (usually 24/44.1). Since we know for
                        # sure whether the files are 24 bit, we might as well correct the listing
                        # on the site (and get an extra upload in the process).
                        if args.no_24bit_edit:
                            print "Release is actually 24-bit lossless, skipping."
                            continue
                        if int(do_24_bit) == 1:
                            confirmation = raw_input("Mark release as 24bit lossless? y/n: ")
                            if confirmation != 'y':
                                continue
                        print "Marking release as 24bit lossless."
                      #  api.set_24bit(torrent)
                        group = api.request('torrentgroup', id=groupid)
                        torrent = [t for t in group['torrents'] if t['id'] == torrentid][0]
                except Exception as e:
                    print "Error: can't edit 24-bit torrent - skipping: %s" % e
                    continue

            if transcode.is_multichannel(flac_dir):
                print "This is a multichannel release, which is unsupported - skipping"
                continue

            needed = formats_needed(group, torrent, supported_formats)
            print "Formats needed: %s" % ', '.join(needed)

            if needed:
                # Before proceeding, do the basic tag checks on the source
                # files to ensure any uploads won't be reported, but punt
                # on the tracknumber formatting; problems with tracknumber
                # may be fixable when the tags are copied.
                broken_tags = False
                for flac_file in transcode.locate(flac_dir, transcode.ext_matcher('.flac')):
                    (ok, msg) = tagging.check_tags(flac_file, check_tracknumber_format=False)
                    if not ok:
                        print "A FLAC file in this release has unacceptable tags - skipping: %s" % msg
                        print "You might be able to trump it."
                        broken_tags = True
                        break
                if broken_tags:
                    continue

            while os.path.exists(flac_dir) == False:
                print "Path not found: %s" % flac_dir
                alternative_file_path_exists = ""
                while (alternative_file_path_exists.lower() != "y") and (alternative_file_path_exists.lower() != "n"):
                    alternative_file_path_exists = raw_input("Do you wish to provide an alternative file path? (y/n): ")

                if alternative_file_path_exists.lower() == "y":
                    flac_dir = raw_input("Alternative file path: ")
                else:
                    print "Skipping: %s" % flac_dir
                    break

            for format in needed:
                if os.path.exists(flac_dir):
                    print 'Adding format %s...' % format,
                    tmpdir = tempfile.mkdtemp()
                    try:
                        if len(torrent['remasterTitle']) >= 1:
                            basename = artist + " - " + group['group']['name'] + " (" + torrent['remasterTitle'] + ") " + "[" + year + "] (" + torrent['media'] + " - "
                        else:
                            basename = artist + " - " + group['group']['name'] + " [" + year + "] (" + torrent['media'] + " - "

                        transcode_dir = transcode.transcode_release(flac_dir, output_dir, basename, format, max_threads=args.threads)
                        if transcode_dir == False:
                            print "Skipping - some file(s) in this release were incorrectly marked as 24bit."
                            break

                        new_torrent = transcode.make_torrent(transcode_dir, tmpdir, api.tracker, api.passkey, config.get('redacted', 'piece_length'))

                        if upload_torrent:
                            permalink = api.permalink(torrent)
                            description = create_description(torrent, flac_dir, format, permalink)
                            api.upload(group, torrent, new_torrent, format, description)

                        shutil.copy(new_torrent, torrent_dir)
                        print "done!"
                        if args.single: break
                    except Exception as e:
                        print "Error adding format %s: %s" % (format, e)
                    finally:
                        shutil.rmtree(tmpdir)

            seen.add(str(torrentid))
            pickle.dump(seen, open(args.cache, 'wb'))

if __name__ == "__main__":
    main()
