/*
 * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.lwawt.macosx;

import java.awt.*;
import java.awt.image.BufferedImage;

@SuppressWarnings("serial") // JDK implementation class
public class CCustomCursor extends Cursor {
    static Dimension sMaxCursorSize;
    static Dimension getMaxCursorSize() {
        if (sMaxCursorSize != null) return sMaxCursorSize;
        final Rectangle bounds = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration().getBounds();
        return sMaxCursorSize = new Dimension(bounds.width / 2, bounds.height / 2);
    }

    Image fImage;
    Point fHotspot;
    int fWidth;
    int fHeight;

    public CCustomCursor(final Image cursor, final Point hotSpot, final String name) throws IndexOutOfBoundsException, HeadlessException {
        super(name);
        fImage = cursor;
        fHotspot = hotSpot;

        // This chunk of code is copied from sun.awt.CustomCursor
        final Toolkit toolkit = Toolkit.getDefaultToolkit();

        // Make sure image is fully loaded.
        final Component c = new Canvas(); // for its imageUpdate method
        final MediaTracker tracker = new MediaTracker(c);
        // MediaTracker loads resolution variants from MultiResolution Toolkit image
        tracker.addImage(fImage, 0);
        try {
            tracker.waitForAll();
        } catch (final InterruptedException e) {}

        int width = fImage.getWidth(c);
        int height = fImage.getHeight(c);

        // Fix for bug 4212593 The Toolkit.createCustomCursor does not
        // check absence of the image of cursor
        // If the image is invalid, the cursor will be hidden (made completely
        // transparent).
        if (tracker.isErrorAny() || width < 0 || height < 0) {
            fHotspot.x = fHotspot.y = 0;
            width = height = 1;
            fImage = createTransparentImage(width, height);
        } else {
            // Get the nearest supported cursor size
            final Dimension nativeSize = toolkit.getBestCursorSize(width, height);
            width = nativeSize.width;
            height = nativeSize.height;
        }

        fWidth = width;
        fHeight = height;

        // NOTE: this was removed for 3169146, but in 1.5 the JCK tests for an exception and fails if one isn't thrown.
        // See what JBuilder does.
        // Verify that the hotspot is within cursor bounds.
        if (fHotspot.x >= width || fHotspot.y >= height || fHotspot.x < 0 || fHotspot.y < 0) {
            throw new IndexOutOfBoundsException("invalid hotSpot");
        }

        // Must normalize the hotspot
        if (fHotspot.x >= width) {
            fHotspot.x = width - 1; // it is zero based.
        } else if (fHotspot.x < 0) {
            fHotspot.x = 0;
        }
        if (fHotspot.y >= height) {
            fHotspot.y = height - 1; // it is zero based.
        } else if (fHotspot.y < 0) {
            fHotspot.y = 0;
        }
    }

    private static BufferedImage createTransparentImage(int w, int h) {
        GraphicsEnvironment ge =
                GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gs = ge.getDefaultScreenDevice();
        GraphicsConfiguration gc = gs.getDefaultConfiguration();

        BufferedImage img = gc.createCompatibleImage(w, h, Transparency.BITMASK);
        Graphics2D g = (Graphics2D)img.getGraphics();
        g.setBackground(new Color(0, 0, 0, 0));
        g.clearRect(0, 0, w, h);
        g.dispose();

        return img;
    }

    public static Dimension getBestCursorSize(final int preferredWidth, final int preferredHeight) {
        // With Panther, cursors have no limit on their size. So give the client their
        // preferred size, but no larger than half the dimensions of the main screen
        // This will allow large cursors, but not cursors so large that they cover the
        // screen. Since solaris nor windows allow cursors this big, this shouldn't be
        // a limitation.
        // JCK triggers an overflow in the int -- if we get a bizarre value normalize it.
        final Dimension maxCursorSize = getMaxCursorSize();
        final Dimension d = new Dimension(Math.max(1, Math.abs(preferredWidth)), Math.max(1, Math.abs(preferredHeight)));
        return new Dimension(Math.min(d.width, maxCursorSize.width), Math.min(d.height, maxCursorSize.height));
    }

    // Called from native when the cursor is set
    CImage fCImage;
    long getImageData() {
        if (fCImage != null) {
            return fCImage.ptr;
        }

        try {
            fCImage = CImage.getCreator().createFromImage(fImage);
            if (fCImage == null) {
                // Something unexpected happened: CCustomCursor constructor
                // takes care of invalid cursor images, yet createFromImage()
                // failed to do its job. Return null to keep the cursor unchanged.
                return 0L;
            } else {
                fCImage.resizeRepresentations(fWidth, fHeight);
                return fCImage.ptr;
            }
        } catch (IllegalArgumentException iae) {
            // see comment above
            return 0L;
        }
    }

    Point getHotSpot() {
        return fHotspot;
    }
}