# -*- coding: utf-8 -*-

# Copyright(C) 2010-2012  Christophe Benz, Romain Bignon
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.


from __future__ import print_function

from copy import copy
import getpass
import logging
import subprocess
import sys
import os

from weboob.capabilities import UserError
from weboob.capabilities.account import CapAccount, Account, AccountRegisterError
from weboob.core.backendscfg import BackendAlreadyExists
from weboob.core.modules import ModuleLoadError
from weboob.core.repositories import ModuleInstallError, IProgress
from weboob.exceptions import BrowserUnavailable, BrowserIncorrectPassword, BrowserForbidden, BrowserSSLError, BrowserQuestion
from weboob.tools.value import Value, ValueBool, ValueFloat, ValueInt, ValueBackendPassword
from weboob.tools.misc import to_unicode
from weboob.tools.ordereddict import OrderedDict

from .base import Application, MoreResultsAvailable


__all__ = ['ConsoleApplication', 'BackendNotGiven']


class BackendNotGiven(Exception):
    def __init__(self, id, backends):
        self.id = id
        self.backends = sorted(backends)
        Exception.__init__(self, 'Please specify a backend to use for this argument (%s@backend_name). '
                'Availables: %s.' % (id, ', '.join(name for name, backend in backends)))


class BackendNotFound(Exception):
    pass


class ConsoleProgress(IProgress):
    def __init__(sel
f, app):
        self.app = app

    def progress(self, percent, message):
        self.app.stdout.write('=== [%3.0f%%] %s\n' % (percent*100, message))

    def error(self, message):
        self.app.stderr.write('ERROR: %s\n' % message)

    def prompt(self, message):
        return self.app.ask(message, default=True)


class ConsoleApplication(Appli
cation):
    """
    Base application class for CLI applications.
    """

    CAPS = None

    # shell escape strings
    if sys.platform == 'win32' \
            or not sys.stdout.isatty() \
            or os.getenv('ANSI_COLORS_DISABLED') is not None:
        #workaround to disable bold
        BOLD   = ''
        NC     = ''          # no color
    else:
        BOLD   = '[1m'
        NC     = '[0m'    # no color

    def __init__(self, option_parser=None):
        Application.__init__(self, option_parser)
        self.weboob.callbacks['login'] = self.login_cb
        self.enabled_backends = set()

    def login_cb(self, backend_name, value):
        return self.ask('[%s] %s' % (backend_name,
                        value.label),
                        masked=True,
                        default='',
                        regexp=value.regexp)

    def unload_backends(self, *args, **kwargs):
        unloaded = self.weboob.unload_backends(*args, **kwargs)
        for backend in unloaded.itervalues():
            try:
                self.enabled_backends.remove(backend)
            except KeyError:
                pass
        return unloaded

    def is_module_loadable(self, info):
        return self.CAPS is None or info.has_caps(self.CAPS)

    def load_backends(self, *args, **kwargs):
        if 'errors' in kwargs:
            errors = kwargs['errors']
        else:
            kwargs['errors'] = errors = []
        ret = super(ConsoleApplication, self).load_backends(*args, **kwargs)

        for err in errors:
            print(u'Error(%s): %s' % (err.backend_name, err), file=self.stderr)
            if self.ask('Do you want to reconfigure this backend?', default=True):
                self.edit_backend(err.backend_name)
                self.load_backends(names=[err.backend_name])

        for name, backend in ret.iteritems():
            self.enabled_backends.add(backend)

        self.check_loaded_backends()

        return ret

    def check_loaded_backends(self, default_config=None):
        while len(self.enabled_backends) == 0:
            print('Warning: there is currently no configured backend for %s' % self.APPNAME)
            if not self.stdout.isatty() or not self.ask('Do you want to configure backends?', default=True):
                return False

            self.prompt_create_backends(default_config)

        return True

    def prompt_create_backends(self, default_config=None):
        r = ''
        while r != 'q':
            modules = []
            print('\nAvailable modules:')
            for name, info in sorted(self.weboob.repositories.get_all_modules_info().iteritems()):
                if not self.is_module_loadable(info):
                    continue
                modules.append(name)
                loaded = ' '
                for bi in self.weboob.iter_backends():
                    if bi.NAME == name:
                        if loaded == ' ':
                            loaded = 'X'
                        elif loaded == 'X':
                            loaded = 2
                        else:
                            loaded += 1
                print('%s%d)%s [%s] %s%-15s%s   %s' % (self.BOLD, len(modules), self.NC, loaded,
                                                       self.BOLD, name, self.NC,
                                                       info.description.encode(self.encoding)))
            print('%sa) --all--%s               install all backends' % (self.BOLD, self.NC))
            print('%sq)%s --stop--\n' % (self.BOLD, self.NC))
            r = self.ask('Select a backend to create (q to stop)', regexp='^(\d+|q|a)$')

            if str(r).isdigit():
                i = int(r) - 1
                if i < 0 or i >= len(modules):
                    print('Error: %s is not a valid choice' % r, file=self.stderr)
                    continue
                name = modules[i]
                try:
                    inst = self.add_backend(name, name, default_config)
                    if inst:
                        self.load_backends(names=[inst])
                except (KeyboardInterrupt, EOFError):
                    print('\nAborted.')
            elif r == 'a':
                try:
                    for name in modules:
                        if name in [b.NAME for b in self.weboob.iter_backends()]:
                            continue
                        inst = self.add_backend(name, name, default_config)
                        if inst:
                            self.load_backends(names=[inst])
                except (KeyboardInterrupt, EOFError):
                    print('\nAborted.')
                else:
                    break

        print('Right right!')

    def _handle_options(self):
        self.load_default_backends()

    def load_default_backends(self):
        """
        By default loads all backends.

        Applications can overload this method to restrict backends loaded.
        """
        if len(self.STORAGE) > 0:
            self.load_backends(self.CAPS, storage=self.create_storage())
        else:
            self.load_backends(self.CAPS)

    @classmethod
    def run(klass, args=None):
        try:
            super(ConsoleApplication, klass).run(args)
        except BackendNotFound as e:
            print('Error: Backend "%s" not found.' % e)
            sys.exit(1)

    def do(self, function, *args, **kwargs):
        if 'backends' not in kwargs:
            kwargs['backends'] = self.enabled_backends
        return self.weboob.do(function, *args, **kwargs)

    def parse_id(self, _id, unique_backend=False):
        try:
            _id, backend_name = _id.rsplit('@', 1)
        except ValueError:
            backend_name = None
        backends = [(b.name, b) for b in self.ena