/**
 * Copyright 2013 JogAmp Community. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice, this list of
 *       conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above copyright notice, this list
 *       of conditions and the following disclaimer in the documentation and/or other materials
 *       provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are those of the
 * authors and should not be interpreted as representing official policies, either expressed
 * or implied, of JogAmp Community.
 */
package jogamp.newt;

import java.nio.ByteBuffer;

import com.jogamp.nativewindow.NativeWindowException;
import com.jogamp.nativewindow.util.DimensionImmutable;
import com.jogamp.nativewindow.util.PixelFormat;
import com.jogamp.nativewindow.util.PixelRectangle;
import com.jogamp.nativewindow.util.PointImmutable;

import com.jogamp.newt.Display;
import com.jogamp.newt.Display.PointerIcon;

public class PointerIconImpl implements PointerIcon {
    private final DisplayImpl display;
    private final PixelFormat pixelformat;
    private final DimensionImmutable size;
    private final ByteBuffer pixels;
    private final PointImmutable hotspot;
    private long handle;
    private int hashCode = 0;
    private volatile boolean hashCodeComputed = false;

    public PointerIconImpl(final DisplayImpl display, final PixelFormat pixelformat, final DimensionImmutable size, final ByteBuffer pixels, final PointImmutable hotspot, final long handle) {
        this.display = display;
        this.pixelformat = pixelformat;
        this.size = size;
        this.pixels = pixels;
        this.hotspot = hotspot;
        this.handle=handle;
        if( 0 == handle ) {
            throw new IllegalArgumentException("0 native handle: "+this);
        }
    }
    public PointerIconImpl(final DisplayImpl display, final PixelRectangle pixelrect, final PointImmutable hotspot, final long handle) {
        this.display = display;
        this.pixelformat = pixelrect.getPixelformat();
        this.size = pixelrect.getSize();
        this.pixels = pixelrect.getPixels();
        this.hotspot = hotspot;
        this.handle=handle;
        if( 0 == handle ) {
            throw new IllegalArgumentException("0 native handle: "+this);
        }
    }

    @Override
    public int hashCode() {
        if( !hashCodeComputed ) { // DBL CHECKED OK VOLATILE
            synchronized (this) {
                if( !hashCodeComputed ) {
                    // 31 * x == (x << 5) - x
                    int hash = 31 + display.getFQName().hashCode();
                    hash = ((hash << 5) - hash) + pixelformat.hashCode();
                    hash = ((hash << 5) - hash) + size.hashCode();
                    hash = ((hash << 5) - hash) + getStride();
                    hash = ((hash << 5) - hash) + ( isGLOriented() ? 1 : 0);
                    hash = ((hash << 5) - hash) + pixels.hashCode();
                    hashCode = ((hash << 5) - hash) + hotspot.hashCode();
                }
            }
        }
        return hashCode;
    }

    /**
     * @return the native handle of this pointer icon, maybe {@code null}
     */
    public synchronized final long getHandle() { return handle; }
    /**
     * @return the {@link #getHandle()} if not {@code null}, otherwise throws NativeWindowException
     * @throws NativeWindowException if {@link #getHandle()} is {@code null}
     */
    public synchronized final long validatedHandle() throws NativeWindowException  {
        if( 0 == handle ) {
            throw new NativeWindowException("PointerIconImpl has null handle: "+this);
        } else {
            return handle;
        }
    }
    @Override
    public final Display getDisplay() { return display; }
    @Override
    public final PixelFormat getPixelformat() { return pixelformat; }
    @Override
    public final ByteBuffer getPixels() { return pixels; }
    @Override
    public synchronized final boolean isValid() { return 0 != handle; }

    @Override
    public synchronized void destroy() {
        if(Display.DEBUG) {
            System.err.println("PointerIcon.destroy: "+this+", "+display+", "+Display.getThreadName());
        }
        if( 0 != handle ) { // early out
            synchronized(display.pointerIconList) {
                display.pointerIconList.remove(this);
            }
            display.runOnEDTIfAvail(false, new Runnable() {
                public void run() {
                    if( display.isNativeValidAsync() ) {
                        destroyOnEDT(display.getHandle());
                    }
                } } );
        }
    }

    /** assume execution on EDT */
    synchronized void destroyOnEDT(final long dpy) {
        final long h = handle;
        handle = 0;
        if( 0 != h ) { // avoid double free
            try {
                display.destroyPointerIconImpl(dpy, h);
            } catch (final Exception e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public final DimensionImmutable getSize() {
        return size;
    }
    @Override
    public final int getStride() {
        return size.getWidth() * pixelformat.comp.bytesPerPixel();
    }
    @Override
    public final boolean isGLOriented() {
        return false;
    }
    @Override
    public final PointImmutable getHotspot() {
        return hotspot;
    }
    @Override
    public final String toString() {
        return "PointerIcon[obj 0x"+Integer.toHexString(super.hashCode())+", "+display.getFQName()+", 0x"+Long.toHexString(handle)+", "+pixelformat+", "+size+", "+hotspot+", pixels "+pixels+"]";
    }
}