# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function, unicode_literals

__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
__copyright__ = "Copyright (C) 2015 The OctoPrint Project - Released under terms of the AGPLv3 License"


import click
click.disable_unicode_literals_warning = True
import octoprint
import sys

#~~ click context

class OctoPrintContext(object):
	"""Custom context wrapping the standard options."""

	def __init__(self, configfile=None, basedir=None, verbosity=0, safe_mode=False):
		self.configfile = configfile
		self.basedir = basedir
		self.verbosity = verbosity
		self.safe_mode = safe_mode

pass_octoprint_ctx = click.make_pass_decorator(OctoPrintContext, ensure=True)
"""Decorator to pass in the :class:`OctoPrintContext` instance."""

#~~ Custom click option to hide from help

class HiddenOption(click.Option):
	"""Custom option sub class with empty help."""
	def get_help_record(self, ctx):
		pass

def hidden_option(*param_decls, **attrs):
	"""Attaches a hidden option to the command.  All positional arguments are
	passed as parameter declarations to :class:`Option`; all keyword
	arguments are forwarded unchanged.  This is equivalent to creating an
	:class:`Option` instance manually and attaching it to the
	:attr:`Command.params` list.
	"""

	import inspect
	from click.decorators import _param_memo

	def decorator(f):
		if 'help' in attrs:
			attrs['help'] = inspect.cleandoc(attrs['help'])
		_param_memo(f, HiddenOption(param_decls, **attrs))
		return f
	return decorator

#~~ helper for setting context options

def set_ctx_obj_option(ctx, param, value):
	"""Helper for setting eager options on the context."""
	if ctx.obj is None:
		ctx.obj = OctoPrintContext()
	if value != param.default:
		setattr(ctx.obj, param.name, value)
	elif param.default is not None:
		setattr(ctx.obj, param.name, param.default)

#~~ helper for retrieving context options

def get_ctx_obj_option(ctx, key, default, include_parents=True):
	if include_parents and hasattr(ctx, "parent") and ctx.parent:
		fallback = get_ctx_obj_option(ctx.parent, key, default)
	else:
		fallback = default
	return getattr(ctx.obj, key, fallback)

#~~ helper for setting a lot of bulk options

def bulk_options(options):
	"""
	Utility decorator to decorate a function with a list of click decorators.

	The provided list of ``options`` will be reversed to ensure correct
	processing order (inverse from what would be intuitive).
	"""

	def decorator(f):
		options.reverse()
		for option in options:
			option(f)
		return f
	return decorator

#~~ helper for setting --basedir, --config and --verbose options

def standard_options(hidden=False):
	"""
	Decorator to add the standard options shared among all "octoprint" commands.

	Adds the options ``--basedir``, ``--config`` and ``--verbose``. If ``hidden``
	is set to ``True``, the options will be available on the command but not
	listed in its help page.
	"""

	factory = click.option
	if hidden:
		factory = hidden_option

	options = [
		factory("--basedir", "-b", type=click.Path(), callback=set_ctx_obj_option, is_eager=True, expose_value=False,
		        help="Specify the basedir to use for configs, uploads, timelapses etc."),
		factory("--config", "-c", "configfile", type=click.Path(), callback=set_ctx_obj_option, is_eager=True, expose_value=False,
		        help="Specify the config file to use."),
		factory("--verbose", "-v", "verbosity", count=True, callback=set_ctx_obj_option, is_eager=True, expose_value=False,
		        help="Increase logging verbosity."),
		factory("--safe", "safe_mode", is_flag=True, callback=set_ctx_obj_option, is_eager=True, expose_value=False,
		        help="Enable safe mode; disables all third party plugins.")
	]

	return bulk_options(options)

#~~ helper for settings legacy options we still have to support on "octoprint"

legacy_options = bulk_options([
	hidden_option("--host", type=click.STRING, callback=set_ctx_obj_option),
	hidden_option("--port", type=click.INT, callback=set_ctx_obj_option),
	hidden_option("--logging", type=click.Path(), callback=set_ctx_obj_option),
	hidden_option("--debug", "-d", is_flag=True, callback=set_ctx_obj_option),
	hidden_option("--daemon", type=click.Choice(["start", "stop", "restart"]), callback=set_ctx_obj_option),
	hidden_option("--pid", type=click.Path(), default="/tmp/octoprint.pid", callback=set_ctx_obj_option),
	hidden_option("--iknowwhatimdoing", "allow_root", is_flag=True, callback=set_ctx_obj_option),
	hidden_option("--ignore-blacklist", "ignore_blacklist", is_flag=True, callback=set_ctx_obj_option)
])
"""Legacy options available directly on the "octoprint" command in earlier versions.
   Kept available for reasons of backwards compatibility, but hidden from the
   generated help pages."""

#~~ "octoprint" command, merges server_commands and plugin_commands groups

from .server import server_commands
from .plugins import plugin_commands
from .dev import dev_commands
from .client import client_commands
from .config import config_commands
from .analysis import analysis_commands
from .user import user_commands

@click.group(name="octoprint", invoke_without_command=True, cls=click.CommandCollection,
             sources=[server_commands, plugin_commands, dev_commands, client_commands, config_commands, analysis_commands, user_commands])
@standard_options()
@legacy_options
@click.version_option(version=octoprint.__version__, allow_from_autoenv=False)
@click.pass_context
def octo(ctx, **kwargs):

	if ctx.invoked_subcommand is None:
		# We have to support calling the octoprint command without any
		# sub commands to remain backwards compatible.
		#
		# But better print a message to inform people that they should
		# use the sub commands instead.

		def get_value(key):
			return get_ctx_obj_option(ctx, key, kwargs.get(key))
		daemon = get_value("daemon")

		if daemon:
			click.echo("Daemon operation via \"octoprint --daemon "
			           "start|stop|restart\" is deprecated, please use "
			           "\"octoprint daemon start|stop|restart\" from now on")

			if sys.platform == "win32" or sys.platform == "darwin":
				click.echo("Sorry, daemon mode is not supported under your operating system right now")
			else:
				from octoprint.cli.server import daemon_command
				ctx.invoke(daemon_command, command=daemon, **kwargs)
		else:
			click.echo("Starting the server via \"octoprint\" is deprecated, "
			           "please use \"octoprint serve\" from now on.")

			from octoprint.cli.server import serve_command
			ctx.invoke(serve_command, **kwargs)
