/*
 * This file is modified by Ivan Maidanski <ivmai@ivmaisoft.com>
 * Project name: JCGO-SUNAWT (http://www.ivmaisoft.com/jcgo/)
 */

/*
 * @(#)MFramePeer.java  1.100 03/01/23
 *
 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package sun.awt.motif;

import java.util.Vector;
import java.awt.*;
import java.awt.peer.*;
import java.awt.event.*;
import sun.awt.motif.X11InputMethod;
import sun.awt.motif.MInputMethodControl;
import sun.awt.im.*;
import java.awt.image.ColorModel;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferUShort;
import java.awt.image.ImageObserver;
import java.awt.image.WritableRaster;
import sun.awt.image.ImageRepresentation;

class MFramePeer extends MWindowPeer implements FramePeer, MInputMethodControl {
    static Vector allFrames = new Vector();

    // XXX: Stub out for now.  Need to propagate to normal size hints.
    public void setMaximizedBounds(Rectangle b) {}

    public void create(MComponentPeer parent, Object arg) {
        super.create( parent );
    }

    MFramePeer(Frame target) {
        super();
        // set the window attributes for this Frame
        winAttr.nativeDecor = !target.isUndecorated();
        winAttr.initialFocus = true;
        winAttr.isResizable =  target.isResizable();
        winAttr.initialState = target.getState();
        winAttr.title = target.getTitle();
        winAttr.icon = target.getIconImage();
        if (winAttr.nativeDecor) {
            winAttr.decorations = winAttr.AWT_DECOR_ALL;
        } else {
            winAttr.decorations = winAttr.AWT_DECOR_NONE;
        }

        // for input method windows, use minimal decorations
        if (target instanceof InputMethodWindow) {
            winAttr.initialFocus = false;
            winAttr.decorations = (winAttr.AWT_DECOR_TITLE | winAttr.AWT_DECOR_BORDER);
        }

        // create and init native component
        init( target);
    if (winAttr.icon != null) {
        setIconImage(winAttr.icon);
    }
        allFrames.addElement(this);
    }

    public void setTitle(String title) {
        pSetTitle(title);
    }

    protected void disposeImpl() {
        allFrames.removeElement(this);
        super.disposeImpl();
    }

    public void setMenuBar(MenuBar mb) {
        MMenuBarPeer mbpeer = (MMenuBarPeer) MToolkit.targetToPeer(mb);
        pSetMenuBar(mbpeer);

        Rectangle r = target.bounds();

        pReshape(r.x, r.y, r.width, r.height);
        if (target.isVisible()) {
            target.validate();
        }
    }

    public void setIconImage(Image im) {
        int width;
        int height;
        GraphicsConfiguration defaultGC;
        if (im != null) {  // 4633887  Avoid Null pointer exception.
        if (im instanceof X11Image) {
            ImageRepresentation ir = ((X11Image)im).getImageRep();
            ir.reconstruct(ImageObserver.ALLBITS);
            width = ir.getWidth();
            height = ir.getHeight();
        }
        else {
            width = im.getWidth(null);
            height = im.getHeight(null);
        }
        if (pGetIconSize(width, height)) {
            //Icons are displayed using the default visual, so create image
            //using default GraphicsConfiguration
            defaultGC = getGraphicsConfiguration().getDevice().
                getDefaultConfiguration();
            ColorModel model = defaultGC.getColorModel();
            WritableRaster raster =
                model.createCompatibleWritableRaster(iconWidth, iconHeight);
            Image image = new BufferedImage(model, raster,
                                            model.isAlphaPremultiplied(),
                                            null);

            // ARGB BufferedImage to hunt for transparent pixels
            BufferedImage bimage =
                new BufferedImage(iconWidth, iconHeight,
                                  BufferedImage.TYPE_INT_ARGB);
            ColorModel alphaCheck = bimage.getColorModel();
            Graphics g = image.getGraphics();
            Graphics big = bimage.getGraphics();
            try {
                g.drawImage(im, 0, 0, iconWidth, iconHeight, null);
                big.drawImage(im, 0, 0, iconWidth, iconHeight, null);
            } finally {
                g.dispose();
                big.dispose();
            }

            DataBuffer db = ((BufferedImage)image).getRaster().getDataBuffer();
            DataBuffer bidb = bimage.getRaster().getDataBuffer();
            byte[] bytedata = null;
            int[] intdata = null;
            int bidbLen = bidb.getSize();
            int imgDataIdx;
            //Get native RGB value for window background color
            //Should work for byte as well as int
            int bgRGB = getNativeColor(SystemColor.window, defaultGC);

            /* My first attempt at a solution to bug 4175560 was to use
             * the iconMask and iconPixmap attributes of Windows.
             * This worked fine on CDE/dtwm, however olwm displayed only
             * single color icons (white on background).  Instead, the
             * fix gets the default background window color and replaces
             * transparent pixels in the icon image with this color.  This
             * solutions works well with dtwm as well as olwm.
             */

            for (imgDataIdx = 0; imgDataIdx < bidbLen; imgDataIdx++) {
                if (alphaCheck.getAlpha(bidb.getElem(imgDataIdx)) == 0 ) {
                    //Assuming single data bank
                    db.setElem(imgDataIdx, bgRGB);
                }
            }
            short[] ushortdata = null;
            if (db instanceof DataBufferByte) {
                // Pseudocolor data
                bytedata = ((DataBufferByte)db).getData();
            }
            else if (db instanceof DataBufferInt) {
                // Truecolor data
                intdata = ((DataBufferInt) db).getData();
            }
            else if (db instanceof DataBufferUShort) {
                // Truecolor data
                ushortdata = ((DataBufferUShort) db).getData();
            }
               pSetIconImage(bytedata, intdata, ushortdata,
                          iconWidth, iconHeight);
        }
        }
    }

    native boolean pGetIconSize(int widthHint, int heightHint);

    // [jk] added ushortData for 16-bpp displays
    native void pSetIconImage(byte[] byteData,
                              int[] intData,
                              short[] ushortData,
                              int iconWidth, int iconHeight);
    private native void pSetIconImage(boolean dummy);

    // NOTE: This method may be called by privileged threads.
    //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
    public void handleIconify() {
        postEvent(new WindowEvent((Window)target, WindowEvent.WINDOW_ICONIFIED));
    }

    // NOTE: This method may be called by privileged threads.
    //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
    public void handleDeiconify() {
        postEvent(new WindowEvent((Window)target, WindowEvent.WINDOW_DEICONIFIED));
    }


    /**
     * Called to inform the Frame that it has moved.
     */
    // NOTE: This method may be called by privileged threads.
    //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
    public void handleMoved(int x, int y) {
        postEvent(new ComponentEvent(target, ComponentEvent.COMPONENT_MOVED));
    }

    static final int CROSSHAIR_INSET = 5;

    static final int BUTTON_Y = CROSSHAIR_INSET + 1;
    static final int BUTTON_W = 17;
    static final int BUTTON_H = 17;

    static final int SYS_MENU_X = CROSSHAIR_INSET + 1;
    static final int SYS_MENU_CONTAINED_X = SYS_MENU_X + 5;
    static final int SYS_MENU_CONTAINED_Y = BUTTON_Y + 7;
    static final int SYS_MENU_CONTAINED_W = 8;
    static final int SYS_MENU_CONTAINED_H = 3;

    static final int MAXIMIZE_X_DIFF = CROSSHAIR_INSET + BUTTON_W;
    static final int MAXIMIZE_CONTAINED_X_DIFF = MAXIMIZE_X_DIFF - 5;
    static final int MAXIMIZE_CONTAINED_Y = BUTTON_Y + 5;
    static final int MAXIMIZE_CONTAINED_W = 8;
    static final int MAXIMIZE_CONTAINED_H = 8;

    static final int MINIMIZE_X_DIFF = MAXIMIZE_X_DIFF + BUTTON_W;
    static final int MINIMIZE_CONTAINED_X_DIFF = MINIMIZE_X_DIFF - 7;
    static final int MINIMIZE_CONTAINED_Y = BUTTON_Y + 7;
    static final int MINIMIZE_CONTAINED_W = 3;
    static final int MINIMIZE_CONTAINED_H = 3;

    static final int TITLE_X = SYS_MENU_X + BUTTON_W;
    static final int TITLE_W_DIFF = BUTTON_W * 3 + CROSSHAIR_INSET * 2 - 1;
    static final int TITLE_MID_Y = BUTTON_Y + (BUTTON_H / 2);

    static final int MENUBAR_X = CROSSHAIR_INSET + 1;
    static final int MENUBAR_Y = BUTTON_Y + BUTTON_H;

    static final int HORIZ_RESIZE_INSET = CROSSHAIR_INSET + BUTTON_H;
    static final int VERT_RESIZE_INSET = CROSSHAIR_INSET + BUTTON_W;


    /*
     * Print the native component by rendering the Motif look ourselves.
     * We also explicitly print the MenuBar since a MenuBar isn't a subclass
     * of Component (and thus it has no "print" method which gets called by
     * default).
     */
    public void print(Graphics g) {
        super.print(g);

        Frame f = (Frame)target;
        Insets finsets = f.getInsets();
        Dimension fsize = f.getSize();

        Color bg = f.getBackground();
        Color fg = f.getForeground();
        Color highlight = bg.brighter();
        Color shadow = bg.darker();

        // Well, we could query for the currently running window manager
        // and base the look on that, or we could just always do dtwm.
        // aim, tball, and levenson all agree we'll just do dtwm.

        if (hasDecorations(MWindowAttributes.AWT_DECOR_BORDER)) {

            // top outer -- because we'll most likely be drawing on white paper,
            // for aesthetic reasons, don't make any part of the outer border
            // pure white
            if (highlight.equals(Color.white)) {
                g.setColor(new Color(230, 230, 230));
            }
            else {
                g.setColor(highlight);
            }
            g.drawLine(0, 0, fsize.width, 0);
            g.drawLine(0, 1, fsize.width - 1, 1);

            // left outer
            // if (highlight.equals(Color.white)) {
            //     g.setColor(new Color(230, 230, 230));
            // }
            // else {
            //     g.setColor(highlight);
            // }
            g.drawLine(0, 0, 0, fsize.height);
            g.drawLine(1, 0, 1, fsize.height - 1);

            // bottom cross-hair
            g.setColor(highlight);
            g.drawLine(CROSSHAIR_INSET + 1, fsize.height - CROSSHAIR_INSET,
                       fsize.width - CROSSHAIR_INSET,
                       fsize.height - CROSSHAIR_INSET);

            // right cross-hair
            // g.setColor(highlight);
            g.drawLine(fsize.width - CROSSHAIR_INSET, CROSSHAIR_INSET + 1,
                       fsize.width - CROSSHAIR_INSET,
                       fsize.height - CROSSHAIR_INSET);

            // bottom outer
            g.setColor(shadow);
            g.drawLine(1, fsize.height, fsize.width, fsize.height);
            g.drawLine(2, fsize.height - 1, fsize.width, fsize.height - 1);

            // right outer
            // g.setColor(shadow);
            g.drawLine(fsize.width, 1, fsize.width, fsize.height);
            g.drawLine(fsize.width - 1, 2, fsize.width - 1, fsize.height);

            // top cross-hair
            // g.setColor(shadow);
            g.drawLine(CROSSHAIR_INSET, CROSSHAIR_INSET,
                       fsize.width - CROSSHAIR_INSET, CROSSHAIR_INSET);

            // left cross-hair
            // g.setColor(shadow);
            g.drawLine(CROSSHAIR_INSET, CROSSHAIR_INSET, CROSSHAIR_INSET,
                   fsize.height - CROSSHAIR_INSET);
        }

        if (hasDecorations(MWindowAttributes.AWT_DECOR_TITLE)) {

            if (hasDecorations(MWindowAttributes.AWT_DECOR_MENU)) {

                // system menu
                g.setColor(bg);
                g.fill3DRect(SYS_MENU_X, BUTTON_Y, BUTTON_W, BUTTON_H, true);
                g.fill3DRect(SYS_MENU_CONTAINED_X, SYS_MENU_CONTAINED_Y,
                             SYS_MENU_CONTAINED_W, SYS_MENU_CONTAINED_H, true);
            }

            // title bar
            // g.setColor(bg);
            g.fill3DRect(TITLE_X, BUTTON_Y, fsize.width - TITLE_W_DIFF, BUTTON_H,
                         true);

            if (hasDecorations(MWindowAttributes.AWT_DECOR_MINIMIZE)) {

                // minimize button
                // g.setColor(bg);
                g.fill3DRect(fsize.width - MINIMIZE_X_DIFF, BUTTON_Y, BUTTON_W,
                             BUTTON_H, true);
                g.fill3DRect(fsize.width - MINIMIZE_CONTAINED_X_DIFF,
                             MINIMIZE_CONTAINED_Y, MINIMIZE_CONTAINED_W,
                             MINIMIZE_CONTAINED_H, true);
            }

            if (hasDecorations(MWindowAttributes.AWT_DECOR_MAXIMIZE)) {

                // maximize button
                // g.setColor(bg);
                g.fill3DRect(fsize.width - MAXIMIZE_X_DIFF, BUTTON_Y, BUTTON_W,
                             BUTTON_H, true);
                g.fill3DRect(fsize.width - MAXIMIZE_CONTAINED_X_DIFF,
                             MAXIMIZE_CONTAINED_Y, MAXIMIZE_CONTAINED_W,
                             MAXIMIZE_CONTAINED_H, true);
            }

            // title bar text
            g.setColor(fg);
            Font sysfont = new Font("SansSerif", Font.PLAIN, 10);
            g.setFont(sysfont);
            FontMetrics sysfm = g.getFontMetrics();
            String ftitle = f.getTitle();
            g.drawString(ftitle,
                         ((TITLE_X + TITLE_X + fsize.width - TITLE_W_DIFF) / 2) -
                         (sysfm.stringWidth(ftitle) / 2),
                         TITLE_MID_Y + sysfm.getMaxDescent());
        }

        if (f.isResizable() &&
            hasDecorations(MWindowAttributes.AWT_DECOR_RESIZEH)) {

            // add resize cross hairs

            // upper-left horiz (shadow)
            g.setColor(shadow);
            g.drawLine(1, HORIZ_RESIZE_INSET, CROSSHAIR_INSET,
                       HORIZ_RESIZE_INSET);
            // upper-left vert (shadow)
            // g.setColor(shadow);
            g.drawLine(VERT_RESIZE_INSET, 1, VERT_RESIZE_INSET, CROSSHAIR_INSET);
            // upper-right horiz (shadow)
            // g.setColor(shadow);
            g.drawLine(fsize.width - CROSSHAIR_INSET + 1, HORIZ_RESIZE_INSET,
                       fsize.width, HORIZ_RESIZE_INSET);
            // upper-right vert (shadow)
            // g.setColor(shadow);
            g.drawLine(fsize.width - VERT_RESIZE_INSET - 1, 2,
                       fsize.width - VERT_RESIZE_INSET - 1, CROSSHAIR_INSET + 1);
            // lower-left horiz (shadow)
            // g.setColor(shadow);
            g.drawLine(1, fsize.height - HORIZ_RESIZE_INSET - 1,
                       CROSSHAIR_INSET, fsize.height - HORIZ_RESIZE_INSET - 1);
            // lower-left vert (shadow)
            // g.setColor(shadow);
            g.drawLine(VERT_RESIZE_INSET, fsize.height - CROSSHAIR_INSET + 1,
                       VERT_RESIZE_INSET, fsize.height);
            // lower-right horiz (shadow)
            // g.setColor(shadow);
            g.drawLine(fsize.width - CROSSHAIR_INSET + 1,
                       fsize.height - HORIZ_RESIZE_INSET - 1, fsize.width,
                       fsize.height - HORIZ_RESIZE_INSET - 1);
            // lower-right vert (shadow)
            // g.setColor(shadow);
            g.drawLine(fsize.width - VERT_RESIZE_INSET - 1,
                       fsize.height - CROSSHAIR_INSET + 1,
                       fsize.width - VERT_RESIZE_INSET - 1, fsize.height);

            // upper-left horiz (highlight)
            g.setColor(highlight);
            g.drawLine(2, HORIZ_RESIZE_INSET + 1, CROSSHAIR_INSET,
                       HORIZ_RESIZE_INSET + 1);
            // upper-left vert (highlight)
            // g.setColor(highlight);
            g.drawLine(VERT_RESIZE_INSET + 1, 2, VERT_RESIZE_INSET + 1,
                       CROSSHAIR_INSET);
            // upper-right horiz (highlight)
            // g.setColor(highlight);
            g.drawLine(fsize.width - CROSSHAIR_INSET + 1,
                       HORIZ_RESIZE_INSET + 1, fsize.width - 1,
                       HORIZ_RESIZE_INSET + 1);
            // upper-right vert (highlight)
            // g.setColor(highlight);
            g.drawLine(fsize.width - VERT_RESIZE_INSET, 2,
                       fsize.width - VERT_RESIZE_INSET, CROSSHAIR_INSET);
            // lower-left horiz (highlight)
            // g.setColor(highlight);
            g.drawLine(2, fsize.height - HORIZ_RESIZE_INSET, CROSSHAIR_INSET,
                       fsize.height - HORIZ_RESIZE_INSET);
            // lower-left vert (highlight)
            // g.setColor(highlight);
            g.drawLine(VERT_RESIZE_INSET + 1,
                       fsize.height - CROSSHAIR_INSET + 1,
                       VERT_RESIZE_INSET + 1, fsize.height - 1);
            // lower-right horiz (highlight)
            // g.setColor(highlight);
            g.drawLine(fsize.width - CROSSHAIR_INSET + 1,
                       fsize.height - HORIZ_RESIZE_INSET, fsize.width - 1,
                       fsize.height - HORIZ_RESIZE_INSET);
            // lower-right vert (highlight)
            // g.setColor(highlight);
            g.drawLine(fsize.width - VERT_RESIZE_INSET,
                       fsize.height - CROSSHAIR_INSET + 1,
                       fsize.width - VERT_RESIZE_INSET, fsize.height - 1);
        }

        MenuBar mb = f.getMenuBar();
        if (mb != null) {
            MMenuBarPeer peer = (MMenuBarPeer) MToolkit.targetToPeer(mb);
            if (peer != null) {
                Insets insets = getInsets();
                Graphics ng = g.create();
                int menubarX = 0;
                int menubarY = 0;
                if (hasDecorations(MWindowAttributes.AWT_DECOR_BORDER)) {
                    menubarX += CROSSHAIR_INSET + 1;
                    menubarY += CROSSHAIR_INSET + 1;
                }
                if (hasDecorations(MWindowAttributes.AWT_DECOR_TITLE)) {
                    menubarY += BUTTON_H;
                }
                try {
                    ng.translate(menubarX, menubarY);
                    peer.print(ng);
                } finally {
                    ng.dispose();
                }
            }
        }
    }

    // Saveunders are not done by Frame.
    void setSaveUnder(boolean state) {}

    /* Returns the native paint should be posted after setting new size
     */
    public boolean checkNativePaintOnSetBounds(int width, int height) {
        // Fix for 4418155. Undecorated Frame does not repaint
        // automticaly if shrinking. Should not wait for Expose
        return ((Frame)target).isUndecorated() ?
            ((width > oldWidth) || (height > oldHeight)):
            ((width != oldWidth) || (height != oldHeight));
    }

    final boolean isTargetUndecorated() {
        return ((Frame)target).isUndecorated();
 }
}
