#
# -*- coding: utf-8 -*-
#
# This file is part of reclass (http://github.com/madduck/reclass)
#
# Copyright © 2007–13 martin f. krafft <madduck@madduck.net>
# Released under the terms of the Artistic Licence 2.0
#

import posix, sys
import traceback

from reclass.defaults import PARAMETER_INTERPOLATION_SENTINELS

class ReclassException(Exception):

    def __init__(self, msg, rc=posix.EX_SOFTWARE, *args):
        super(ReclassException, self).__init__(msg, *args)
        self._rc = rc
        self._traceback = traceback.format_exc()

    def __str__(self):
        return self.message

    rc = property(lambda self: self._rc)

    def exit_with_message(self, out=sys.stderr):
        print >>out, self.message
        print >>out, self._traceback
        sys.exit(self.rc)


class PermissionError(ReclassException):

    def __init__(self, msg, rc=posix.EX_NOPERM):
        super(PermissionError, self).__init__(msg, rc)


class InvocationError(ReclassException):

    def __init__(self, msg, rc=posix.EX_USAGE):
        super(InvocationError, self).__init__(msg, rc)


class ConfigError(ReclassException):

    def __init__(self, msg, rc=posix.EX_CONFIG):
        super(ConfigError, self).__init__(msg, rc)


class DuplicateUriError(ConfigError):

    def __init__(self, nodes_uri, classes_uri):
        msg = "The inventory URIs must not be the same for nodes and classes: "
        msg += nodes_uri
        super(DuplicateUriError, self).__init__(msg)


class UriOverlapError(ConfigError):

    def __init__(self, nodes_uri, classes_uri):
        msg = "The URIs for the nodes and classes inventories must not " \
              "overlap, but {0} and {1} do."
        msg = msg.format(nodes_uri, classes_uri)
        super(UriOverlapError, self).__init__(msg)


class NotFoundError(ReclassException):

    def __init__(self, msg, rc=posix.EX_IOERR):
        super(NotFoundError, self).__init__(msg, rc)


class NodeNotFound(NotFoundError):

    def __init__(self, storage, nodename, uri):
        self._storage = storage
        self._name = nodename
        self._uri = uri
        msg = "Node '{0}' not found under {1}://{2}".format(self._name,
                                                            self._storage,
                                                            self._uri)
        super(NodeNotFound, self).__init__(msg)


class ClassNotFound(NotFoundError):

    def __init__(self, storage, classname, uri, nodename):
        self._storage = storage
        self._name = classname
        self._uri = uri
        self._nodename = nodename
        msg = "Class '{0}' (in ancestry of node '{1}') not found under {2}://{3}" \
                .format(self._name, self._nodename, self._storage, self._uri)
        super(ClassNotFound, self).__init__(msg)


class InterpolationError(ReclassException):

    def __init__(self, msg, rc=posix.EX_DATAERR):
        super(InterpolationError, self).__init__(msg, rc)


class UndefinedVariableError(InterpolationError):

    def __init__(self, var, context=None):
        self._var = var
        msg = "Cannot resolve " + var.join(PARAMETER_INTERPOLATION_SENTINELS)
        if context:
            msg += ' in the context of %s' % context
        super(UndefinedVariableError, self).__init__(msg)

    var = property(lambda x: x._var)


class IncompleteInterpolationError(InterpolationError):

    def __init__(self, string, end_sentinel):
        msg = "Missing '%s' to end reference: %s" % \
                (end_sentinel, string.join(PARAMETER_INTERPOLATION_SENTINELS))
        super(IncompleteInterpolationError, self).__init__(msg)


class InfiniteRecursionError(InterpolationError):

    def __init__(self, path, ref):
        msg = "Infinite recursion while resolving %s at %s" \
                % (ref.join(PARAMETER_INTERPOLATION_SENTINELS), path)
        super(InfiniteRecursionError, self).__init__(msg)


class MappingError(ReclassException):

    def __init__(self, msg, rc=posix.EX_DATAERR):
        super(MappingError, self).__init__(msg, rc)


class MappingFormatError(MappingError):

    def __init__(self, msg):
        super(MappingFormatError, self).__init__(msg)


class NameError(ReclassException):

    def __init__(self, msg, rc=posix.EX_DATAERR):
        super(NameError, self).__init__(msg, rc)


class InvalidClassnameError(NameError):

    def __init__(self, invalid_character, classname):
        self._invalid_character = invalid_character
        self._classname = classname
        msg = "Invalid character '{0}' in class name '{1}'."
        msg = msg.format(invalid_character, classname)
        super(InvalidClassnameError, self).__init__(msg)


class DuplicateNodeNameError(NameError):

    def __init__(self, storage, name, uri1, uri2):
        msg = "{0}: Definition of node '{1}' in '{2}' collides with " \
              "definition in '{3}'. Nodes can only be defined once per inventory."
        msg = msg.format(storage, name, uri2, uri1)
        super(DuplicateNodeNameError, self).__init__(msg)
