# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.



from __future__ import absolute_import

import os
import sys
from optparse import BadOptionError, Option, OptionParser
import re

from six import print_

import ccmlib
from ccmlib import common
from ccmlib.cluster_factory import ClusterFactory
from ccmlib.remote import PARAMIKO_IS_AVAILABLE, get_remote_usage


# This is fairly fragile, but handy for now
class ForgivingParser(OptionParser):

    def __init__(self, usage=None, option_list=None, option_class=Option, version=None, conflict_handler="error", description=None, formatter=None, add_help_option=True, prog=None, epilog=None):
        OptionParser.__init__(self, usage, option_list, option_class, version, conflict_handler, description, formatter, add_help_option, prog, epilog)
        self.ignored = []

    def _process_short_opts(self, rargs, values):
        opt = rargs[0]
        try:
            OptionParser._process_short_opts(self, rargs, values)
        except BadOptionError:
            self.ignored.append(opt)
            self.eat_args(rargs)

    def _process_long_opt(self, rargs, values):
        opt = rargs[0]
        try:
            OptionParser._process_long_opt(self, rargs, values)
        except BadOptionError:
            self.ignored.append(opt)
            self.eat_args(rargs)

    def eat_args(self, rargs):
        while len(rargs) > 0 and rargs[0][0] != '-':
            self.ignored.append(rargs.pop(0))

    def get_ignored(self):
        return self.ignored


class Cmd(object):
    options_list = []
    usage = ""
    descr_text = ""
    ignore_unknown_options = False

    def get_parser(self):
        if self.usage == "":
            pass
        # Do not collapse to PARAMIKO_IS_AVAILABLE, we need to pull it dynamically for testing purposes
        if ccmlib.remote.PARAMIKO_IS_AVAILABLE:
            self.usage = self.usage.replace("usage: ccm",
                                            "usage: ccm [remote_options]") + \
                         os.linesep + os.linesep + \
                         get_remote_usage()
        parser = self._get_default_parser(self.usage, self.description(), self.ignore_unknown_options)
        for args, kwargs in self.options_list:
            parser.add_option(*args, **kwargs)
        return parser

    def validate(self, parser, options, args, cluster_name=False, node_name=False, load_cluster=False, load_node=True):
        self.options = options
        self.args = args
        if options.config_dir is None:
            self.path = common.get_default_path()
        else:
            self.path = options.config_dir

        if cluster_name:
            if len(args) == 0:
                print_('Missing cluster name', file=sys.stderr)
                parser.print_help()
                exit(1)
            if not re.match('^[a-zA-Z0-9_-]+$', args[0]):
                print_('Cluster name should only contain word characters or hyphen', file=sys.stderr)
                exit(1)
            self.name = args[0]
        if node_name:
            if len(args) == 0:
                print_('Missing node name', file=sys.stderr)
                parser.print_help()
                exit(1)
            self.name = args[0]

        if load_cluster:
            self.cluster = self._load_current_cluster()
            if node_name and load_node:
                try:
                    self.node = self.cluster.nodes[self.name]
                except KeyError:
                    print_('Unknown node %s in cluster %s' % (self.name, self.cluster.name), file=sys.stderr)
                    exit(1)

    def run(self):
        pass

    def _get_default_parser(self, usage, description, ignore_unknown_options=False):
        if ignore_unknown_options:
            parser = ForgivingParser(usage=usage, description=description)
        else:
            parser = OptionParser(usage=usage, description=description)
        parser.add_option('--config-dir', type="string", dest="config_dir",
                          help="Directory for the cluster files [default to {0}]".format(common.get_default_path_display_name()))
        return parser

    def description(self):
        return self.descr_text

    def _load_current_cluster(self):
        name = common.current_cluster_name(self.path)
        if name is None:
            print_('No currently active cluster (use ccm cluster switch)')
            exit(1)
        try:
            return ClusterFactory.load(self.path, name)
        except common.LoadError as e:
            print_(str(e))
            exit(1)
