/***************************************************************************
 *   Copyright (C) 2009 by Paul Lutus                                      *
 *   lutusp@arachnoid.com                                                  *
 *                                                                         *
 *   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 2 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, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
package fftexplorer;

/*
 * GraphicPanel.java
 *
 * Created on February 9, 2004, 9:49 AM
 */
/**
 *
 * @author  lutusp
 */
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;

public final class GraphicPanel extends javax.swing.JPanel {

    FFTExplorer parent;
    GraphicControl gc;
    boolean freqMode;
    boolean antialias;
    BufferedImage image = null;
    int oldxs = -1, oldys = -1;
    //Color backgroundColor = Color.white;
    //Color lineColor = new Color(0, 128, 0);
    int xsize, ysize;
    double vScale = 1.0;
    double hScale = 1.0;
    Complex[] data = null;
    long t = 0;
    long tl = 0;
    int lineThickness = 1;
    int mousePressX;
    double xOffset = 0;
    boolean updateParent = false;

    /** Creates new form GraphicPanel */
    public GraphicPanel(FFTExplorer p, GraphicControl gc, boolean freqMode) {
        parent = p;
        this.gc = gc;
        this.freqMode = freqMode;
        initComponents();
        xsize = getSize().width;
        ysize = getSize().height;
    //setToolTipText("<html>Spin mouse wheel to zoom,<br/>drag mouse to pan</html>");
    }

    public void setupOffset() {
        xOffset = (freqMode) ? parent.sv_xFreqOffset : parent.sv_xTimeOffset;
        updateParent = true;
    }

    public void setData(Complex[] d) {
        if (d != null) {
            data = d;
            repaint();
        }
    }

    @Override
    public void paintComponent(Graphics g) {
        if (isVisible()) {
            Color lineColor = parent.sv_traceColor.getColor();
            //System.out.println("Repainting " + new Date());
            //super.paintComponent(g);
            xsize = getSize().width;
            ysize = getSize().height;
            if (xsize > 0 && ysize > 0) {
                // only allocate an image buffer
                // if the image has changed size.
                // this is crucial to fast drawing
                if (xsize != oldxs || ysize != oldys) {
                    image = new BufferedImage(xsize, ysize, BufferedImage.TYPE_INT_RGB);
                    oldxs = xsize;
                    oldys = ysize;

                }
                Graphics2D ig = (Graphics2D) image.getGraphics();
                ig.setColor(parent.sv_backgroundColor.getColor());
                ig.fillRect(0, 0, xsize, ysize);
                if (antialias) {
                    RenderingHints rh = new RenderingHints(
                            RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                    ig.addRenderingHints(rh);
                }
                //cg.setStroke(new BasicStroke(lineThickness));
                if (freqMode) {
                    drawPointsFreq(ig, lineColor);
                } else {
                    drawPointsTime(ig, lineColor);
                }
                g.drawImage(image, 0, 0, this);
                ig.dispose();
            }
        }
    }

    private void drawPointsFreq(Graphics2D cg, Color color) {
        if (data != null) {
            int width = parent.graphicLineWidth;
            double xScale = (data.length * hScale) / 2.0;
            cg.setColor(color);
            //int py0 = scalePoint(zeroPoint, ysize - 1, 0);
            int pxa = 0, pxb, py;
            //width = (width == 0)?1:width;
            double dy;
            for (int i = 0; i < xScale; i++) {
                int j = (int) (i + xOffset * xScale);
                if (j < 0 || j > data.length / 2) {
                    dy = 0;
                } else {
                    dy = data[j].mag() * vScale;
                }
                pxa = (int) (((double) i * xsize / xScale));
                pxb = (int) (((double) (i + 1) * xsize / xScale) - pxa);
                pxb = (pxb < width) ? width : pxb;
                dy = (dy < 0) ? 0 : dy;
                dy = (dy > 1) ? 1 : dy;
                py = scalePoint(dy, ysize-1, 0);
                //if (i > 0) {
                //    cg.drawLine(opx, opy, px, py);
                //int width = px-opx;
                cg.fillRect(pxa, py, pxb, ysize - 1);
                pxa = pxb;
            //}
            }
        }
    }

    private void drawPointsTime(Graphics2D cg, Color color) {
        if (data != null) {
            if (parent.graphicLineWidth > 1) {
                cg.setStroke(new BasicStroke(parent.graphicLineWidth));
            }
            double zpoint = 0.5;
            double xScale = ((data.length - 1) * hScale);
            xScale = (xScale < 1) ? 1 : xScale;
            cg.setColor(color);
            int opx = 0, opy = 0;
            double dy;
            for (int i = 0; i <= xScale; i++) {
                int j = (int) (i + xOffset * xScale);
                if (j < 0 || j > data.length - 1) {
                    dy = 0;
                } else {
                    dy = (data[j].re * vScale) + zpoint;
                }
                double dx = (double) i / xScale;
                int px = scalePoint(dx, 0, xsize);
                int py = scalePoint(dy, ysize - 1, 0);
                if (i > 0) {
                    cg.drawLine(opx, opy, px, py);
                }
                opx = px;
                opy = py;
            }
        }
    }

    double dispToSpace(double x, double size) {
        return parent.ntrp(x, 0, size, 0, 1.0);
    }

    int scalePoint(double v, int a, int b) {
        return (int) ((v * (b - a)) + a);
    }

    double xOffsetLimit(double v) {
        v = (v < 0) ? 0 : v;
        v = (v > (1.0 / hScale) - 1) ? (1.0 / hScale) - 1 : v;
        return v;
    }

    void adjustOffset() {
        xOffset = xOffsetLimit(xOffset);
        if (updateParent) {
            if (freqMode) {
                parent.sv_xFreqOffset = xOffset;
            } else {
                parent.sv_xTimeOffset = xOffset;
            }
        }
    }

    void handleWheelEvent(MouseWheelEvent evt) {
        int x = evt.getX();
        double sx = dispToSpace(x, xsize);
        double delta = (xOffset + sx) * hScale;
        gc.handleWheelEvent(evt);
        xOffset = (delta / hScale) - sx;
        adjustOffset();
    }

    void handleMousePressed(MouseEvent evt) {
        mousePressX = evt.getX();
        if (evt.isPopupTrigger()) {
            //parent.p("context action");
            parent.sv_graphicTipFlag = !parent.sv_graphicTipFlag;
        }
    }

    void handleMouseDragged(MouseEvent evt) {
        int deltaX = mousePressX - evt.getX();
        double offset = dispToSpace(deltaX, xsize);
        xOffset += offset;
        adjustOffset();
        mousePressX = evt.getX();
    }

    void handleMouseMoved(MouseEvent evt) {
        double x = evt.getX();
        double y = evt.getY();
        double sx = dispToSpace(x, xsize) + xOffset;
        sx *= ((freqMode) ? gc.freqSpan : gc.timeSpan) * hScale;
        double sub = (freqMode) ? 1 : 0.5;
        double sy = (sub - dispToSpace(y, ysize)) / vScale;
        String s = (freqMode) ? "Frequency:" : "Time:";
        s += " " + parent.getEngFormatString(sx, freqMode);
        s += ", Amplitude: " + parent.formatNum(sy);
        parent.setStatus(s);
        if (parent.sv_graphicTipFlag) {
            setToolTipText("<html>" + s + "<br/>(right-click to hide)</html>");
        } else {
            setToolTipText(null);
        }
    }

    void handleMouseExited() {
        parent.setStatus("Status/Position");
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        setBackground(java.awt.Color.white);
        setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
        setDoubleBuffered(false);
        addMouseWheelListener(new java.awt.event.MouseWheelListener() {
            public void mouseWheelMoved(java.awt.event.MouseWheelEvent evt) {
                formMouseWheelMoved(evt);
            }
        });
        addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseExited(java.awt.event.MouseEvent evt) {
                formMouseExited(evt);
            }
            public void mousePressed(java.awt.event.MouseEvent evt) {
                formMousePressed(evt);
            }
            public void mouseReleased(java.awt.event.MouseEvent evt) {
                formMouseReleased(evt);
            }
        });
        addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
            public void mouseDragged(java.awt.event.MouseEvent evt) {
                formMouseDragged(evt);
            }
            public void mouseMoved(java.awt.event.MouseEvent evt) {
                formMouseMoved(evt);
            }
        });
        setLayout(new java.awt.BorderLayout());
    }// </editor-fold>//GEN-END:initComponents

    private void formMouseWheelMoved(java.awt.event.MouseWheelEvent evt) {//GEN-FIRST:event_formMouseWheelMoved
        // TODO add your handling code here:
        handleWheelEvent(evt);
    }//GEN-LAST:event_formMouseWheelMoved

    private void formMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMousePressed
        // TODO add your handling code here:
        handleMousePressed(evt);
    }//GEN-LAST:event_formMousePressed

    private void formMouseDragged(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseDragged
        // TODO add your handling code here:
        handleMouseDragged(evt);
    }//GEN-LAST:event_formMouseDragged

    private void formMouseMoved(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseMoved
        // TODO add your handling code here:
        handleMouseMoved(evt);
    }//GEN-LAST:event_formMouseMoved

    private void formMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseReleased
        // TODO add your handling code here:
        handleMousePressed(evt);
    }//GEN-LAST:event_formMouseReleased

    private void formMouseExited(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseExited
        // TODO add your handling code here:
        handleMouseExited();
    }//GEN-LAST:event_formMouseExited

    // Variables declaration - do not modify//GEN-BEGIN:variables
    // End of variables declaration//GEN-END:variables
}
