# Copyright 2015 Vmware, Inc. All rights reserved. -- VMware Confidential
#
'''Holder of patch specific errors.

In patch we will support only one type of errors: not recoverable(PermanentError).
All other type of errors will be considered as InternalError and their error
messages won't be revealed to our customers, because will be considered as
container of internal information which shouldn't be exposed. InternalError
will also be considered as PermanentError, with a difference that their error
message won't be exposed to the customer.
Therefore from patch framework point of view there are follow type of errors

    *PermanentError - errors targeted to expose errors to customers and
      optionally suggest what have to be changed/improved before running the
      patching from the very beginning. The patching cannot be restarted if such
      error occurs.

    *InternalError - errors targeted to hide errors from customers. The patching
      cannot be restarted if such error occurs. All errors except PermanentError
      will be considered as InternalErrors.
'''
from status_reporting_sdk.json_utils import Serializable

class BaseError(Exception, Serializable):
    '''Represents the root exception type of patch specific error.
    '''
    def __init__(self, cause):
        '''Initializes the error.

        @param cause: The error root cause.
        @type cause: Object
        '''
        super(BaseError, self).__init__()
        self.cause = cause

    def __str__(self):
        return str(self.cause)

class InternalError(BaseError):
    '''Thrown when something unexpected happened. Once thrown the customer won't
    be allowed to proceed with the patching and he/she have to start from the
    very beginning. The internal cause stays private for users, and they need to
    check out the logs to see what wrong has happened. Any none UserError will
    be automatically transformed to InternalErrors.
    '''
    def __init__(self, cause):
        '''Initializes the error.

        @param cause: The error root cause.
        @type cause: string
        '''
        super(InternalError, self).__init__(cause)

class UserError(BaseError):
    '''Error which is supposed to be shown to the user. All inheritors
    of this class should be aware that the error details will be shown to the
    end-user.
    '''
    def __init__(self, cause, resolution=None, problemId=None):
        '''Initializes the error.

        @param cause: The error root cause.
        @type cause: LocalizableMessage

        @param resolution: Suggested resolution which will put the system in order.
        @type resolution: LocalizableMessage

        @param problemId: Unique problem identificator which is mapped to
            external VMWare article explaining the problem.
        @type problemId: str
        '''
        super(UserError, self).__init__(cause)
        self.resolution = resolution
        self.problemId = problemId

class PermanentError(UserError):
    '''Thrown when permanent error occurs on the system. Once thrown the
      customer won't be allowed to proceed with the patching and he/she has to
    start from the very beginning.
    '''
    pass

class ComponentError(Exception, Serializable):
    '''Wrapper Error that shows which component throw the base error. All inheritors
    of this class should be aware that the error details will be shown to the
    end-user.

    @param baseError: The original error
    @type baseError: BaseError or subclass

    @param componentKey: The component key for this error
    @type componentKey: str
    '''
    def __init__(self, baseError, componentKey):
        super(ComponentError, self).__init__()
        self.componentKey = componentKey
        self.baseError = baseError

    def __getattr__(self, attr):
        if hasattr(self.baseError, attr):
            return getattr(self.baseError, attr)
        else:
            raise AttributeError("ComponentError instance has no attribute '%s'" % attr)
