# -*- coding: ascii -*-

###########################################################################
# clive, video extraction utility
# Copyright (C) 2007-2008 Toni Gundogdu
#
# This file is part of clive.
#
# 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 3 of the License, or
# (at your option) any later version.
#
# clive 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 clive.  If not, see <http://www.gnu.org/licenses/>.
###########################################################################

## The classes for parsing command line options

# Do not cache these imports: they are not used in any other files
import platform
import locale
from optparse import OptionParser, OptionGroup

__all__ = ['Options']

import clive as _clive
from clive.config import ConfigParser
from clive.modules import Modules
from clive.singleton import Singleton

## The class that parses command line options
class Options(Singleton):

    ## Constructor
    def __init__(self):
        (lang, self._encoding) = locale.getdefaultlocale()
        if not self._encoding: self._encoding = 'iso-8859-1'
        m = Modules()
        self._ver = [
            'clive %s%s %s  [%s]\n' % (
                _clive.__version__, _clive.__version_alphanum__,
                _clive.__date__.replace('-',''),
                platform.system()
            ),
            '%s/%s; python/%s; locale/%s; build/%s\n' % (
                platform.system(), platform.release().split('-',1)[0],
                platform.python_version(), self._encoding,
                _clive.__build_date__.replace('-','')
            ),
            'urlgrabber/%s; pysqlite/%s; feedparser/%s; pycrypto/%s; ' \
            'newt/%s\n' % (
                m.check('urlgrabber')['version'],
                m.check('pysqlite')['version'],
                m.check('feedparser')['version'],
                m.check('pycrypto')['version'],
                m.check('newt')['version']
            ),
        ]
        self._warnings = []

    ## Parses options
    def parse(self):
        self._parse_opts()

    ## Print some options to stdout
    def show(self, say):
        say(self._ver[0])
        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=''.join(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, 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 template ~/.clive/config file',
                '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 all caching',
                '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',
                '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 batch from ~/.clive/recall',
                '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':'read and select video links from rss/atom feeds',
                'default':False,
            },
            'enable_scan':{
                'group':p,
                'l_opt':'--scan',
                's_opt':'-S',
                'action':'store_true',
                'choices':None,
                'metavar':None,
                'desc':'scan and select video links from source URLs',
                'default':False,
            },
            'enable_cache_browse':{
                'group':p,
                'l_opt':'--cache',
                's_opt':'-c',
                'action':'store_true',
                'choices':None,
                'metavar':None,
                'desc':'browse and re-extract from cache',
                'default':False,
            },
            'clear_cache':{
                'group':p,
                'l_opt':'--clear-cache',
                's_opt':'-d',
                'action':'store_true',
                'choices':None,
                'metavar':None,
                'desc':'clear cache data from ~/.clive/cache',
                'default':False,
            },
            'clear_last':{
                'group':p,
                'l_opt':'--clear-last',
                's_opt':'-D',
                'action':'store_true',
                'choices':None,
                'metavar':None,
                'desc':'clear last batch data from ~/.clive/recall',
                'default':False,
            },
            'enable_confirm':{
                'group':p,
                'l_opt':'--no-confirm',
                's_opt':'-Q',
                'action':'store_false',
                'choices':None,
                'metavar':None,
                'desc':'do not prompt for approval before each action',
                'default':True,
            },
            # 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_filter':{
                'group':gout,
                'l_opt':'--filter',
                's_opt':'-m',
                'action':'store',
                'choices':None,
                'metavar':'M',
                'desc':'filter title; either regexp (re.sub), "custom" or "no"',
                'default':confp.get_default('output_filter'),
            },
            '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'),
            },
            'enable_proxy':{
                'group':gconn,
                'l_opt':'--no-proxy',
                's_opt':'-N',
                'action':'store_false',
                'choices':None,
                'metavar':None,
                'desc':'disable use of http proxy altogether',
                'default':True,
            },
            '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_login':{
                'group':gconn,
                'l_opt':'--youtube',
                's_opt':None,
                'action':'store',
                'choices':None,
                'metavar':'LOGINFO',
                'desc':'youtube login in format "username:password"',
                'default':confp.get_default('youtube_login'),
            },
            'dmotion_login':{
                'group':gconn,
                'l_opt':'--dmotion',
                's_opt':None,
                'action':'store',
                'choices':None,
                'metavar':'LOGINFO',
                'desc':'dailymotion login in format "username:password"',
                'default':confp.get_default('dmotion_login'),
            },
            'enable_login':{
                'group':gconn,
                'l_opt':'--no-login',
                's_opt':'-G',
                'action':'store_false',
                'choices':None,
                'metavar':None,
                'desc':'disable logging in altogether',
                'default':True,
            },
            'passwd_passphrase':{
                'group':gconn,
                'l_opt':'--passwd',
                's_opt':'-P',
                'action':'store',
                'choices':None,
                'metavar':'P',
                'desc':'use passphrase for ~/.clive/passwd w/o prompting',
                'default':None,
            },
            # 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',
                '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()

        os = Modules().getinst('os')

        # Modify: proxy -- use http_proxy
        proxy = os.environ.get('http_proxy')
        if proxy and not self._opts.http_proxy:
            self._opts.http_proxy = proxy
        if self._opts.http_proxy and not self._opts.enable_proxy:
            self._opts.http_proxy = None # Disable proxy altogether
            try:
                del os.environ['http_proxy'] # Otherwise urlgrabber will use it
            except KeyError:
                pass

        # 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 '%i' not in self._opts.player:
                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 s not in self._opts.ffmpeg:
                    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')
            else:                
                if '-o' not in self._opts.xclip:
                    self._opts.xclip += ' -o'
                    self._warnings.append('warn: --xclip missing "-o", ' +
                        'appending it')

        mods = Modules()

        # Modify: cache
        if not mods.check('pysqlite')['installed']:
            self._opts.enable_cache = False

        # Modify: cache browser
        if self._opts.enable_cache_browse:
            m = mods.check('pysqlite')
            if not m['installed']:
                raise SystemExit('error: %s' % m['error'])

        # Modify: scan, rss, cache (newt)
        if self._opts.enable_scan \
        or self._opts.enable_rss \
        or self._opts.enable_cache_browse:
            m = mods.check('newt')
            if not m['installed']:
                raise SystemExit('error: %s' % m['error'])

        # Modify: rss
        if self._opts.enable_rss:
            m = mods.check('feedparser')
            if not m['installed']:
                raise SystemExit('error: %s' % m['error'])

        # Modify: youtube login
        if self._opts.youtube_login:
            if not self._opts.enable_login: # Turn it off
                self._opts.youtube_login = None
            else: # Do a basic string format check
                if not ':' in self._opts.youtube_login:
                    self._opts.youtube_login = None
                    self._warnings.append('warn: ignored --youtube, ' +
                        'login info needs to be in "username:password" format')

        # Modify: dmotion login
        if self._opts.dmotion_login:
            if not self._opts.enable_login: # Turn it off
                self._opts.dmotion_login = None
            else: # Do a basic string format check
                if not ':' in self._opts.dmotion_login:
                    self._opts.dmotion_login = None
                    self._warnings.append('warn: ignored --dmotion, ' +
                        'login info needs to be in "username:password" format')

        # Check for default locale settings which are used for string
        # conversions.
        setattr(self._opts, 'encoding', self._encoding) # Add artificially

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

    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;
        random = Modules().getinst('random')
        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)
