/*
 *     Orbit, a versatile image analysis software for biological image-based quantification.
 *     Copyright (C) 2009 - 2018 Idorsia Pharmaceuticals Ltd., Hegenheimermattweg 91, CH-4123 Allschwil, Switzerland.
 *
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     This program 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 for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package com.actelion.research.orbit.imageAnalysis.components;

import com.actelion.research.orbit.beans.RawDataFile;
import com.actelion.research.orbit.beans.RawMeta;
import com.actelion.research.orbit.exceptions.OrbitImageServletException;
import com.actelion.research.orbit.imageAnalysis.components.RecognitionFrame.Tools;
import com.actelion.research.orbit.imageAnalysis.dal.DALConfig;
import com.actelion.research.orbit.imageAnalysis.utils.OrbitUtils;
import com.actelion.research.orbit.imageAnalysis.utils.ScaleoutMode;
import com.actelion.research.orbit.imageAnalysis.utils.TiledImagePainter;
import com.actelion.research.orbit.imageAnalysis.utils.ViewPortScrollListener;
import com.actelion.research.orbit.utils.RawMetaFactoryFile;
import com.actelion.research.orbit.utils.RawUtilsCommon;
import com.sun.media.jai.codec.JPEGEncodeParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.media.jai.JAI;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.List;


public class ImageFrame extends JInternalFrame implements ComponentListener, PropertyChangeListener {

    public static final String STR_CHANNELS_META = "Linked Channels";
    public static final String SCALE_EVENT = "imageScale";
    public static final String SCALE_SET_EVENT = "imageScaleSet";
    public static final String SCALE_SET_EVENT_OUT = "imageScaleSetOut";
    public static final String IFRAME_SELECTED = "iFrameSelected";
    public final static String MOUSE_MOVED_ON_IMAGE = "iFrame_mouse_moved";
    public final static String IFRAME_CLOSING = "iFrame_closing";


    private static final long serialVersionUID = 1L;
    private Logger logger = LoggerFactory.getLogger(ImageFrame.class);
    public RecognitionFrame recognitionFrame = null;
    private volatile RenderGrid renderGrid = null;
    protected JSlider opacitySlider = null;
    private RawDataFile rdf = null; // only set if it stems from a rdf, otherwise null
    private boolean exclusive = false;
    private int mipLayer = 0;
    private ImageIcon icon = null;
    private boolean channelContributionsLoaded = false;


    public ImageFrame(Object imageStrOrUrl) throws OrbitImageServletException {
        this(new RecognitionFrame(imageStrOrUrl, true));
    }

    public ImageFrame(final RecognitionFrame recognitionFrame) {
        super(recognitionFrame.getPicName(),
                true, //resizable
                true, //closable
                true, //maximizable
                true);//iconifiable

        java.net.URL imgURL = this.getClass().getResource(OrbitImageAnalysis.LOGO_NAME);
        if (imgURL != null) {
            ImageIcon icon = new ImageIcon(imgURL);
            this.setFrameIcon(icon);
        }

        enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK |
                AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_WHEEL_EVENT_MASK);


        this.setDoubleBuffered(true);
        this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);

        recognitionFrame.setIgnoreRepaint(true);
        this.recognitionFrame = recognitionFrame;
        addPropertyChangeListener(this);

        opacitySlider = new JSliderOrbit(0, 100);
        opacitySlider.setToolTipText("adjust classification opacity");
        opacitySlider.setEnabled(false);
        opacitySlider.setValue(0);
        opacitySlider.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                JSlider slider = (JSlider) e.getSource();
                ImageFrame.this.recognitionFrame.setOpacity(slider.getValue() / 100f);
                if (!opacitySlider.getValueIsAdjusting()) {
                    ImageFrame.this.recognitionFrame.prepareBuffer();
                    ImageFrame.this.recognitionFrame.repaint();
                }
            }

            ;
        });
        add(recognitionFrame);
        add(opacitySlider, BorderLayout.SOUTH);


        ViewPortScrollListener viewPortScroller = new ViewPortScrollListener(recognitionFrame, this);
        recognitionFrame.addMouseListener(viewPortScroller);
        recognitionFrame.addMouseMotionListener(viewPortScroller);
        try {
            recognitionFrame.addMouseWheelListener(viewPortScroller);  // >= Java 1.6
        } catch (Exception e) {
            System.out.println("Warning: MouseWheelListener not supported -> please upgrade to Java >= 1.6!");
        }

        // add "pan with space" key adapter (allows panning while in drawing mode)
        addKeyListener(new KeyAdapter() {
            boolean dragging = false;
            Tools oldTool = recognitionFrame.getSelectedTool();
            Cursor oldCursor = recognitionFrame.getCursor();

            @Override
            public void keyPressed(KeyEvent e) {
                if (dragging) return;
                if (e.getKeyCode() == KeyEvent.VK_SPACE) {
                    if (recognitionFrame.getMyListener() != null) {
                        recognitionFrame.getMyListener().mouseReleased(null);
                        Robot r = null;
                        try {
                            r = new Robot();
                            r.mouseRelease(InputEvent.BUTTON1_MASK);
                        } catch (AWTException e1) {
                            e1.printStackTrace();
                        }
                    }
                    dragging = true;
                    oldTool = recognitionFrame.getSelectedTool();
                    oldCursor = recognitionFrame.getCursor();
                    recognitionFrame.setSelectedTool(Tools.finger);
                    recognitionFrame.setCursor(new Cursor(Cursor.HAND_CURSOR));
                }
            }

            @Override
            public void keyReleased(KeyEvent e) {
                if (dragging) {
                    recognitionFrame.setSelectedTool(oldTool);
                    recognitionFrame.setCursor(oldCursor);
                    dragging = false;
                }
            }
        });

        addComponentListener(this);

    }

    public synchronized ImageIcon getOrbitIcon() {
        if (icon == null) {
            icon = new ImageIcon(this.getClass().getResource(OrbitImageAnalysis.LOGO_NAME));
        }
        return icon;
    }


    /**
     * make firePropertyChange public
     */

    public void firePropertyChangeExt(String propertyName, Object oldValue, Object newValue) {
        firePropertyChange(propertyName, oldValue, newValue);
    }


    public void processMouseWheelEvent(MouseWheelEvent e) {
        if (!acceptTool(recognitionFrame.getSelectedTool())) return;
        firePropertyChange(ImageFrame.SCALE_EVENT, null, new Double(e.getWheelRotation()));
    }

    private boolean acceptTool(Tools tool) {
        return tool.equals(Tools.finger);
    }


    /**
     * The preferred way to set the viewPort position. It calls recognitionFrame.setViewPortOffset followed
     * by a adjustViewport(). As result the image and the renderGrid will be adjusted.
     *
     * @param vpPos
     */
    public void setViewPortPositionAndAdjust(Point2D vpPos) {
        recognitionFrame.setViewPortOffset(vpPos);
        adjustViewport(false);
    }

    public void setViewPortPosAndSize(Point position, Dimension size) {
        recognitionFrame.setViewPortOffset(position);
        recognitionFrame.setViewPortSize(size);
    }

    public void updateRenderGrid() {
        if (renderGrid == null) return;
        if (!(exclusive || isSelected())) return;
        renderGrid.setFullImage(recognitionFrame.bimg);
        renderGrid.setViewportSize(getSize());
        renderGrid.setImageSize(new Dimension(recognitionFrame.bimg.getWidth(), recognitionFrame.bimg.getHeight())); // scale?
        renderGrid.setViewportPosition(recognitionFrame.getViewPortOffset()); // update 0.80g
        renderGrid.setImage(null);
        renderGrid.setScale(recognitionFrame.getScale() / 100d);
        renderGrid.repaint();
    }

    /**
     * stores the current viewport as an image file. Executes a fileChooser for choosing the file and
     * displays a JOptionPane if successful.
     */
    public void saveCurrentView() {
        JFileChooser fc = new JFileChooser();
        FileNameExtensionFilter filter = new FileNameExtensionFilter("*.jpg", "jpg");
        fc.setFileFilter(filter);
        String snapShotFileName = recognitionFrame.getPicName();
        snapShotFileName = snapShotFileName.replaceAll(RawUtilsCommon.getExtension(snapShotFileName, false), "snapshot.jpg");
        fc.setSelectedFile(new File(snapShotFileName));
        int returnVal = fc.showSaveDialog(this);
        String fn = null;
        if (returnVal == JFileChooser.APPROVE_OPTION) {
            fn = fc.getSelectedFile().getAbsolutePath();
            if (!fn.toLowerCase().endsWith(".jpg")) fn += ".jpg";
        }
        fc = null;
        this.repaint();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
        if (fn != null) {
            BufferedImage bi;
            Robot robot;
            try {
                Rectangle r = recognitionFrame.getBounds();
                r.x += recognitionFrame.getLocationOnScreen().x;
                r.y += recognitionFrame.getLocationOnScreen().y;
                robot = new Robot();
                bi = robot.createScreenCapture(r);
                JPEGEncodeParam jpgParam = new JPEGEncodeParam();
                jpgParam.setQuality(0.85f);
                JAI.create("filestore", bi, fn, "JPEG", jpgParam);
                System.gc(); // forces JAI to close the filehandle
                logger.debug("finished writing");
                JOptionPane.showMessageDialog(this, "Screenshot successfully saved to " + fn, "Screenshot saved", JOptionPane.INFORMATION_MESSAGE);
            } catch (AWTException e) {
                logger.error("error saving screenshot", e);
            }
        }
    }


    /**
     * get the current viewport as an image
     */
    public BufferedImage getCurrentView() {
        int w = getWidth();
        int h = getHeight();
        BufferedImage bi;
        Robot robot;
        try {
            Rectangle r = recognitionFrame.getBounds();
            r.x += recognitionFrame.getLocationOnScreen().x;
            r.y += recognitionFrame.getLocationOnScreen().y;
            robot = new Robot();
            bi = robot.createScreenCapture(r);
            return bi;
        } catch (AWTException e) {
            logger.error("error getting viewport", e);
            return null;
        }
    }


    public synchronized void adjustViewport() {
        adjustViewport(false);
    }

    private synchronized void adjustViewport(boolean fromMouse) {
        double scOld = recognitionFrame.getOldScale() / 100d;
        double scNew = recognitionFrame.getScale() / 100d;
        if (scOld < 0.00001d) return; // avoid diff by 0 later

        Dimension frameSize = getSize();
        recognitionFrame.setViewPortSize(recognitionFrame.getSize());
        Point2D offs = recognitionFrame.getViewPortOffset();

        // "centralized zooming"
        double centerX = frameSize.width / 2d;
        double centerY = frameSize.height / 2d;

        Point2D mousePos = getMousePosition(); // returns null if mouse cursor is not over component
        double imgW = (int) (recognitionFrame.bimg.getWidth() * scOld);
        double imgH = (int) (recognitionFrame.bimg.getHeight() * scOld);
        Rectangle2D viewPortRect = new Rectangle2D.Double(-offs.getX(), -offs.getY(), imgW, imgH);
        if (fromMouse && mousePos != null && viewPortRect.contains(mousePos)) { // if available, center the zooming to the mouse position
            centerX = mousePos.getX();
            centerY = mousePos.getY();
        }

        double x = offs.getX() + centerX;
        double y = offs.getY() + centerY;
        x *= (scNew / scOld);
        y *= (scNew / scOld);
        x -= centerX;
        y -= centerY;

        offs.setLocation(x, y);

        // don't let the image out of the viewport
        final double border = 20;
        double ox = offs.getX();
        double oy = offs.getY();
        if (fromMouse) {
            if (-offs.getX() + imgW - border < 0) ox = imgW - border;
            if (-offs.getY() + imgH - border < 0) oy = imgH - border;
            if (-offs.getX() + border > recognitionFrame.getWidth()) ox = -recognitionFrame.getWidth() + border;
            if (-offs.getY() + border > recognitionFrame.getHeight()) oy = -recognitionFrame.getHeight() + border;
        }
        offs.setLocation(ox, oy);

        recognitionFrame.setOldScale(scNew * 100d);    // version 1.91
        recognitionFrame.setViewPortOffset(new Point2D.Double(offs.getX(), offs.getY()));
        recognitionFrame.repaint();

        if ((exclusive || isSelected()) && renderGrid != null) {
            renderGrid.setScale(recognitionFrame.getScale() / 100d);
            //this.propertyChange(new PropertyChangeEvent(this, SCALE_SET_EVENT_OUT, null, new Integer((int)recognitionFrame.getScale())));
            renderGrid.setViewportPosition(recognitionFrame.getViewPortOffset());
            renderGrid.repaint();
        }

    }


    public void propertyChange(PropertyChangeEvent evt) {
        final double minScale = 0.2d;


        if (evt.getPropertyName().equals("closed")) {
            logger.trace("closed imageframe: " + title);
            if (recognitionFrame.getRenderThreadOriginal() != null) {
                recognitionFrame.getRenderThreadOriginal().setBufferReady(true);
                recognitionFrame.getRenderThreadOriginal().setBufferRendering(false);
                recognitionFrame.getRenderThreadOriginal().setScheduleUpdate(false);
                recognitionFrame.getRenderThreadOriginal().removePropertyChangeListener(recognitionFrame);
                recognitionFrame.getRenderThreadOriginal().interrupt();
                recognitionFrame.getRenderThreadOriginal().stop();
            }
            try {
                recognitionFrame.bimg.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            removePropertyChangeListener(this);
            firePropertyChange(IFRAME_CLOSING, null, this);
        }


        OrbitImageAnalysis OIA = OrbitImageAnalysis.getInstance();
        boolean inSync = OIA.isSyncFrames();
        if ((exclusive || isSelected()) && evt.getPropertyName().equals(SCALE_EVENT)) {     // scroll wheel zooming
            recognitionFrame.setOldScale(recognitionFrame.getScale());
            double newScale = recognitionFrame.getScale();
            double factor = -OrbitUtils.log2(recognitionFrame.getScale() + 1d) - 1;
            factor /= 1d / (OrbitUtils.log2(newScale + 1d) / 10d);
            newScale = Math.max(minScale, recognitionFrame.getScale() + ((Double) evt.getNewValue()) * factor);

            // ensure minWidth of image
            double minWidth = 100d;
            double scaledWidth = (newScale / 100d) * recognitionFrame.bimg.getImage().getWidth();
            if (scaledWidth < minWidth)
                newScale = (minWidth / (double) recognitionFrame.bimg.getImage().getWidth()) * 100d;
            newScale = Math.min(500d, newScale); // TODO: get maxRange from scaleSlider!
            recognitionFrame.setScale(newScale);
            adjustViewport(true);
            if ((exclusive || isSelected()))
                firePropertyChange(SCALE_SET_EVENT_OUT, null, recognitionFrame.getScale());

        } else if ((exclusive || inSync || isSelected()) && evt.getPropertyName().equals(SCALE_SET_EVENT))     // from zoom slider
        {  // called for all iFrames when new image is opened  or scale slider moved
            double oldScale = recognitionFrame.getScale();
            double newScale = Math.max(minScale, (Double) evt.getNewValue());
            recognitionFrame.setOldScale(oldScale);
            recognitionFrame.setScale(newScale);
            if ((exclusive || isSelected())) adjustViewport(false);
            //	if ((exclusive||isSelected()))  firePropertyChange(SCALE_SET_EVENT_OUT, null, (Double)recognitionFrame.getScale());
        } else if (evt.getPropertyName().equals("selected") && evt.getNewValue().equals(true) && evt.getSource().equals(this)) {    // imageFrame selected
            updateRenderGrid();
            if (rdf != null) { // it stems from a rdf, so load meta data
                firePropertyChange(ImageList.PROPERTY_DISPLAY_META, null, rdf);
            }
            // update scaleSlider
            if ((exclusive || isSelected()))
                firePropertyChange(SCALE_SET_EVENT_OUT, null, recognitionFrame.getScale());
            firePropertyChange(IFRAME_SELECTED, null, this);
        }

    }


    public boolean saveChannels() {
        OrbitImageAnalysis.getInstance().forceLogin();
        if (OrbitImageAnalysis.loginOk) {
            try {
                String vals = channelsToString();
                List<RawMeta> rms = DALConfig.getImageProvider().LoadRawMetasByRawDataFileAndName(getRdf().getRawDataFileId(), STR_CHANNELS_META);
                if (rms != null && rms.size() > 0) { // update
                    RawMeta rm = rms.get(0);
                    rm.setModifyDate(new Date());
                    rm.setUserId(OrbitImageAnalysis.loginUser);
                    rm.setValue(vals);
                    if (DALConfig.getImageProvider().UpdateRawMeta(rm) && vals != null && vals.length() > 0) {
                        rdf.setFlagBit(RawDataFile.Flag_HAS_LINKED_CHANNELS);
                        DALConfig.getImageProvider().UpdateRawDataFile(rdf);
                    }
                    logger.debug("Channels updated in database. RawMeta: " + rm);
                } else { // insert
                    RawMetaFactoryFile rmff = new RawMetaFactoryFile(getRdf().getRawDataFileId(), new Date(), OrbitImageAnalysis.loginUser);
                    RawMeta rm = rmff.createMetaStr(STR_CHANNELS_META, vals);
                    if (DALConfig.getImageProvider().InsertRawMeta(rm) > 0) {
                        // successful, so set flag in rdf
                        rdf.setFlagBit(RawDataFile.Flag_HAS_LINKED_CHANNELS);
                        DALConfig.getImageProvider().UpdateRawDataFile(rdf);
                    }
                    logger.debug("Channels saved to database. RawMeta: " + rm);
                }
                JOptionPane.showMessageDialog(this, "Channels successfully saved.", "Channels Saved", JOptionPane.INFORMATION_MESSAGE);
                return true;
            } catch (Exception e) {
                logger.error("cannot insert or update raw meta into database", e);
                return false;
            }

        } else {
            logger.debug("Channels not saved because Orbit login is not ok or canceled.");
            return false;
        }
    }

    public boolean removeChannels() {
        OrbitImageAnalysis.getInstance().forceLogin();
        if (OrbitImageAnalysis.loginOk) {
            try {
                //String vals = channelsToString();
                List<RawMeta> rms = DALConfig.getImageProvider().LoadRawMetasByRawDataFileAndName(getRdf().getRawDataFileId(), STR_CHANNELS_META);
                if (rms != null && rms.size() > 0) {
                    for (RawMeta rm : rms) {
                        if (DALConfig.getImageProvider().DeleteRawMeta(rm.getRawMetaId())) {
                            logger.debug("RawMeta deleted: " + rm.getRawMetaId());
                            // successful, so unset has linked channels flag
                            rdf.unsetFlagBit(RawDataFile.Flag_HAS_LINKED_CHANNELS);
                            DALConfig.getImageProvider().UpdateRawDataFile(rdf);
                        }
                    }
                }
                // unset channels
                recognitionFrame.bimg.setRedChannel(null);
                recognitionFrame.bimg.setGreenChannel(null);
                recognitionFrame.bimg.setBlueChannel(null);
                recognitionFrame.bimg.setRedChannelRdfId(0);
                recognitionFrame.bimg.setGreenChannelRdfId(0);
                recognitionFrame.bimg.setBlueChannelRdfId(0);
                repaint();
                JOptionPane.showMessageDialog(this, "Channels successfully removed.", "Channels Removed", JOptionPane.INFORMATION_MESSAGE);
                return true;
            } catch (Exception e) {
                logger.error("cannot remove raw meta from database", e);
                return false;
            }
        } else {
            logger.debug("Channels not removed because Orbit login is not ok or canceled.");
            return false;
        }
    }

    public void loadChannels() {
        if (rdf == null) return;
        try {
            List<RawMeta> channelMeta = DALConfig.getImageProvider().LoadRawMetasByRawDataFileAndName(getRdf().getRawDataFileId(), STR_CHANNELS_META);
            if ((!ScaleoutMode.SCALEOUTMODE.get()) && channelMeta != null && channelMeta.size() > 0) {
                if (JOptionPane.showConfirmDialog(this,
                        "Linked channels (red/green/blue) found. Do you want to load and link the channels?",
                        "Load Linked Channels?", JOptionPane.YES_NO_OPTION)
                        == JOptionPane.YES_OPTION) {
                    String[] vals = channelMeta.get(0).getValue().split(",");
                    if (vals != null && vals.length >= 3) {
                        for (String val : vals) {
                            String[] split = val.split(":");
                            String channel = split[0];
                            int rdfId = Integer.parseInt(split[1]);
                            if (rdfId > 0) {
                                loadChannel(rdfId, channel);
                                repaint();
                            }
                        }
                    }
                } // really load?
            }
        } catch (Exception e) {
            logger.error("error loading channel meta data", e);
        }
    }


    private void loadChannel(int rdfId, String channel) {
        try {
            RawDataFile rdf = DALConfig.getImageProvider().LoadRawDataFile(rdfId);
            String ch = "red";
            if (channel.equalsIgnoreCase("g")) ch = "green";
            if (channel.equalsIgnoreCase("b")) ch = "blue";
            if (channel.equalsIgnoreCase("o")) ch = "overlay";
            OrbitImageAnalysis.getInstance().loadFile(rdf, rdf.getFileName() + " [" + ch + "]", false);
            RecognitionFrame rf = new RecognitionFrame(rdf, false);
            if (channel.equalsIgnoreCase("r")) {
                setTIPChannel(recognitionFrame.bimg, rdfId, rf.bimg, TiledImagePainter.CHANNEL_RED);
            } else if (channel.equalsIgnoreCase("g")) {
                setTIPChannel(recognitionFrame.bimg, rdfId, rf.bimg, TiledImagePainter.CHANNEL_GREEN);
            } else if (channel.equalsIgnoreCase("b")) {
                setTIPChannel(recognitionFrame.bimg, rdfId, rf.bimg, TiledImagePainter.CHANNEL_BLUE);
            } else if (channel.equalsIgnoreCase("o")) {
                setTIPChannel(recognitionFrame.bimg, rdfId, rf.bimg, TiledImagePainter.CHANNEL_OVERLAY);
            }
        } catch (Exception e) {
            logger.error("error loading channel", e);
        }
    }

    private void setTIPChannel(TiledImagePainter tip, int rdfId, TiledImagePainter channel, int chanNr) {
        tip.setChannelRdfId(rdfId, chanNr);
        tip.setChannel(channel.getImage(), chanNr);

        // TODO remove work around
        if (OrbitImageAnalysis.getInstance().loadAllLayersMultiChannel.isSelected()) {
            if (tip.hasMipMaps()) {
                for (int i = 0; i < tip.getMipMaps().length; i++) {
                    tip.getMipMaps()[i].setChannelRdfId(rdfId, chanNr);
                    tip.getMipMaps()[i].setChannel(channel.getMipMaps()[i].getImage(), chanNr);
                }
            }
        }

    }

    private String channelsToString() {
        return "r:" + recognitionFrame.bimg.getRedChannelRdfId()
                + ",g:" + recognitionFrame.bimg.getGreenChannelRdfId()
                + ",b:" + recognitionFrame.bimg.getBlueChannelRdfId()
                + ",o:" + recognitionFrame.bimg.getOverlayChannelRdfId();
    }


    public void componentHidden(ComponentEvent e) {
    }

    public void componentMoved(ComponentEvent e) {
    }

    public void componentResized(ComponentEvent e) {
        if (recognitionFrame != null) {
            recognitionFrame.setViewPortSize(getSize());
            recognitionFrame.prepareBuffer();
            recognitionFrame.repaint();
        }
        if (renderGrid != null) {
            renderGrid.setViewportSize(getSize());
            if (recognitionFrame != null)
                renderGrid.setViewportPosition(recognitionFrame.getViewPortOffset());
            renderGrid.repaint();
        }
    }

    public void componentShown(ComponentEvent e) {
    }


    @Override
    public Point getMousePosition() throws HeadlessException {
        if (!exclusive) return super.getMousePosition();
        else
            return MouseInfo.getPointerInfo().getLocation(); // e.g. in fullscreen mode
    }

    @Override
    public Dimension getSize() {
        if (!exclusive) return super.getSize();
        else {
            // exclusive
            return Toolkit.getDefaultToolkit().getScreenSize();
        }
    }

    public RecognitionFrame getRecognitionFrame() {
        return recognitionFrame;
    }

    public void setRecognitionFrame(RecognitionFrame recognitionFrame) {
        this.recognitionFrame = recognitionFrame;
    }


    public RenderGrid getRenderGrid() {
        return renderGrid;
    }


    public void setRenderGrid(RenderGrid renderGrid) {
        this.renderGrid = renderGrid;
    }


    public JSlider getOpacitySlider() {
        return opacitySlider;
    }


    public void setOpacitySlider(JSlider opacitySlider) {
        this.opacitySlider = opacitySlider;
    }


    public RawDataFile getRdf() {
        return rdf;
    }

    public void setRdf(RawDataFile rdf) {
        this.rdf = rdf;
    }

    public boolean isExclusive() {
        return exclusive;
    }

    public void setExclusive(boolean exclusive) {
        this.exclusive = exclusive;
    }

    public int getMipLayer() {
        return mipLayer;
    }

    public void setMipLayer(int mipLayer) {
        this.mipLayer = mipLayer;
    }

    public boolean isChannelContributionsLoaded() {
        return channelContributionsLoaded;
    }

    public void setChannelContributionsLoaded(boolean channelContributionsLoaded) {
        this.channelContributionsLoaded = channelContributionsLoaded;
    }
}