/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: ExportMismatchTable.java
 *
 * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
 *
 * Electric(tm) 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.
 *
 * Electric(tm) 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.sun.electric.tool.user.ncc;

import java.awt.Color;
import java.util.Iterator;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JEditorPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.border.Border;

import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.variable.VarContext;
import com.sun.electric.tool.ncc.result.NetObjReport;
import com.sun.electric.tool.ncc.result.PortReport;
import com.sun.electric.tool.ncc.result.WireReport;
import com.sun.electric.tool.user.Highlighter;

/**
 * This class implements the tbale for Export mismatches
 */
class ExportMismatchTable extends ExportTable {

    ExportMismatch[] mismatches;
    
    public ExportMismatchTable(NccGuiInfo res) {
        super(res, 2);
        height = Math.min(result.getValidExportMismatchCount(), MAXROWS);
        mismatches = new ExportMismatch[height];
        setup();
        
        boolean topoOK = result.isTopologyMatch();
        int row = 0;
        
        for (Iterator<ExportMismatch> it = result.getExportMismatches().iterator(); it.hasNext() && row<height;) {
            ExportMismatch em = it.next();
            if (topoOK && em.isValidOnlyWhenTopologyMismatch()) continue;
            mismatches[row] = em;
            row++;
        }
        
        setModel(new MismatchTableModel(this));
        getTableHeader().setReorderingAllowed(false);
        getColumnModel().getColumn(0).addPropertyChangeListener(this);
        getColumnModel().getColumn(1).addPropertyChangeListener(this);
    }
}

/**
 * This class implements the model for the Export mismatch table
 */
class MismatchTableModel extends ExportTableModel {
    /**
     * Each item in array indicates whether cells in the corresponding 
     * row should be switched  
     */
    private boolean swapCells[];
    /**
     * Array of mismatches
     */
    private ExportMismatch[] mismatches;
    
    public MismatchTableModel(ExportMismatchTable parent) {
        super(parent);
        mismatches = parent.mismatches;
        int[][] cellPrefHeights = parent.cellPrefHeights;
        int[][] cellPrefWidths  = parent.cellPrefWidths;
        swapCells = new boolean[height];
        int swap, cellNdx = 0;
        Border border = BorderFactory.createEmptyBorder();
        StringBuffer html = new StringBuffer(64);
        
        // fill swap array
        for (int row=0; row<height; row++) {
            // if first design in the mismatch object has the same name as
            // the first design in the parent result object, then don't swap.
            // Otherwise swap
            if (mismatches[row].getName(0).equals(parent.result.getNames()[0])) {
                swap = 0;
                swapCells[row] = false;
            } else {
                swap = 1;
                swapCells[row] = true;
            }
            // fill cells with hyperliked Export lists
            for (int j=0; j<2; j++, cellNdx++) {
                html.setLength(0);
                html.append("<html><font size=3><font face=\"Helvetica, TimesRoman\">");
                int lineNdx = cellNdx*10000;
                if (mismatches[row] instanceof ExportMismatch.MultiMatch) {
                    List<PortReport> ports = ((ExportMismatch.MultiMatch)mismatches[row]).getAll((j+swap)%2);
                    // each Port has a list of Exports which are printed as hyperlinked list
                    for (Iterator<PortReport> it=ports.iterator(); it.hasNext();) {
                        appendNameOf(it.next(), html, lineNdx, false, null);
                        if (it.hasNext()) html.append("<br>" + LSEP);
                        lineNdx++;
                        cellPrefHeights[row][j] += ExportTable.LINEHEIGHT;
                    }
                } else if (mismatches[row] instanceof ExportMismatch.NameMismatch) {
                    if (j == swap) {
                        PortReport port = ((ExportMismatch.NameMismatch)mismatches[row]).getFirstExport(); 
                        appendNameOf(port, html, lineNdx, false, null);
                    } else {
                        NetObjReport no = ((ExportMismatch.NameMismatch)mismatches[row]).getSuggestion();
                        appendNameOf(no, html, lineNdx, true, ExportTable.GREEN);
                    }
                    lineNdx++;
                    cellPrefHeights[row][j] += ExportTable.LINEHEIGHT;
                } else if (mismatches[row] instanceof ExportMismatch.TopologyMismatch) {
                    PortReport port;
                    if (j == swap) {
                        port = ((ExportMismatch.TopologyMismatch)mismatches[row]).getFirstExport();
                        appendNameOf(port, html, lineNdx, true, ExportTable.RED);
                    } else {
                        port = ((ExportMismatch.TopologyMismatch)mismatches[row]).getSecondExport();
                        appendNameOf(port, html, lineNdx, true, ExportTable.RED);
                    }
                    lineNdx++;
                    if (j != swap) { // if a cell in the right column
                        NetObjReport no = ((ExportMismatch.TopologyMismatch)mismatches[row]).getSuggestion();
                        if (no != null) {  // if suggestion exists
                            html.append("<br>");
                            appendNameOf(no, html, lineNdx, true, ExportTable.GREEN);
                            lineNdx++;
                            cellPrefHeights[row][j] += ExportTable.LINEHEIGHT;
                        }
                    }
                    cellPrefHeights[row][j] += ExportTable.LINEHEIGHT;
                }
                html.append("</font></html>");
                
                JEditorPane htmlPane = new JEditorPane();
                htmlPane.setEditable(false);
                htmlPane.addHyperlinkListener(this);
                htmlPane.setContentType("text/html");
                htmlPane.setText(html.toString());
                htmlPane.setMargin(insets);
                htmlPane.addMouseListener(mouseAdapter);                    
                htmlPane.moveCaretPosition(0);
                cellPrefWidths[row][j] = htmlPane.getPreferredSize().width + ExportTable.WIDTHMARGIN;
                if (cellPrefHeights[row][j] > ExportTable.MAX_VISIBLE_LINES*ExportTable.LINEHEIGHT+ExportTable.HEIGHTMARGIN)
                    cellPrefHeights[row][j] = ExportTable.MAX_VISIBLE_LINES*ExportTable.LINEHEIGHT+ExportTable.HEIGHTMARGIN;
                JPanel panel = new JPanel();
                panel.setBackground(Color.WHITE);
                panel.add(htmlPane);
                panes[row][j] = new JScrollPane(panel);
                panes[row][j].setBorder(border);
            }
        }
    }
    
    /**
     * If the provieded NetObject is a Port, then print all Exports 
     * corresponding to it. If the NetObject is a Wire, print its name.
     * @param no  Port or Wire
     * @param html  buffer to print to
     * @param lineNdx  line number on which this text is going to appear 
     * in the parent table cell. Used to create hyperlink indices
     * @param doColoring  paint text to the provided Color if this is true.
     * Print in default color otherwise.
     * @param sugColor  text color
     */
    protected void appendNameOf(NetObjReport no, StringBuffer html, 
            int lineNdx, boolean doColoring, String sugColor) {
        String href = "<a style=\"text-decoration: none\" href=\"";
        String text = null;
        boolean isImpl = false;
        if (no instanceof PortReport) {
            PortReport port = (PortReport)no;
            isImpl = port.isImplied();
            text = port.exportNamesString();
            if (doColoring) html.append("<font COLOR=\"" + sugColor + "\">");
            if (isImpl) {
                html.append(text);
                if (doColoring) html.append("</font>");
                html.append("<font COLOR=\"gray\"> : implied</font>");
            } else {
                html.append(href + lineNdx +"\">"+ text +"</a>");
                if (doColoring) html.append("</font>");
            }
        } else if (no instanceof WireReport) {
            text = no.getName();
            if (doColoring) html.append("<font COLOR=\"" + sugColor + "\">");
            html.append(href + lineNdx + "\">"+ text +"</a></font>");
            if (doColoring) html.append("</font>");
        }
    }
    
//    /**
//     * Detect whether a Port in mismamatch has noo Exports.
//     * If no Exports exist then the Port has  an implied Export  
//     * @param port  Port to test
//     * @return true if Port has only an implied Export, false otherwise
//     */
//    protected boolean isImplied(PortReport port) {
//        Iterator it=port.getWire().getNameProxy().getNet().getExports();
//        if (it.hasNext()) return false;
//        return true;
//    }
    
    /**
     * Highlight Exportson Port with the provided index. 
     * Index encodes table row, table column, and line number inside 
     * the table cell on which the Port is printed  
     * @param index  Port index
     */
    protected void highlight(int index) {
        // decode index
        int line = index%10000;
        int row = (index/10000)/2;
        int col = (index/10000)%2;
        if (swapCells[row]) col = (col+1)%2;
        
        ExportMismatch em = mismatches[row];            
        Cell cell = em.getCell(col);
        VarContext context = em.getContext(col);
        
        // find the highlighter corresponding to the cell
        Highlighter highlighter = HighlightTools.getHighlighter(cell, context);
        if (highlighter == null) return;
            
        // find what to highlight 
        if (em instanceof ExportMismatch.MultiMatch) {
            List<PortReport> ports = ((ExportMismatch.MultiMatch)em).getAll(col);
            int i;
            Iterator<PortReport> it;
            // go to the necessary line
            for (it=ports.iterator(), i=0; it.hasNext()&&i<line; i++,it.next());
            PortReport port = it.next();
            HighlightTools.highlightPortExports(highlighter, cell, port);
        } else if (em instanceof ExportMismatch.NameMismatch) {
            PortReport port;
            NetObjReport portOrWire;
            if (col == 0) {
                port = ((ExportMismatch.NameMismatch)em).getFirstExport();
                HighlightTools.highlightPortExports(highlighter, cell, port);
            } else {
                portOrWire = ((ExportMismatch.NameMismatch)em).getSuggestion();
                HighlightTools.highlightPortOrWire(highlighter, cell, portOrWire);
            }
        } else if (em instanceof ExportMismatch.TopologyMismatch) {
            PortReport port1, port2;
            NetObjReport portOrWire;
            if (col == 0) {
                port1 = ((ExportMismatch.TopologyMismatch)em).getFirstExport();
                HighlightTools.highlightPortExports(highlighter, cell, port1);
            } else if (line == 0) {
                port2 = ((ExportMismatch.TopologyMismatch)em).getSecondExport();
                HighlightTools.highlightPortExports(highlighter, cell, port2);
            } else if (line == 1) {  // the second line has a suggestion
                portOrWire = ((ExportMismatch.TopologyMismatch)em).getSuggestion();
                HighlightTools.highlightPortOrWire(highlighter, cell, portOrWire);
            }
        }
        highlighter.finished();
    }
    
    /**
     * Get column name
     * @param col  column
     * @return name of the column
     */ 
    public String getColumnName(int col) {
        return parent.result.getNames()[col];
    }

}
