/*
 *                         OpenSplice DDS
 *
 *   This software and documentation are Copyright 2006 to TO_YEAR PrismTech
 *   Limited, its affiliated companies and licensors. All rights reserved.
 *
 *   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.
 *
 */
package org.opensplice.dds.core;

import java.util.concurrent.TimeoutException;

import org.omg.dds.core.Duration;
import org.omg.dds.core.InstanceHandle;
import org.omg.dds.core.Time;

import DDS.ErrorInfo;

public class Utilities {

    public static void checkReturnCode(int retCode,
            OsplServiceEnvironment environment, String message) {
        try {
            checkReturnCode(retCode, environment, message, false);
        } catch (TimeOutExceptionImpl t) {
            throw new DDSExceptionImpl(environment,
                    "Internal error: TimeOutException caught in unexpected location.");
        }
    }

    private static void checkReturnCode(int retCode,
            OsplServiceEnvironment environment, String message,
            boolean withTimeOut) throws TimeOutExceptionImpl {
        switch (retCode) {
        case DDS.RETCODE_PRECONDITION_NOT_MET.value:
            throw new PreconditionNotMetExceptionImpl(environment,
                    getErrorMessage(retCode, message));
        case DDS.RETCODE_OUT_OF_RESOURCES.value:
            throw new OutOfResourcesExceptionImpl(environment, getErrorMessage(
                    retCode, message));
        case DDS.RETCODE_ALREADY_DELETED.value:
            throw new AlreadyClosedExceptionImpl(environment, getErrorMessage(
                    retCode, message));
        case DDS.RETCODE_BAD_PARAMETER.value:
            throw new IllegalArgumentExceptionImpl(environment,
                    getErrorMessage(retCode, message));
        case DDS.RETCODE_ERROR.value:
            throw new DDSExceptionImpl(environment, getErrorMessage(retCode,
                    message));
        case DDS.RETCODE_ILLEGAL_OPERATION.value:
            throw new IllegalOperationExceptionImpl(environment,
                    getErrorMessage(retCode, message));
        case DDS.RETCODE_IMMUTABLE_POLICY.value:
            throw new ImmutablePolicyExceptionImpl(environment,
                    getErrorMessage(retCode, message));
        case DDS.RETCODE_INCONSISTENT_POLICY.value:
            throw new InconsistentPolicyExceptionImpl(environment,
                    getErrorMessage(retCode, message));
        case DDS.RETCODE_NOT_ENABLED.value:
            throw new NotEnabledExceptionImpl(environment, getErrorMessage(
                    retCode, message));
        case DDS.RETCODE_UNSUPPORTED.value:
            throw new UnsupportedOperationExceptionImpl(environment,
                    getErrorMessage(retCode, message));
        case DDS.RETCODE_TIMEOUT.value:
            if (withTimeOut) {
                if (retCode == DDS.RETCODE_TIMEOUT.value) {
                    throw new TimeOutExceptionImpl(environment,
                            getErrorMessage(retCode, message));
                }
            }
        case DDS.RETCODE_OK.value:
        case DDS.RETCODE_NO_DATA.value:
        default:
            break;
        }
    }

    private static String getDetails(DDS.ErrorInfo errorInfo, String message) {
        String result = "";
        DDS.StringHolder messageHolder = new DDS.StringHolder();

        errorInfo.get_message(messageHolder);

        if (messageHolder.value != null) {
            if (message != null) {
                result += message;
                result += "(" + messageHolder.value + ")";
            } else {
                result += messageHolder.value;
            }
        } else if (message != null) {
            result += message;
        }
        errorInfo.get_location(messageHolder);

        if (messageHolder.value != null) {
            result += " at " + messageHolder.value;
        }
        errorInfo.get_source_line(messageHolder);

        if (messageHolder.value != null) {
            result += " (" + messageHolder.value + ")";
        }
        errorInfo.get_stack_trace(messageHolder);

        if (messageHolder.value != null) {
            result += "\nStack trace:\n" + messageHolder.value;
        }
        return result;
    }

    private static String getErrorMessage(int retCode, String message) {
        String output;
        DDS.ErrorInfo errorInfo = new ErrorInfo();
        int result = errorInfo.update();

        switch (result) {
        case DDS.RETCODE_NO_DATA.value:
            output = message;
            break;
        case DDS.RETCODE_OK.value:
            output = getDetails(errorInfo, message);
            break;
        default:
            if (message != null) {
                output = " Unable to get extra error information due to internal error.("
                    + message + ")";
            } else {
                output = " Unable to get extra error information due to internal error.";
            }
            break;
        }
        return output;
    }

    public static String getOsplExceptionStack(Exception ex,
            StackTraceElement[] stack) {
        StringBuffer result = new StringBuffer();
        int startIndex = 0;

        result.append(ex.getClass().getSuperclass().getName() + ": "
                + ex.getMessage() + "\n");

        while ((stack.length > startIndex)
                && (Utilities.class.getName().equals(stack[startIndex]
                        .getClassName()))) {
            startIndex++;
        }
        for (int i = startIndex; i < stack.length; i++) {
            result.append("\tat ");
            result.append(stack[i].getClassName());
            result.append(".");
            result.append(stack[i].getMethodName());
            result.append(" (");
            result.append(stack[i].getFileName());
            result.append(":");
            result.append(stack[i].getLineNumber());
            result.append(")\n");
        }
        return result.toString();
    }

    public static void checkReturnCodeWithTimeout(int retCode,
            OsplServiceEnvironment environment, String message)
            throws TimeoutException {
        checkReturnCode(retCode, environment, message, true);
    }

    public static void throwLastErrorException(
            OsplServiceEnvironment environment) {
        String message;
        int code;
        DDS.ErrorInfo errorInfo = new ErrorInfo();
        int result = errorInfo.update();

        switch (result) {
        case DDS.RETCODE_NO_DATA.value:
            message = "";
            code = DDS.RETCODE_ERROR.value;
            break;
        case DDS.RETCODE_OK.value:
            DDS.ReturnCodeHolder errorHolder = new DDS.ReturnCodeHolder();
            errorInfo.get_code(errorHolder);
            code = errorHolder.value;
            message = getDetails(errorInfo, null);
            break;
        default:
            message = "Unable to get extra error information due to internal error.";
            code = DDS.RETCODE_ERROR.value;
        }

        switch (code) {
        case DDS.RETCODE_PRECONDITION_NOT_MET.value:
            throw new PreconditionNotMetExceptionImpl(environment, message);
        case DDS.RETCODE_OUT_OF_RESOURCES.value:
            throw new OutOfResourcesExceptionImpl(environment, message);
        case DDS.RETCODE_ALREADY_DELETED.value:
            throw new AlreadyClosedExceptionImpl(environment, message);
        case DDS.RETCODE_BAD_PARAMETER.value:
            throw new IllegalArgumentExceptionImpl(environment, message);
        case DDS.RETCODE_ILLEGAL_OPERATION.value:
            throw new IllegalOperationExceptionImpl(environment, message);
        case DDS.RETCODE_IMMUTABLE_POLICY.value:
            throw new ImmutablePolicyExceptionImpl(environment, message);
        case DDS.RETCODE_INCONSISTENT_POLICY.value:
            throw new InconsistentPolicyExceptionImpl(environment, message);
        case DDS.RETCODE_NOT_ENABLED.value:
            throw new NotEnabledExceptionImpl(environment, message);
        case DDS.RETCODE_UNSUPPORTED.value:
            throw new UnsupportedOperationExceptionImpl(environment, message);
        case DDS.RETCODE_ERROR.value:
        default:
            throw new DDSExceptionImpl(environment, message);

        }

    }

    public static DDS.Duration_t convert(OsplServiceEnvironment environment,
            Duration d) {
        if (d == null) {
            throw new IllegalArgumentExceptionImpl(environment,
                    "Illegal Duration provided (null).");
        }
        try {
            return ((DurationImpl) d).convert();
        } catch (ClassCastException e) {
            throw new IllegalArgumentExceptionImpl(environment,
                    "Usage of non-OpenSplice Duration implementation is not supported.");
        }
    }

    public static Duration convert(OsplServiceEnvironment env, DDS.Duration_t d) {
        return new DurationImpl(env, d.sec, d.nanosec);
    }

    public static long convert(OsplServiceEnvironment environment,
            InstanceHandle h) {
        if (h == null) {
            throw new IllegalArgumentExceptionImpl(environment,
                    "Illegal InstanceHandle provided (null).");
        }
        try {
            return ((InstanceHandleImpl) h).getValue();
        } catch (ClassCastException e) {
            throw new IllegalArgumentExceptionImpl(environment,
                    "Usage of non-OpenSplice InstanceHandle implementation is not supported.");
        }
    }

    public static InstanceHandle convert(OsplServiceEnvironment env, long handle) {
        return new InstanceHandleImpl(env, handle);
    }

    public static DDS.Time_t convert(OsplServiceEnvironment environment, Time t) {
        if (t == null) {
            throw new IllegalArgumentExceptionImpl(environment,
                    "Illegal Time provided (null).");
        }
        try {
            return ((ModifiableTimeImpl) t).convert();
        } catch (ClassCastException e) {
            throw new IllegalArgumentExceptionImpl(environment,
                    "Usage of non-OpenSplice Time implementation is not supported.");
        }
    }

    public static Time convert(OsplServiceEnvironment env, DDS.Time_t t) {
        return new TimeImpl(env, t.sec, t.nanosec);
    }
}
