/*
 *  ******************************************************************************
 *  *
 *  *
 *  * This program and the accompanying materials are made available under the
 *  * terms of the Apache License, Version 2.0 which is available at
 *  * https://www.apache.org/licenses/LICENSE-2.0.
 *  *
 *  *  See the NOTICE file distributed with this work for additional
 *  *  information regarding copyright ownership.
 *  * 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.
 *  *
 *  * SPDX-License-Identifier: Apache-2.0
 *  *****************************************************************************
 */

package org.nd4j.jita.allocator.pointers.cuda;

import lombok.Getter;
import lombok.Setter;
import lombok.val;
import org.bytedeco.javacpp.Pointer;
import org.nd4j.jita.allocator.pointers.CudaPointer;
import org.nd4j.linalg.exception.ND4JException;
import org.nd4j.linalg.factory.Nd4j;
import org.nd4j.nativeblas.NativeOps;
import org.nd4j.nativeblas.NativeOpsHolder;

/**
 * @author raver119@gmail.com
 */
public class cudaEvent_t extends CudaPointer {

    private boolean destroyed = false;

    @Getter
    @Setter
    private long clock;

    @Getter
    @Setter
    private int laneId;

    @Getter
    @Setter
    private int deviceId;

    public cudaEvent_t(Pointer pointer) {
        super(pointer);
    }

    public synchronized boolean isDestroyed() {
        return destroyed;
    }

    public synchronized void markDestroyed() {
        destroyed = true;
    }

    public void destroy() {
        if (!isDestroyed()) {
            NativeOpsHolder.getInstance().getDeviceNativeOps().destroyEvent(this);
            markDestroyed();
        }
    }

    public void synchronize() {
        if (!isDestroyed()) {
            int res = NativeOpsHolder.getInstance().getDeviceNativeOps().eventSynchronize(this);
            if (res == 0)
                throw new ND4JException("CUDA exception happened. Terminating. Last op: [" + Nd4j.getExecutioner().getLastOp() +"]");

            val code = NativeOpsHolder.getInstance().getDeviceNativeOps().lastErrorCode();
            if (code != 0)
                throw new RuntimeException(NativeOpsHolder.getInstance().getDeviceNativeOps().lastErrorMessage() + "; Error code: " + code);
        }
    }

    public void register(cudaStream_t stream) {
        if (!isDestroyed()) {
            int res = NativeOpsHolder.getInstance().getDeviceNativeOps().registerEvent(this, stream);

            val code = NativeOpsHolder.getInstance().getDeviceNativeOps().lastErrorCode();
            if (code != 0)
                throw new RuntimeException(NativeOpsHolder.getInstance().getDeviceNativeOps().lastErrorMessage() + "; Error code: " + code);
        }
    }
}
