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

# Copyright 2012 Calculate Ltd. http://www.calculate-linux.org
#
#  Licensed 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.

import sys
import re
import textwrap
import optparse

from utils.text import _toUNICODE
from cl_lang import setLocalTranslate
setLocalTranslate('cl_lib3',sys.modules[__name__],optparse)

def check_choice_regignore(option, opt, value):
    """Check choice with registry independence"""
    if value.lower() in map(lambda x:x.lower(),option.choices):
        return value
    else:
        return optparse.check_choice(option,opt,value)

def make_option(shortOption=None, longOption=None,
                optVal=None, help=None, default=None,
                action=None, type=None, choices=None,
                choices_regignore=None):
    """Make option. Used for processing options parameter in opt __init__. 
    
    shortOption         short option
    longOption          long option
    optVal              argument name, if specified then
                        option is store, else bool value
    help                help string
    default             default value of this option
    action              another action (at example append, for creating list)
    type                type of value
    choices             choice values for type 'choice'
    choices_regignore   alsa as choice only registry ignored

    Examples:
    Simple bool option:
    make_option(shortOption="q",help=_("quiet work mode"))

    Simple value option:
    make_option(longOption="file",help=_("log file"),optVal="LOGFILE",
                default="/var/log/test.log")

    Choice option:
    make_option(shortOption="color",help=_("color mode"), type="choice",
            choices=["auto","always","never"])

    Many option:
    make_option(longOption="set",help=_("set variable"),action="append",
        optVal="VAR")
    """
    # create option by arguments
    args = []
    dest = None
    choices = choices or choices_regignore
    if not shortOption is None:
        args.append("-%s"%shortOption)
        dest = shortOption
    if not longOption is None:
        args.append("--%s"%longOption)
    if type == "choice":
        action = "store"
    else:
        if not optVal is None:
            if not action:
                action = "store"
            type = "string"
        else:
            if not action:
                action = "store_true"
            default = False
            type = None
    opt =  optparse.make_option(*args, action=action, type=type,
                                dest=dest, metavar=optVal,
                                help=help, default=default,
                                choices=choices)
    if choices_regignore:
        opt.TYPE_CHECKER["choice"] = check_choice_regignore
    return opt

class ShareHelpFormatter:
    """Общие методы форматирования help-а"""
    
    def format_option(self, option):
        # The help for each option consists of two parts:
        #   * the opt strings and metavars
        #     eg. ("-x", or "-fFILENAME, --file=FILENAME")
        #   * the user-supplied help string
        #     eg. ("turn on expert mode", "read data from FILENAME")
        #
        # If possible, we write both of these on the same line:
        #   -x      turn on expert mode
        #
        # But if the opt string list is too long, we put the help
        # string on a second line, indented to the same column it would
        # start in if it fit on the first line.
        #   -fFILENAME, --file=FILENAME
        #           read data from FILENAME
        result = []
        opts = self.option_strings[option]
        opt_width = self.help_position - self.current_indent - 2
        if len(opts) > opt_width:
            opts = "%*s%s\n" % (self.current_indent, "", opts)
            indent_first = self.help_position
        else:                       # start help on same line as opts
            opts = "%*s%-*s  " % (self.current_indent, "", opt_width, opts)
            indent_first = 0
        result.append(opts)
        if option.help:
            help_text = self.expand_default(option)
            help_lines = map(lambda x : x.encode('UTF-8'),
                             textwrap.wrap(_toUNICODE(help_text),
                                           self.help_width))
            result.append("%*s%s\n" % (indent_first, "", help_lines[0]))
            result.extend(["%*s%s\n" % (self.help_position, "", line)
                           for line in help_lines[1:]])
        elif opts[-1] != "\n":
            result.append("\n")
        return "".join(result)

    def _format_text(self, text):
        """
        Format a paragraph of free-form text for inclusion in the
        help output at the current indentation level.
        """
        text_width = self.width - self.current_indent
        indent = " "*self.current_indent
        return textwrap.fill(_toUNICODE(text),
                             text_width,
                             initial_indent=indent,
                             subsequent_indent=indent).encode('UTF-8')

    def format_description(self, description):
        if description:
            lines = map(lambda x: self._format_text(x), description.split("\n"))
            return "\n".join(lines)
        else:
            return ""

    def get_example_header(self):
        return _("Examples") + ":\n"

    def format_examples(self, examples, comment=""):
        if examples:
            # "Examples:"
            header = self.get_example_header()
            if comment:
                comment = "%s" % comment
                backup_width = self.width
                self.width = self.width - self.indent_increment - 2
                lines = []
                for line in comment.split("\n"):
                    lines.append(self._format_text(line))
                self.width = backup_width
                lines = ("\n".join(lines)).split("\n")
                out_lines = []
                for line in lines:
                    if line.strip():
                        out_lines.append(" "*self.indent_increment+"# "+line)
                    else:
                        out_lines.append(line)
                comment = "\n".join(out_lines)
                text_examples = "%s\n%s%s" \
                                %(comment," "*self.indent_increment, examples)
            else:
                text_examples = "%s%s" %(" "*self.indent_increment,examples)
            return header + text_examples + "\n"
        else:
            return ""


class TitledHelpFormatter(ShareHelpFormatter, optparse.TitledHelpFormatter):
    """Форматирование вывода помощи с заголовками"""
    def __init__(self,
                 indent_increment=2,
                 max_help_position=24,
                 width=None,
                 short_first=1):
                 optparse.TitledHelpFormatter.__init__(self, indent_increment,
                                                       max_help_position, width,
                                                       short_first)

    def format_usage(self, usage):
        if usage:
            return  "%s%s%s" % (self.format_heading(_("Usage")),
                                                    " "*self.indent_increment,
                                                    usage)
        else:
            return ""

    def get_example_header(self):
        return self.format_heading(_("Examples"))

    def format_description(self, description):
        description = ShareHelpFormatter.format_description(self, description)
        if description:
            return " "*self.indent_increment + description
        else:
            return ""

class IndentedHelpFormatter(ShareHelpFormatter, optparse.IndentedHelpFormatter):
    """Форматирование вывода стандартный класс"""
    def __init__(self,
                 indent_increment=2,
                 max_help_position=24,
                 width=None,
                 short_first=1):
                 optparse.IndentedHelpFormatter.__init__(self, indent_increment,
                                                       max_help_position, width,
                                                       short_first)

    def format_usage(self, usage):
        if usage:
            return _("Usage") + ": %s" % usage
        else:
            return ""


class opt(optparse.OptionParser):
    """Class of options for calculate utilities 

    Example:
    class new_utility(opt):
        def __init__(self):
            opt.__init__(self,
                package=__app__,
                version=__version__,
                description=_("Some description"),\
                option_list= [\
                    #Simple bool option:
                    {
                        'shortOption':'q',
                        'help':_("quiet work mode")
                    },
                    #Simple value option:
                    {
                        'longOption':"file",
                        'help':_("log file"),
                        'optVal':"LOGFILE",
                        "default":"/var/log/test.log")
                    },
                    #Choice option:
                    {
                        'shortOption':"color",
                        'help':_("color mode"),
                        'type':"choice",
                        'choices'=["auto","always","never"]
                    },
                    #Summary option:
                    {
                        'longOption':"set",
                        'help':_("set variable"),
                        'action':"append",
                        'optVal'="VAR"
                    }
                    ]
                )
    """

    variable_set = \
        [{'longOption':"set",
         'optVal':"VAR=VALUE",
         'action':'append',
         'help':_("set the variable value")
        }]
    variable_view = \
        [{'shortOption':"v",
         'longOption':"show-variables",
         'action':'count',
         'help':_("print variables, in verbose mode if the two options "
                  "are applied (includes printing hidden variables)")
        },
        {'longOption':"filter",
         'optVal':"FILTER",
         'help':_("filter variables (as regexp, use *), to be used in"
                  " conjunction with options '-v --show-variables'")
        },
        {'longOption':"xml",
         'help':_("output variables in XML format, to be used in"
                  " conjunction with options '-v --show-variables'")
        }]
    variable_control = variable_set + variable_view

    color_control = \
        [
        {'longOption':"color",
         'optVal':"WHEN",
         'type':'choice',
         'choices':['never','always','auto'],
         'help':_("control whether color is used. WHEN may be") +\
                " 'never', 'always', "+ _("or") + " 'auto'"
        }]

    def __init__(self, package=None, version=None,
                    usage=None, examples=None, description=None,
                    option_list=[],
                    epilog=None,
                    comment_examples=None,
                    formatter=IndentedHelpFormatter(),
                    check_values=None):
        """Установка опций командной строки"""
        self.package = package
        self.examples = examples
        self.comment_examples = comment_examples
        # Дополнительная функция проверки опций
        if check_values:
            self.check_values = check_values
        optparse.OptionParser.__init__(self,
                 usage=usage,
                 option_list=[make_option(**i) for i in option_list],
                 option_class=optparse.Option,
                 version=version,
                 conflict_handler="error",
                 description=description,
                 formatter=formatter,
                 #formatter=TitledHelpFormatter(),
                 epilog=epilog)
        self.formatter._long_opt_fmt = "%s %s"

    def _get_encoding(self, file):
        encoding = getattr(file, "encoding", None)
        if not encoding:
            encoding = "UTF-8"
        return encoding

    def get_usage(self):
        """Replace standard help header"""
        if self.usage:
            return "%s %s\n\n%s"%(self.package,self.version,
                 self.formatter.format_usage(self.expand_prog_name(self.usage)))
        else:
            return ""

    def get_description(self):
        """Replace standard help header"""
        if self.examples:
            if self.comment_examples:
                text_examples = self.formatter.format_examples(self.examples,
                                                         self.comment_examples)
            else:
                text_examples = self.formatter.format_examples(self.examples)
            return self.description + "\n\n" +\
                   self.expand_prog_name(text_examples)
        return self.description + "\n"

    def format_help(self, formatter=None):
        """Decode format help from utf-8 to unicode"""
        return \
            optparse.OptionParser.format_help(self,formatter).decode('UTF-8')

    def error(self, msg):
        """error(msg : string)

        Print a usage message incorporating 'msg' to stderr and exit.
        If you override this in a subclass, it should not return -- it
        should either exit or raise an exception.
        """
        self.print_usage(sys.stderr)
        self.exit(2, "%s: %s: %s\n%s\n" % (self.get_prog_name(),
                                           _("error"), msg,
                _("Try `%s' for more information")%
                ("%s --help"%self.get_prog_name())))

    def checkVarSyntax(self,values):
        """Check value of parameter set, was used for change vars"""
        reCheckSet = re.compile("^[0-9a-z_]+=.*$")
        if values.set:
            for val in values.set:
                if not reCheckSet.match(val):
                    self.error(_("wrong variable set %s")%val)
