# -*- coding: ascii -*-

###########################################################################
# clive, video extraction utility
# Copyright (C) 2007-2008 Toni Gundogdu
#
# clive is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 0.1.2-1307 USA
###########################################################################

## The classes for parsing command line options

import os
import platform
import random
from optparse import OptionParser, OptionGroup

import clive as _clive
from clive.config import ConfigParser
try:
    import clive.urlgrabber as urlg
except ImportError, err:
    raise SystemExit('error: %s' % err)
try:
    from clive.feedparser import feedparser as fparser
except ImportError, err:
    raise SystemExit('error: %s' % err)

try:
    import sqlite3
    _sqlite3_found = 1
except ImportError:
    _sqlite3_found = 0

__all__ = ['Options']


## The class that parses command line options
class Options:

    ## Constructor
    def __init__(self):
        self._ver = '%s (%s; py/%s; urlg/%s; fparser/%s' % (
            _clive.__version_str__,
            '%s/%s' % (platform.system().lower(), \
            platform.release().split('-',1)[0]),
            platform.python_version(),
            urlg.__version__,
            fparser.__version__)
        if len(_clive.__newt_version__) > 0:
            self._ver += '; newt/%s' % _clive.__newt_version__
        self._ver += ')'
        self._warnings = []

    ## Parses options
    def parse(self):
        self._parse_opts()
        return (self.opts, self.args)

    ## Print some options to stdout
    def show(self, say):
        onoff = ['off','yes']
        reencode = self.opts.reencode_format
        if not reencode: reencode = 'off'
        play = self.opts.play_format
        if not play: play = 'off'
        say('%s\ncache:%s; paste:%s; play:%s; encode:%s; rate:%s; exist:%s' % (
            self._ver,
            (onoff[self.opts.enable_cache]),
            (onoff[self.opts.enable_xclip_paste]),
            play, reencode,
            (['off','%.0fK/s' % (self.opts.http_throttle/1024)] \
                [self.opts.http_throttle > 0]),
            self.opts.output_exists.lower())
        )

        for w in self._warnings:
            say(w)

    def _parse_opts(self):
        self._conf_parser = ConfigParser()
        self._config = self._conf_parser.load()

        p = OptionParser(
            usage='%prog [OPTION].. [URL]..',
            description=_clive.__doc__,
            version=self._ver+'\n'+_clive.__notice__)

        gout = OptionGroup(p, 'output', None)
        gconn = OptionGroup(p, 'connection', None)
        gplay = OptionGroup(p, 'playback', None)
        genc = OptionGroup(p, 're-encode', None)
        gclip = OptionGroup(p, 'clipboard', None)

        p.add_option_group(gout)
        p.add_option_group(gconn)
        p.add_option_group(gplay)
        p.add_option_group(genc)
        p.add_option_group(gclip)

        confp = self._conf_parser

        lookup = {
            # Top group
            'enable_extract':{
                'group':p,
                'l_opt':'--no-extract',
                's_opt':'-n',
                'action':'store_false',
                'choices':None,
                'metavar':None,
                'desc':'do not actually extract any videos',
                'default':confp.get_default('enable_extract'),
            },
            'emit_csv':{
                'group':p,
                'l_opt':'--emit-csv',
                's_opt':'-e',
                'action':'store_true',
                'choices':None,
                'metavar':None,
                'desc':'do not extract, only print out urls in csv format',
                'default':False,
            },
            'enable_verbose':{
                'group':p,
                'l_opt':'--quiet',
                's_opt':'-q',
                'action':'store_false',
                'choices':None,
                'metavar':None,
                'desc':'be less verbose',
                'default':confp.get_default('enable_verbose'),
            },
            'write_conf':{
                'group':p,
                'l_opt':'--write-conf',
                's_opt':'-w',
                'action':'store_true',
                'choices':None,
                'metavar':None,
                'desc':'write config file with program defaults',
                'default':False,
            },
            'check_updates':{
                'group':p,
                'l_opt':'--check-update',
                's_opt':'-u',
                'action':'store_true',
                'choices':None,
                'metavar':None,
                'desc':'check for new updates',
                'default':False,
            },
            'enable_cache':{
                'group':p,
                'l_opt':'--no-cache',
                's_opt':'-C',
                'action':'store_false',
                'choices':None,
                'metavar':None,
                'desc':'explicitly turn off caching features',
                'default':confp.get_default('enable_cache'),
            },
            'enable_recall':{
                'group':p,
                'l_opt':'--no-recall',
                's_opt':'-R',
                'action':'store_false',
                'choices':None,
                'metavar':None,
                'desc':'explicitly turn off writing to ~/.clive/recall file',
                'default':confp.get_default('enable_recall'),
            },
            'recall':{
                'group':p,
                'l_opt':'--recall',
                's_opt':'-l',
                'action':'store_true',
                'choices':None,
                'metavar':None,
                'desc':'recall last saved url batch from ~/.clive/recall file',
                'default':False,
            },
            'enable_low_quality':{
                'group':p,
                'l_opt':'--low-quality',
                's_opt':'-L',
                'action':'store_true',
                'choices':None,
                'metavar':None,
                'desc':'explicitly extract low quality videos',
                'default':confp.get_default('enable_low_quality'),
            },
            'enable_rss':{
                'group':p,
                'l_opt':'--rss',
                's_opt':'-F',
                'action':'store_true',
                'choices':None,
                'metavar':None,
                'desc':'parse sources for rss/atom feed content',
                'default':False,
            },
            # Output group
            'output_exists':{
                'group':gout,
                'l_opt':'--exist',
                's_opt':'-a',
                'action':'store',
                'choices':['overwrite','rename'],
                'metavar':'A',
                'desc':'action if file exists (overwrite, rename)',
                'default':confp.get_default('output_exists').lower(),
            },
            'output_file':{
                'group':gout,
                'l_opt':'--output',
                's_opt':'-o',
                'action':'store',
                'choices':None,
                'metavar':'F',
                'desc':'write videos to file',
                'default':None,
            },
            'output_savedir':{
                'group':gout,
                'l_opt':'--savedir',
                's_opt':'-s',
                'action':'store',
                'choices':None,
                'metavar':'D',
                'desc':'write videos to directory',
                'default':confp.get_default('output_savedir'),
            },
            'output_mask':{
                'group':gout,
                'l_opt':'--mask',
                's_opt':'-m',
                'action':'store',
                'choices':None,
                'metavar':'M',
                'desc':'use mask for title filtering re.sub call',
                'default':confp.get_default('output_mask'),
            },
            'output_format':{
                'group':gout,
                'l_opt':'--format',
                's_opt':'-f',
                'action':'store',
                'choices':None,
                'metavar':'F',
                'desc':'use format for constructing output filename',
                'default':confp.get_default('output_format'),
            },
            # Connection group
            'http_proxy':{
                'group':gconn,
                'l_opt':'--proxy',
                's_opt':'-y',
                'action':'store',
                'choices':None,
                'metavar':'P',
                'desc':'proxy for http connections',
                'default':confp.get_default('http_proxy'),
            },
            'http_agent':{
                'group':gconn,
                'l_opt':'--agent',
                's_opt':'-g',
                'action':'store',
                'choices':None,
                'metavar':'A',
                'desc':'user-agent string for http connections',
                'default':confp.get_default('http_agent'),
            },
            'http_throttle':{
                'group':gconn,
                'l_opt':'--throttle',
                's_opt':'-t',
                'action':'store',
                'choices':None,
                'metavar':'T',
                'desc':'throttle limit for transfers (kb/s)',
                'default':confp.get_default('http_throttle'),
            },
            'youtube_username':{
                'group':gconn,
                'l_opt':'--youtube-user',
                's_opt':None,
                'action':'store',
                'choices':None,
                'metavar':'U',
                'desc':'youtube log-in username',
                'default':confp.get_default('youtube_username'),
            },
            'youtube_password':{
                'group':gconn,
                'l_opt':'--youtube-pass',
                's_opt':None,
                'action':'store',
                'choices':None,
                'metavar':'P',
                'desc':'youtube log-in password',
                'default':confp.get_default('youtube_password'),
            },
            'dmotion_username':{
                'group':gconn,
                'l_opt':'--dmotion-user',
                's_opt':None,
                'action':'store',
                'choices':None,
                'metavar':'U',
                'desc':'dailymotion log-in username',
                'default':confp.get_default('dmotion_username'),
            },
            'dmotion_password':{
                'group':gconn,
                'l_opt':'--dmotion-pass',
                's_opt':None,
                'action':'store',
                'choices':None,
                'metavar':'P',
                'desc':'dailymotion log-in password',
                'default':confp.get_default('dmotion_password'),
            },
            # Playback options
            'player':{
                'group':gplay,
                'l_opt':'--player',
                's_opt':None,
                'action':'store',
                'choices':None,
                'metavar':'PATH',
                'desc':'path to a player with any options',
                'default':confp.get_default('player'),
            },
            'play_format':{
                'group':gplay,
                'l_opt':'--play',
                's_opt':'-p',
                'action':'store',
                'choices':None,
                'metavar':'F',
                'desc':'play video format',
                'default':confp.get_default('play_format'),
            },
            # Re-encode group
            'ffmpeg':{
                'group':genc,
                'l_opt':'--ffmpeg',
                's_opt':None,
                'action':'store',
                'choices':None,
                'metavar':'PATH',
                'desc':'path to ffmpeg with any options',
                'default':confp.get_default('ffmpeg'),
            },
            'reencode_format':{
                'group':genc,
                'l_opt':'--reencode',
                's_opt':'-r',
                'action':'store',
                'choices':None,
                'metavar':'F',
                'desc':'re-encode videos to format',
                'default':confp.get_default('reencode_format'),
            },
            # Clipboard group
            'xclip':{
                'group':gclip,
                'l_opt':'--xclip',
                's_opt':None,
                'action':'store',
                'choices':None,
                'metavar':'PATH',
                'desc':'path to xclip (include "-o" option in path)',
                'default':confp.get_default('xclip'),
            },
            'enable_xclip_paste':{
                'group':gclip,
                'l_opt':'--paste',
                's_opt':'-x',
                'action':'store_true',
                'choices':None,
                'metavar':None,
                'desc':'paste from clipboard using xclip',
                'default':confp.get_default('enable_xclip_paste'),
            },
        }

        for (opt, data) in sorted(lookup.items()):
            o = lookup[opt]
            o['group'].add_option(
                o['l_opt'],
                o['s_opt'],
                dest=opt,
                action=o['action'],
                choices=o['choices'],
                metavar=o['metavar'],
                default=o['default'],
                help=o['desc']
            )

        (self.opts, self.args) = p.parse_args()

        # Modify: proxy -- use http_proxy
        proxy = os.environ.get('http_proxy')
        if proxy and not self.opts.http_proxy:
            self.opts.http_proxy = proxy

        # Modify: savedir
        w = 'warn: invalid savedir, using workdir'
        if not self.opts.output_savedir or len(self.opts.output_savedir) == 0:
            self.opts.output_savedir = os.getcwd()
            self._warnings.append(w)

        if not os.path.exists(self.opts.output_savedir):
            self.opts.output_savedir = os.getcwd()
            self._warnings.append(w)

        # Modify: user-agent
        if not self.opts.http_agent:
            self.opts.http_agent = self._random_agent()
        if len(self.opts.http_agent) == 0:
            self.opts.http_agent = self._random_agent()
            self._warnings.append('warn: invalid user-agent, using random')

        # Modify: throttle (KB/s -> Bytes/s for urlgrabber)
        if not self.opts.http_throttle: self.opts.http_throttle = 0
        try:
            self.opts.http_throttle = int(self.opts.http_throttle) * 1024
        except ValueError:
            self.opts.http_throttle = 0
            self._warnings.append('warn: invalid throttle, using default')

        # Modify: play, player, ffmpeg and reencode
        if not self.opts.player or len(self.opts.player) == 0:
            if self.opts.play_format:
                self._warnings.append('warn: --player unused, --play ignored')
                self.opts.play_format = None

        if not self.opts.ffmpeg or len(self.opts.ffmpeg) == 0:
            if self.opts.reencode_format:
                self._warnings.append('warn: --ffmpeg unused, ' +
                    '--reencode ignored')
                self.opts.reencode_format = None                    

        if self.opts.play_format and self.opts.play_format != 'src':
            if self.opts.ffmpeg and len(self.opts.ffmpeg) > 0:
                self.opts.reencode_format = self.opts.play_format
            else:
                self.opts.play_format = 'src'
                self.opts.reencode_format = None
                self._warnings.append('warn: --ffmpeg unused, using --play=src')

        if self.opts.player:
            if self.opts.player.find('%i') == -1:
                if self.opts.play_format:
                    self._warnings.append('warn: --player missing "%i", ' +
                        'ignoring --play')
                    if self.opts.play_format != 'src':
                        self.opts.reencode_format = None
                        self._warnings.append('warn: --reencode ignored ' +
                            'because of the above')
                    self.opts.play_format = None

        if self.opts.ffmpeg:
            for s in ['%i','%o']:
                if self.opts.ffmpeg.find(s) == -1:
                    if self.opts.reencode_format:
                        self._warnings.append('warn: --ffmpeg missing "%s", ' \
                            '--reencode ignored' % s)
                        self.opts.reencode_format = None
                        if self.opts.play_format and \
                            self.opts.play_format != 'src':
                            self._warnings.append('warn: using --play=src ' +
                                'instead')
                            self.opts.play_format = 'src'

        # Modify: xclip
        if self.opts.enable_xclip_paste:
            if not self.opts.xclip:
                self.opts.enable_xclip_paste = False
                self._warnings.append('warn: --xclip unused, ignoring --paste')

        # Modify: cache
        if not _sqlite3_found:
            self.opts.enable_cache = False

        # Modify: youtube-*
        if self.opts.youtube_username:
            if not self.opts.youtube_password:
                self.opts.youtube_username= None
                self._warnings.append('warn: --youtube-pass unused, ' +
                    'ignoring --youtube-user')
        if self.opts.youtube_password:
            if not self.opts.youtube_username:
                self.opts.youtube_password = None
                self._warnings.append('warn: --youtube-user unused, ' +
                    'ignoring --youtube-pass')

        # Modify: dmotion-*
        if self.opts.dmotion_username:
            if not self.opts.dmotion_password:
                self.opts.dmotion_username = None
                self._warnings.append('warn: --dmotion-pass unused, ' +
                    'ignoring --dmotion-user')
        if self.opts.dmotion_password:
            if not self.opts.dmotion_username:
                self.opts.dmotion_password = None
                self._warnings.append('warn: --dmotion-user unused, ' +
                    'ignoring --dmotion-pass')

        # Modify: Append "artificially" url_feed attribute to the options
        # The "url_feed" a sequence of RSS/Atom URLs from the command line
        # or the config file.
        setattr(self.opts, 'url_feed', confp.get_default('url_feed'))

    def _random_agent(self, pairs=4, pair_len=2):
        lst = ['clive', 'youtube', 'google', 'motion',
            'video', 'guba', 'meta', 'cafe']
        a = []
        for w in lst:
            i=0; l=pair_len;
            while 1:
                a.append(w[i:l])
                i=l; l+=pair_len;
                if i >= len(w): break
        t=''; l=-1;
        for i in range(pairs):
            while 1:
                j = random.randint(0, len(a)-1)
                if j != l: break
            l=j; t+=a[j];
        ma = random.randint(0,2)
        mi = random.randint(0,9)
        rv = random.randint(0,9)
        return '%s/%d.%d.%d' % (t,ma,mi,rv)

if __name__ == '__main__':
    o = Options()
    opts, args = o.parse()
    print opts
