/*
 *    StreamPanel.java
 *    Copyright (C) 2010 RWTH Aachen University, Germany
 *    @author Jansen ([email protected])
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 *    
 *    
 */

package moa.gui.visualization;

import java.awt.*;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.RescaleOp;
import java.io.*;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import moa.cluster.SphereCluster;
import moa.clusterers.outliers.MyBaseOutlierDetector;
import moa.clusterers.outliers.MyBaseOutlierDetector.Outlier;

public class StreamOutlierPanel extends JPanel implements ComponentListener {
    private OutlierPanel highlighted_outlier = null;
    private double zoom_factor = 0.2;
    private int zoom = 1;
    private int width_org;
    private int height_org;
    private int activeXDim = 0;
    private int activeYDim = 1;
    
    private JPanel layerOutliers;
    private LabelAlgorithmPanel layerAlgorithmTitle;
    
    private RunOutlierVisualizer m_visualizer = null;
    private MyBaseOutlierDetector m_outlierDetector = null;    
    
    //Buffered Image stuff
    private BufferedImage pointImg;
    private BufferedImage canvasImg; 
    private ImgPanel layerCanvas;
    private boolean bAntiAlias = false;
    private int EVENTSIZE = 10;

    class ImgPanel extends JPanel{
        public BufferedImage image = null;
        public void setImage(BufferedImage image){
            setSize(image.getWidth(), image.getWidth());
            this.image = image;
        }
        @Override
        protected void paintComponent(Graphics g) {
            Graphics2D g2 = (Graphics2D)g;
            if(image!=null)
                g2.drawImage(image, null, 0, 0);
        }
    }
    
    class LabelAlgorithmPanel extends JPanel{
        public Color color;
        
        public LabelAlgorithmPanel() {
            setOpaque(true);
            setLayout(null);
        }
        
        @Override
        protected void paintComponent(Graphics g) {
            Graphics2D g2 = (Graphics2D)g;
            g2.setColor(color);
            g2.fillRect(0, 0, getWidth(), getHeight());
        }
    }

    public StreamOutlierPanel(Color colorAlgorithmTitle) {
        initComponents();
        
        layerAlgorithmTitle = new LabelAlgorithmPanel();
        layerAlgorithmTitle.color = colorAlgorithmTitle;
        add(layerAlgorithmTitle);
        
        layerOutliers = getNewLayer();                
        add(layerOutliers);
        
        layerCanvas = new ImgPanel();
        add(layerCanvas);
        
        addComponentListener(this);        
    }

    private JPanel getNewLayer(){
        JPanel layer = new JPanel();
        layer.setOpaque(false);
        layer.setLayout(null);
        return layer;
    }
    
    public void drawOutliers(Vector<Outlier> outliers, Color color){
        drawOutliers(layerOutliers, outliers, color);
    }
    
    public void repaintOutliers() {
        layerOutliers.repaint();
    }
    
    public void setOutliersVisibility(boolean visibility){
        layerOutliers.setVisible(visibility);
        layerOutliers.repaint();
    }
    
    public void setPointsVisibility(boolean visibility){        
        layerCanvas.setVisible(visibility);       
        layerCanvas.repaint();
    }
    
    public void clearPoints() {
        Graphics2D imageGraphics = (Graphics2D) pointImg.createGraphics();
                
        imageGraphics.setColor(Color.WHITE);       
        imageGraphics.setPaint(Color.WHITE);
        imageGraphics.fill(new Rectangle2D.Double(0, 0, getWidth(), getHeight()));
           
        ApplyToCanvas(pointImg);
        RedrawPointLayer();
    }
    
    public void clearEvents() {
        ApplyToCanvas(pointImg);
        //RedrawPointLayer();
    }
    
    public void ApplyToCanvas(BufferedImage img) {
        Graphics2D g = (Graphics2D) canvasImg.createGraphics();
        g.drawImage(img, 0, 0, this);
    }
    
    public void RedrawPointLayer() {
        //System.out.println("print?");
        layerCanvas.setImage(canvasImg);
        layerCanvas.repaint();
    }
    
    private void drawPoint(
            DataPoint point, 
            boolean bShowDecay,
            Color c, 
            boolean bFill,
            boolean bRedrawPointImg)
    {
        Graphics2D imageGraphics = (Graphics2D) pointImg.createGraphics();

        if (bAntiAlias) {
            imageGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
        }

        int size = Math.min(getWidth(), getHeight());
        int x = (int) Math.round(point.value(getActiveXDim()) * size);
        int y = (int) Math.round(point.value(getActiveYDim()) * size);
        //System.out.println("drawPoint: size="+size+" x="+x+" y="+y);

        if (c == null) {
            // fixed color of points
            c = Color.GRAY;
            // get a color by class of point
            // Color c = PointPanel.getPointColorbyClass((int)point.classValue(), 10);        
        }
        
        if (bShowDecay) {
            int minValue = 40; // 20
            double w = point.weight();            
            int alpha = (int) (255 * w + minValue);
            if (alpha > 255) alpha = 255;
            //System.out.println("alpha="+alpha+"w="+w);
            c = new Color(c.getRed(),c.getGreen(),c.getBlue(),alpha);
        }
        
        imageGraphics.setColor(c);
        int psize = PointPanel.POINTSIZE;
        int poffset = 2;
        imageGraphics.drawOval(x - poffset, y - poffset, psize, psize);
        if (bFill) imageGraphics.fillOval(x - poffset, y - poffset, psize, psize);
        
        if (bRedrawPointImg) {
            ApplyToCanvas(pointImg);
            RedrawPointLayer();
        }
    }

    public void drawPoint(DataPoint point, boolean bShowDecay, boolean bRedrawPointImg){
        drawPoint(point, bShowDecay, null, true, bRedrawPointImg);
    }
    
    /*public static BufferedImage duplicateImage(BufferedImage image) {
        if (image == null) {
            throw new NullPointerException();
        }

        BufferedImage j = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
        j.setData(image.getData());
        return j;
    }*/
    
    public void drawEvent(OutlierEvent outlierEvent, boolean bRedrawPointImg)
    {
        Graphics2D imageGraphics = (Graphics2D) canvasImg.createGraphics();

        if (bAntiAlias) {
            imageGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
        }

        int size = Math.min(getWidth(), getHeight());
        int x = (int) Math.round(outlierEvent.point.value(getActiveXDim()) * size);
        int y = (int) Math.round(outlierEvent.point.value(getActiveYDim()) * size);
        //System.out.println("drawPoint: size="+size+" x="+x+" y="+y);

        Color c = outlierEvent.outlier ? Color.RED : Color.BLACK;
        
        imageGraphics.setColor(c);
        int psize = EVENTSIZE;
        int poffset = EVENTSIZE / 2;
        imageGraphics.drawOval(x - poffset, y - poffset, psize, psize);

        if (bRedrawPointImg) {
            RedrawPointLayer();
        }
    }

    public void applyDrawDecay(float factor, boolean bRedrawPointImg){
        //System.out.println("applyDrawDecay: factor="+factor);
                
        // 1)
        int v = Color.GRAY.getRed();
        //System.out.println("applyDrawDecay: v="+v);
        RescaleOp brightenOp = new RescaleOp(1f, (255-v)*factor, null);
        
        // 2)
        //RescaleOp brightenOp = new RescaleOp(1f + factor, 0, null);
        
        // 3)
        //RescaleOp brightenOp = new RescaleOp(1f, (255)*factor, null);
        
        pointImg = brightenOp.filter(pointImg, null);
        
        if (bRedrawPointImg) {
            ApplyToCanvas(pointImg);
            RedrawPointLayer();
        }
    }

    private void drawOutliers(JPanel layer, Vector<Outlier> outliers, Color color){        
        layer.removeAll();
        for (Outlier outlier : outliers) {  
            int length = outlier.inst.numValues() - 1; // -1
            double[] center = new double[length]; // last value is the class
            for (int i = 0; i < length; i++) {
                center[i] = outlier.inst.value(i);                
            }            
            SphereCluster cluster = new SphereCluster(center, 0);                
                
            OutlierPanel outlierpanel = new OutlierPanel(m_outlierDetector, outlier, cluster, color, this);
            
            layer.add(outlierpanel);
            outlierpanel.updateLocation();
        }

        layer.repaint();
    }

    public void screenshot(String filename, boolean svg, boolean png){
    	if(layerOutliers.getComponentCount() == 0)
            return;
    	
        BufferedImage image = new BufferedImage(getWidth(),getHeight(),BufferedImage.TYPE_INT_RGB);
        if(png){
            synchronized(getTreeLock()){
                Graphics g = image.getGraphics();
                paintAll(g);
                try {
                    ImageIO.write(image, "png", new File(filename+".png"));
                } catch (Exception e) { }
            }
        }
        if(svg){
            try {
                PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(filename+".svg")));
                int width = 500;
                out.write("<?xml version=\"1.0\"?>\n");
                out.write("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
                out.write("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\""+width+"\" height=\""+width+"\">\n");

                if(layerOutliers.isVisible()){
                    for(Component comp :layerOutliers.getComponents()){
                        if(comp instanceof ClusterPanel)
                            out.write(((ClusterPanel)comp).getSVGString(width));
                    }
                }
                
                out.write("</svg>");
                out.close();
            } catch (IOException ex) {
                Logger.getLogger(StreamPanel.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public OutlierPanel getHighlightedOutlierPanel(){
        return highlighted_outlier;
    }

    public void setHighlightedOutlierPanel(OutlierPanel outlierpanel){
        //if (highlighted_outlier == outlierpanel) 
        //    return;
        
        //System.out.println("setHighlightedOutlierPanel");
        
        // restore previous highlighted outlier
        if (highlighted_outlier != null)            
            highlighted_outlier.highlight(false);
        
        highlighted_outlier = outlierpanel;
        if (highlighted_outlier != null)  
            highlighted_outlier.highlight(true);
        
        repaint();
    }

    public void setZoom(int x, int y, int zoom_delta, JScrollPane scrollPane){
        
        if(zoom ==1){
            width_org = getWidth();
            height_org = getHeight();
        }
        zoom+=zoom_delta;
        
        if(zoom<1) zoom = 1;
        else{
            int size = (int)(Math.min(width_org, height_org)*zoom_factor*zoom);

            setSize(new Dimension(size*zoom, size*zoom));
            setPreferredSize(new Dimension(size*zoom, size*zoom));

            scrollPane.getViewport().setViewPosition(new Point((int)(x*zoom_factor*zoom+x),(int)( y*zoom_factor*zoom+y)));
        }
    }

    public int getActiveXDim() {
        return activeXDim;
    }

    public void setActiveXDim(int activeXDim) {
        this.activeXDim = activeXDim;
    }

    public int getActiveYDim() {
        return activeYDim;
    }

    public void setActiveYDim(int activeYDim) {
        this.activeYDim = activeYDim;
    }

    /** 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.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        setBackground(new java.awt.Color(255, 255, 255));
        addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                formMouseClicked(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 400, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 300, Short.MAX_VALUE)
        );
    }// </editor-fold>//GEN-END:initComponents

    private void formMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseClicked       
        if (highlighted_outlier != null){
            highlighted_outlier.highlight(false);
            highlighted_outlier = null;
        }
    }//GEN-LAST:event_formMouseClicked

    // Variables declaration - do not modify//GEN-BEGIN:variables
    // End of variables declaration//GEN-END:variables
    
    public void setVisualizer(RunOutlierVisualizer v) {
        m_visualizer = v;
    }
    
    public void setOutlierDetector(MyBaseOutlierDetector outlierDetector) {
        m_outlierDetector = outlierDetector;
    }
    
    @Override
    public void componentHidden(ComponentEvent e) {
    }

    @Override
    public void componentMoved(ComponentEvent e) {
    }

    @Override
    public void componentResized(ComponentEvent e) {
        //System.out.println("componentResized");

        int heightAlgorithmTitle = 2;
        int size = Math.min(getWidth(), getHeight() - heightAlgorithmTitle);
        layerOutliers.setBounds(0, heightAlgorithmTitle, size, size);
        layerAlgorithmTitle.setBounds(0, 0, getWidth(), heightAlgorithmTitle);

        pointImg = new BufferedImage(size, size, BufferedImage.TYPE_INT_RGB);
        canvasImg = new BufferedImage(size, size, BufferedImage.TYPE_INT_RGB);
        layerCanvas.setBounds(0, heightAlgorithmTitle, size, size); 

        Graphics2D imageGraphics = (Graphics2D) pointImg.getGraphics();
        imageGraphics.setColor(Color.white);
        imageGraphics.fillRect(0, 0, getWidth(), getHeight());
        imageGraphics.dispose();    
               
        ApplyToCanvas(pointImg);
        RedrawPointLayer();
        
        if (m_visualizer != null) {
            m_visualizer.redrawOnResize();
        }
    }

    @Override
    public void componentShown(ComponentEvent e) {
    }
}