# -*- 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
###########################################################################

## Standard input related classes for reading and parsing

import sys
import subprocess

try:
    from cStringIO import StringIO
except ImportError:
    import StringIO

from clive.path import ConfigDir 
from clive.opts import Options

__all__ = ['Console']


## The console class for reading and parsing standard input and commands
class Console:

    ## Constructor
    def __init__(self, opts, say):
        self._opts = opts
        self._say = say
        self._cmds = [
        ('set [var] [value]', 'e.g. set proxy http://foo:80', self._on_set),
        ('q_ls [index]', 'list url queue', self._on_ls_url),
        ('q_rm [index]', 'remove url from queue', self._on_rm_url),
        ('q_clr','clear queue', self._on_clear_queue),
        ('q_mv [from] [to]', 'move url in queue', self._on_mv_url),
        ('q_srt', 'sort queue by url', self._on_sort_urls),
        ('q_rev', 'reverse queue order', self._on_reverse_urls),
        ('q_r_file [filename]', 'read urls from file',
            self._on_read_file),
        ('q_w_file [filename] [overwrite]',
        'write urls to file (default: append)',
            self._on_write_file),
        ('q_r_paste', 'read urls from clipboard',
            self._on_paste),
        ('q_r_recall', 'recall last batch',
            self._on_recall),
        ('quit', 'quit', self._on_quit),
        ('help', 'this help', self._on_help)
        ]
	
    ## Starts reading stdin in a loop which can perform as a basic console.
    #
    # Depending on the program options, this method reads from stdin,
    # file (~/.clive/recall) or the X clipboard with ``xclip''.
    def read(self):
        self._urls = []
        pipe = sys.stdin

        if self._opts.enable_xclip_paste:
            pipe = self._paste()
        elif self._opts.recall:
            pipe = self._recall()

        while 1:
            ln = pipe.readline()
            if len(ln) == 0: break

            ln = ln.rstrip('\n')
            if len(ln) == 0: continue

            # Parse
            if self._is_cmd(ln):
                pass
            elif self._is_url(ln):
                if ln not in self._urls:
                    self._urls.append(ln)
                else:
                    self._say('error: unrecognized command (%s), ' \
                        'try "help".' % ln)
        return (self._urls)
	
    def _paste(self):
        pipe = sys.stdin # Default
        if not self._opts.xclip:
            self._say('warn: --xclip unused, cannot ' +
                'read from clipboard')
        else:
            cmd = self._opts.xclip + ' 2>/dev/null'
            pipe = subprocess.Popen(cmd, shell=1,
                stdout=subprocess.PIPE).stdout
        return pipe

    def _recall(self):
        pipe = sys.stdin # Default
        try:
            opts = Options()
            pipe = StringIO(open(ConfigDir().recallfile(), 'r').read())
        except IOError, err:
            raise SystemExit('error: recall failed; file read error (%s)' \
                % ConfigDir().recallfile())
        return pipe

    def _set_var(self, var, val):
        try:
            getattr(self._opts, var)
            setattr(self._opts, var, val)
            self._say('"%s" is now "%s".' % (var, val))
        except AttributeError:
            self._say('error: invalid variable (%s).' % var)
	
    def _get_var(self, var):
        try:
            a = getattr(self._opts, var)
            self._say('"%s" is "%s"' % (var, a))
        except AttributeError:
            self._say('error: invalid variable (%s).' % var)
	
    def _list_vars(self):
        self._say('available variables:')
        for opt in self._opts.__dict__:
            if not opt.startswith('_'):
                self._say('  "%s" (%s)' % (opt, getattr(self._opts, opt)))

    def _is_cmd(self, ln):
        try:
            c = ln.split()[0]
        except IndexError:
            c = ln
        for (cmd, desc, func) in self._cmds:
            if c.lower() == cmd.split()[0]:
                func(ln)
                return 1
        return 0
	
    def _is_url(self, ln):
        if len(ln.split()) == 1:
            if ln.find('.') != -1:
                return 1
        return 0
	
    def _on_set(self, ln):
        s = ln.split()
        if len(s) == 3:
            self._set_var(s[1], s[2])
        elif len(s) == 2:
            self._get_var(s[1])
        else:
            self._list_vars()
	
    def _on_ls_url(self, ln):
        s = ln.split()
        if len(s) == 2:
            try:
                i = int(s[1])
                self._say('%d: %s' % (i, self._urls[i]))
            except IndexError:
                self._say('error: invalid index.')
        else:
            self._say('queue:')
            for (index, url) in enumerate(self._urls):
                self._say('%d: %s' % (index, url))
	
    def _on_rm_url(self, ln):
        s = ln.split()
        if len(s) == 2:
            try:
                i = int(s[1])
                del self._urls[i]
            except IndexError:
                self._say('error: invalid index.')
        else:
            self._say('error: specify index.')
	
    def _on_clear_queue(self, ln):
        self._urls = []
        self._say('cleared.')
	
    def _on_mv_url(self, ln):
        s = ln.split()
        if len(s) == 3:
            try:
                index = int(s[1])
                to = int(s[2])
                self._urls.insert(to, self._urls.pop(index))
                self._say('moved %d to %d.' % (index, to))
            except IndexError:
                self._say('error: invalid index.')
        else:
            self._say('error: specify both "to" and "from" indices.')
	
    def _on_sort_urls(self, ln):
        self._urls.sort()
        self._say('sorted.')
	
    def _on_reverse_urls(self, ln):
        self._urls.reverse()
        self._say('reversed.')

    def _read_file(self, filename):
        try:
            f = open(filename, 'r')
            added = 0

            for ln in f.readlines():
                if len(ln) == 0:
                    break

                ln = ln.rstrip('\n')
                if len(ln) == 0:
                    continue

                if self._is_url(ln):
                    if ln not in self._urls:
                        self._urls.append(ln)
                        added += 1

            self._say('added %d new urls.' % added)
            f.close()

        except IOError, err:
            self._say('error: %s' % repr(err))

    def _on_read_file(self, ln):
        s = ln.split()
        if len(s) == 2:
            self._read_file(s[1])
        else:
            self._say('error: filename not specified.')
	
    def _on_write_file(self, ln):
        s = ln.split()

        if len(s) >= 2:
            try:
                mode = 'a'

                if len(s) == 3:
                    if s[2] == 'overwrite': m = 'w'

                f = open(s[1], mode) 
                for url in self._urls:
                    f.write(url + '\n')
                self._say('%d urls written.' % len(self._urls))
                f.close()
            except IOError, err:
                self._say('error: %s' % repr(err))
        else:
            self._say('error: filename not specified.')
	
    def _on_paste(self, ln):
        pipe = self._paste()
        if pipe == sys.stdin:
            return

        added = 0
        while 1:
            ln = pipe.readline()
            if len(ln) == 0: break

            ln = ln.rstrip('\n')
            if len(ln) == 0: continue

            if self._is_url(ln):
                if ln not in self._urls:
                    self._urls.append(ln)
                    added += 1

        self._say('added %d new urls.' % added)

    def _on_recall(self, ln):
        self._read_file(ConfigDir().recallfile())

    def _on_quit(self, ln):
        raise SystemExit

    def _on_help(self, ln):
        self._say('command list:')
        for (cmd, desc, func) in self._cmds:
            self._say('  "%s" %s' % (cmd, desc))
        self._say('NOTE: any input that contains "." will be added ' +
            'to the URL queue')


