/*
 * Copyright (c) 2000-2009 TeamDev Ltd. All rights reserved.
 * TeamDev PROPRIETARY and CONFIDENTIAL.
 * Use is subject to license terms.
 */
package com.jniwrapper.win32;

import com.jniwrapper.Int32;
import com.jniwrapper.IntegerParameter;
import com.jniwrapper.util.Logger;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * The HResult class represents the HRESULT data type and is used to describe errors.
 */
public class HResult extends Int32
{
    /**
     * Successful completion.
     */
    public static final int S_OK = 0;
    /**
     * Completed without error, but only partial results were obtained. If a buffer is not
     * large enough to hold the information that is returned to it, the returned information
     * is often truncated to fit into the buffer and S_FALSE is returned from the method.
     */
    public static final int S_FALSE = 1;
    /**
     * Not implemented.
      */
    public static final int E_NOTIMPL = 0x80004001;
    /**
     * A memory allocation attempt failed.
     */
    public static final int E_OUTOFMEMORY = 0x8007000e;
    /**
     * One of the arguments passed in was invalid.
     */
    public static final int E_INVALIDARG = 0x80070057;
    /**
     * The object searched for was not found.
      */
    public static final int E_NOINTERFACE = 0x80004002;
    /**
     * 	Pointer that is not valid
     */
    public static final int E_POINTER = 0x80004003;
    /**
     * 	Handle that is not valid
     */
    public static final int E_HANDLE = 0x80070006;
    /**
     * 	Operation aborted
     */
    public static final int E_ABORT = 0x80004004;
    /**
     * The operation could not be performed.
     */
    public static final int E_FAIL = 0x80004005;
    /**
     *  The target was not accessible, or the engine was not in a state where the function or
     *  method could be processed.
     */
    public static final int E_UNEXPECTED = 0x8000FFFF;
    /**
     * 	General access denied error
     */
    public static final int E_ACCESSDENIED = 0x80070005;

    /**
     * Class does not support aggregation (or class object is remote).
     */
    public static final int CLASS_E_NOAGGREGATION = 0x80040110;
    /**
     * ClassFactory cannot supply requested class.
     */
    public static final int CLASS_E_CLASSNOTAVAILABLE = 0x80040111;
    /**
     * Class is not licensed for use.
     */
    public static final int CLASS_E_NOTLICENSED = 0x80040112;

    /**
     *  The lowest allowed value for connection error codes.
     */
    public static final int CONNECT_E_FIRST = 0x80040200;
    /**
     * There is no connection
     */
    public static final int CONNECT_E_NOCONNECTION = 0x80040201;
    /**
     * This implementation's limit for advisory connections has been reached
     */
    public static final int CONNECT_E_ADVISELIMIT = 0x80040202;
    /**
     * Connection attempt failed
     */
    public static final int CONNECT_E_CANNOTCONNECT = 0x80040203;
    /**
     * Must use a derived interface to connect
     */
    public static final int CONNECT_E_OVERRIDDEN = 0x80040204;

    /**
     * The server was unable to complete the registration of all the type libraries used by its classes.
     */
    public static final int SELFREG_E_TYPELIB = 0x80040200;
    /**
     * The server was unable to complete the registration of all the object classes.
     */
    public static final int SELFREG_E_CLASS = 0x80040201;

    /**
     * COM object does not have an appropriate connection point for the  specified IID
     */
    public static final int PERPROP_E_NOPAGEAVAILABLE = 0x80040200;

    /**
     * The URL could not be parsed.
     */
    public static final int INET_E_INVALID_URL = 0x800c0002;
    /**
     * No Internet session was established.
     */
    public static final int INET_E_NO_SESSION = 0x800c0003;
    /**
     * The attempt to connect to the Internet has failed.
     */
    public static final int INET_E_CANNOT_CONNECT = 0x800c0004;
    /**
     * The server or proxy was not found.
     */
    public static final int INET_E_RESOURCE_NOT_FOUND = 0x800c0005;
    /**
     * The object was not found.
     */
    public static final int INET_E_OBJECT_NOT_FOUND = 0x800c0006;
    /**
     * An Internet connection was established, but the data cannot be retrieved.
     */
    public static final int INET_E_DATA_NOT_AVAILABLE = 0x800c0007;
    /**
     * The download has failed (the connection was interrupted).
     */
    public static final int INET_E_DOWNLOAD_FAILURE = 0x800c0008;
    /**
     * Authentication is needed to access the object.
     */
    public static final int INET_E_AUTHENTICATION_REQUIRED = 0x800c0009;
    /**
     * The object is not in one of the acceptable MIME types.
     */
    public static final int INET_E_NO_VALID_MEDIA = 0x800c000a;
    /**
     * The Internet connection has timed out.
     */
    public static final int INET_E_CONNECTION_TIMEOUT = 0x800c000b;
    /**
     * The request was invalid.
     */
    public static final int INET_E_INVALID_REQUEST = 0x800c000c;
    /**
     * The protocol is not known and no pluggable protocols have been entered that match.
     */
    public static final int INET_E_UNKNOWN_PROTOCOL = 0x800c000d;
    /**
     * The URI contains syntax that attempts to bypass security.
     */
    public static final int INET_E_SECURITY_PROBLEM = 0x800c000e;
    /**
     * The object could not be loaded.
     */
    public static final int INET_E_CANNOT_LOAD_DATA = 0x800c000f;
    /**
     * Creation of the object of the class associated with a specified CLSID failed
     */
    public static final int INET_E_CANNOT_INSTANTIATE_OBJECT = 0x800c0010;
    /**
     * Use the default protocol handler.
     */
    public static final int INET_E_USE_DEFAULT_PROTOCOLHANDLER = 0x800c0011;
    /**
     * Use the default security manager for this action. A custom security manager should
     * only process input that is both valid and specific to itself and return
     * INET_E_DEFAULT_ACTION for all other methods or URL actions.
     */
    public static final int INET_E_DEFAULT_ACTION = 0x800c0011;
    /**
     * Use the default settings.
     */
    public static final int INET_E_USE_DEFAULT_SETTING = 0x800c0012;
    /**
     * The requested option is unknown.
     */
    public static final int INET_E_QUERYOPTION_UNKNOWN = 0x800c0013;
    /**
     * The request is being redirected.
     */
    public static final int INET_E_REDIRECTING = 0x800c0014;
    /**
     * Microsoft Win32 Internet (WinInet) cannot redirect. This error code might also be
     * returned by a custom protocol handler.
     */
    public static final int INET_E_REDIRECT_FAILED = 0x800c0014;
    /**
     * The request is being redirected to a directory.
     */
    public static final int INET_E_REDIRECT_TO_DIR = 0x800c0015;
    /**
     * The requested resource could not be locked.
     */
    public static final int INET_E_CANNOT_LOCK_REQUEST = 0x800c0016;
    /**
     * Reissue request with extended binding.
     */
    public static final int INET_E_USE_EXTEND_BINDING = 0x800c0017;
    /**
     * The lowest allowed value for an URL moniker error code.
     */
    public static final int INET_E_ERROR_FIRST = 0x800c0002;
    /**
     * The highest allowed value for an URL moniker error code.
     */
    public static final int INET_E_ERROR_LAST = 0x800c0017;
    /**
     * The component download was declined by the user.
     */
    public static final int INET_E_CODE_DOWNLOAD_DECLINED = 0x800c0100;
    /**
     * The binding has already been completed and the result has been dispatched, so your
     * abort call has been canceled.
     */
    public static final int INET_E_RESULT_DISPATCHED = 0x800c0200;
    /**
     * Cannot replace a file that is protected by System File Protection (SFP). 
     */
    public static final int INET_E_CANNOT_REPLACE_SFP_FILE = 0x800c0300;

    /**
     * Unknown interface.
     */
    public static final int DISP_E_UNKNOWNINTERFACE = 0x80020001;
    /**
     * Member not found.
     */
    public static final int DISP_E_MEMBERNOTFOUND = 0x80020003;
    /**
     * Parameter not found.
     */
    public static final int DISP_E_PARAMNOTFOUND = 0x80020004;
    /**
     * Type mismatch.
     */
    public static final int DISP_E_TYPEMISMATCH = 0x80020005;
    /**
     * Unknown name.
     */
    public static final int DISP_E_UNKNOWNNAME = 0x80020006;
    /**
     * No named arguments.
     */
    public static final int DISP_E_NONAMEDARGS = 0x80020007;
    /**
     * Bad variable type.
     */
    public static final int DISP_E_BADVARTYPE = 0x80020008;
    /**
     * Exception occurred.
     */
    public static final int DISP_E_EXCEPTION = 0x80020009;
    /**
     * Out of present range.
     */
    public static final int DISP_E_OVERFLOW = 0x8002000A;
    /**
     * Invalid index.
     */
    public static final int DISP_E_BADINDEX = 0x8002000B;
    /**
     * Unknown language.
     */
    public static final int DISP_E_UNKNOWNLCID = 0x8002000C;
    /**
     * Memory is locked.
     */
    public static final int DISP_E_ARRAYISLOCKED = 0x8002000D;
    /**
     * Invalid number of parameters.
     */
    public static final int DISP_E_BADPARAMCOUNT = 0x8002000E;
    /**
     * Parameter not optional.
     */
    public static final int DISP_E_PARAMNOTOPTIONAL = 0x8002000F;
    /**
     * Invalid callee.
     */
    public static final int DISP_E_BADCALLEE = 0x80020010;
    /**
     * Does not support a collection.
     */
    public static final int DISP_E_NOTACOLLECTION = 0x80020011;
    /**
     * Division by zero.
     */
    public static final int DISP_E_DIVBYZERO = 0x80020012;

    /**
     * COM library has not been called.
     */
    public static final int CO_E_NOTINITIALIZED = 0x800401F0;
    /**
     *  COM library has already been called.
     */
    public static final int CO_E_ALREADYINITIALIZED = 0x800401F1;
    /**
     * Class of object cannot be determined.
     */
    public static final int CO_E_CANTDETERMINECLASS = 0x800401F2;
    /**
     * Invalid class string.
     */
    public static final int CO_E_CLASSSTRING = 0x800401F3;
    /**
     * Invalid interface string
     */
    public static final int CO_E_IIDSTRING = 0x800401F4;
    /**
     * Application not found.
     */
    public static final int CO_E_APPNOTFOUND = 0x800401F5;
    /**
     * Application cannot be run more than once.
     */
    public static final int CO_E_APPSINGLEUSE = 0x800401F6;
    /**
     * Some error in application.
     */
    public static final int CO_E_ERRORINAPP = 0x800401F7;
    /**
     * DLL for class not found.
     */
    public static final int CO_E_DLLNOTFOUND = 0x800401F8;
    /**
     * Error in the DLL.
     */
    public static final int CO_E_ERRORINDLL = 0x800401F9;
    /**
     * Wrong operating system or operating system version for application.
     */
    public static final int CO_E_WRONGOSFORAPP = 0x800401FA;
    /**
     * Object is not registered.
     */
    public static final int CO_E_OBJNOTREG = 0x800401FB;
    /**
     * Object is already registered.
     */
    public static final int CO_E_OBJISREG = 0x800401FC;
    /**
     * Object is not connected to server.
     */
    public static final int CO_E_OBJNOTCONNECTED = 0x800401FD;
    /**
     * Application was launched, but it did not register a class factory.
     */
    public static final int CO_E_APPDIDNTREG = 0x800401FE;
    /**
     * Object has been released.
     */
    public static final int CO_E_RELEASED = 0x800401FF;

    /**
     * The object invoked has disconnected from its clients.
     */
    public static final int RPC_E_DISCONNECTED = 0x80010108;

    /**
     * The message filter indicated that the application is busy.
     */
    public static final int RPC_E_SERVERCALL_RETRYLATER = 0x8001010A;

    /**
     * Call was rejected by callee.
     */
    public static final int RPC_E_CALL_REJECTED = 0x80010001;

    /**
     *  The lowest allowed value for an OLE error code.
     */
    public static final int OLE_E_FIRST = 0x80040000;
    /**
     *  The highest allowed value for an OLE error code.
     */
    public static final int OLE_E_LAST = 0x800400FF;
    /**
     *  The lowest allowed value for an OLE status code.
     */
    public static final int OLE_S_FIRST = 0x00040000;
    /**
     *  The highest allowed value for an OLE status code.
     */
    public static final int OLE_S_LAST = 0x000400FF;
    /**
     * Invalid OLEVERB structure.
     */
    public static final int OLE_E_OLEVERB = 0x80040000;
    /**
     * Invalid advise flags.
     */
    public static final int OLE_E_ADVF = 0x80040001;
    /**
     * Cannot enumerate any more because the associated data is missing.
     */
    public static final int OLE_E_ENUM_NOMORE = 0x80040002;
    /**
     * This implementation does not take advises.
     */
    public static final int OLE_E_ADVISENOTSUPPORTED = 0x80040003;
    /**
     * There is no connection for this connection ID.
     */
    public static final int OLE_E_NOCONNECTION = 0x80040004;
    /**
     * Need to run the object to perform this operation.
     */
    public static final int OLE_E_NOTRUNNING = 0x80040005;
    /**
     * There is no cache to operate on.
     */
    public static final int OLE_E_NOCACHE = 0x80040006;
    /**
     * Uninitialized object.
     */
    public static final int OLE_E_BLANK = 0x80040007;
    /**
     * Linked object's source class has changed.
     */
    public static final int OLE_E_CLASSDIFF = 0x80040008;
    /**
     * Not able to get the moniker of the object.
     */
    public static final int OLE_E_CANT_GETMONIKER = 0x80040009;
    /**
     * Not able to bind to the source.
     */
    public static final int OLE_E_CANT_BINDTOSOURCE = 0x8004000A;
    /**
     * Object is static, operation not allowed.
     */
    public static final int OLE_E_STATIC = 0x8004000B;
    /**
     * User canceled out of the Save dialog box.
     */
    public static final int OLE_E_PROMPTSAVECANCELLED = 0x8004000C;
    /**
     * Invalid rectangle.
     */
    public static final int OLE_E_INVALIDRECT = 0x8004000D;
    /**
     * compobj.dll is too old for the ole2.dll initialized.
     */
    public static final int OLE_E_WRONGCOMPOBJ = 0x8004000E;
    /**
     * Invalid window handle.
     */
    public static final int OLE_E_INVALIDHWND = 0x8004000F;
    /**
     * Object is not in any of the inplace active states.
     */
    public static final int OLE_E_NOT_INPLACEACTIVE = 0x80040010;
    /**
     * Not able to convert object.
     */
    public static final int OLE_E_CANTCONVERT = 0x80040011;
    /**
     * Not able to perform the operation because object is not given storage yet.
     */
    public static final int OLE_E_NOSTORAGE = 0x80040012;

    /**
     * Class not registered.
     */
    public static final int REGDB_E_CLASSNOTREG = 0x80040154;
    /**
     * Interface not registered.
     */
    public static final int REGDB_E_IIDNOTREG = 0x80040155;

    /**
     *  The lowest allowed value for an OLE command error code.
     */
    public static final int OLECMDERR_E_FIRST = OLE_E_LAST + 1;
    /**
     * Command parameter is not recognized as a valid command
     */
    public static final int OLECMDERR_E_NOTSUPPORTED = OLECMDERR_E_FIRST;
    /**
     * Command group parameter is non-NULL but does not specify a recognized command group
     */
    public static final int OLECMDERR_E_UNKNOWNGROUP = OLECMDERR_E_FIRST + 4;

    private static Map _namesMap = null;

    private static final Logger LOG = Logger.getInstance(HResult.class);

    /**
     * Creates the instance of the <code>HResult</code>
     */
    public HResult()
    {
    }

    /**
     * Creates instance of the  <code>HResult</code> with the specified value
     *
     * @param value Error or state code
     */
    public HResult(int value)
    {
        super(value);
    }

    /**
     * Creates instance of the  <code>HResult</code> with the specified value
     *
     * @param value Error or state code
     */
    public HResult(IntegerParameter value)
    {
        super(value);
    }

    /**
     * Creates instance of the  <code>HResult</code> with the specified value
     *
     * @param severity The facility's status code. A code describing the error or warning.
     * @param facility The facility code. Specifies the software component that defines this error code. 
     * @param isError TRUE if success and FALSE IF failure.
     */
    public HResult(short severity, short facility, boolean isError)
    {
        super((int)
                (((long) severity & 0xffff) |
                        (((long) facility & 0x1fff) << 16) |
                        ((isError ? 1L : 0L) << 31))
        );
    }

    /**
     * Returns facility's status code
     *
     * @return Facility's status code
     */
    public short getSCode()
    {
        return (short) (getValue() & 0xffff);
    }

    /**
     * Returns facility code
     *
     * @return The facility code
     */
    public short getFacility()
    {
        return (short) ((getValue() >> 16) & 0x1fff);
    }

    /**
     * Specifies whether this is error or not
     *
     * @return TRUE if this is error cod and FALSE if not
     */
    public boolean isError()
    {
        return (getValue() & 0x80000000L) != 0;
    }

    /** Creates and returns a copy of this object.
     *
     * @return The copy of this instance
     */
    public Object clone()
    {
        return new HResult(this);
    }

    /**
     * Returns instance of the <code>Map</code> that contains all error codes, defined in this class
     *
     * @return Instance of the <code>Map</code> that contains all error codes, defined in this class
     */
    private static Map getNamesMap()
    {
        if (_namesMap == null)
        {
            try
            {
                _namesMap = new HashMap();
                Field[] declaredFields = HResult.class.getDeclaredFields();
                for (int i = 0; i < declaredFields.length; i++)
                {
                    Field field = declaredFields[i];
                    field.setAccessible(true);
                    if (int.class.equals(field.getType()) && (field.getModifiers() & Modifier.FINAL) != 0)
                    {
                        _namesMap.put(new Integer(field.getInt(null)), field.getName());
                    }
                }
            }
            catch (Exception e)
            {
                LOG.error("", e);
                return Collections.EMPTY_MAP;
            }
        }
        return _namesMap;
    }


    /**
     * Returns name (if found) and description of the error that corresponds to the specified code
     *
     * @param hResult <code>HResult</code> code
     * @return String that contains name (if found) and description of the error that corresponds
     * to the specified code in format "[description] ([name])"
     */
    public static String decodeHResult(int hResult)
    {
        String name = (String) getNamesMap().get(new Integer(hResult));
        String desc = (String) LastError.getMessage(hResult);

        if (name != null)
        {
            if (desc != null && !desc.equals(""))
            {
                return name + " (" + desc + ")";
            }
            else
            {
                return name;
            }
        }
        else
        {
            return desc != null ? desc : "";
        }
    }
}