package netmatch;

import javax.swing.table.AbstractTableModel;
import javax.swing.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.awt.*;
import java.awt.image.BufferedImage;

import cytoscape.CyNetwork;
import cytoscape.task.Task;
import cytoscape.task.TaskMonitor;
import giny.model.GraphPerspective;
import giny.model.Node;
import giny.view.NodeView;
import giny.view.EdgeView;
import netmatch.algorithm.Common;
import ding.view.DGraphView;

/**
 * Handles the data to be displayed in the table in this dialog box
 */
public class netMatchResultsTableModel extends AbstractTableModel implements Task {
  String[] columnNames = {"Match Number","Nodes","Image"};
  Object[][] data;    //the actual table data
  protected GraphPerspective[] gpComplexArray;    //The list of complexes, sorted by score when !null
  boolean completedSuccessfully;

  private TaskMonitor taskMonitor;
  private boolean interrupted;
  private CyNetwork network;
  private ArrayList complexes;
  private boolean thereAreImages;
  private boolean doLayout;

  public netMatchResultsTableModel(CyNetwork network, ArrayList complexes,boolean thereAreImages,boolean doLayout) {
    taskMonitor = null;
    interrupted = false;
    completedSuccessfully = false;
    this.network = network;
    this.complexes = complexes;
    this.thereAreImages = thereAreImages;
    this.doLayout = doLayout;
  }

  public void run() {
    if(taskMonitor == null)
      throw new IllegalStateException("Task Monitor is not set.");
    try {
      GraphPerspective gpComplex;
      taskMonitor.setPercentCompleted(0);
      taskMonitor.setStatus("Draw Results... (Step 1 of 2)");
      gpComplexArray = convertComplexListToNetworkList(complexes, network);
      if(checkTask())
        return;
      data = new Object[gpComplexArray.length][columnNames.length];
      taskMonitor.setPercentCompleted(0);
      taskMonitor.setStatus("Draw Results... (Step 2 of 2)");
      for(int i = 0;i < gpComplexArray.length;i++) {
        gpComplex = gpComplexArray[i];
        data[i][0] = (new Integer(i + 1)).toString();
        data[i][1] = getNodeNameList(gpComplex);
        data[i][2] = "No Image";
        if(checkTask())
          return;
        if(thereAreImages)
          data[i][2] = new ImageIcon(convertNetworkToImage(gpComplexArray[i], Common.imageSize, Common.imageSize));
        if(checkTask())
          return;
        taskMonitor.setPercentCompleted(((i * 100) / gpComplexArray.length));
      }
      completedSuccessfully = true;
    }
    catch(Exception e) {
      completedSuccessfully = false;
      taskMonitor.setException(e, "Please check data!");
    }
  }

  /**
   * Gets the Task Title.
   *
   * @return human readable task title.
   */
  public String getTitle() {
    return "NetMatch Results";
  }

  /** Non-blocking call to interrupt the task. */
  public void halt() {
    this.interrupted = true;
  }

  /**
   * Sets the Task Monitor.
   *
   * @param taskMonitor TaskMonitor Object.
   */
  public void setTaskMonitor(TaskMonitor taskMonitor) throws IllegalThreadStateException {
    if(this.taskMonitor != null)
      throw new IllegalStateException("Task Monitor is already set.");
    this.taskMonitor = taskMonitor;
  }

  public boolean checkTask() {
    return interrupted;
  }

  /** @return true if the task has completed successfully */
  public boolean isCompletedSuccessfully() {
    return completedSuccessfully;
  }

  private Image convertNetworkToImage(GraphPerspective gpInput, int height, int width) {
    DGraphView view;
    Image image;

    view = generateGraphView(gpInput);
    for(Iterator in = view.getNodeViewsIterator();in.hasNext();) {
      NodeView nv = (NodeView)in.next();
      nv.setWidth(40);
      nv.setHeight(40);
      nv.setShape(NodeView.ELLIPSE);
      nv.setUnselectedPaint(Color.red);
      nv.setBorderPaint(Color.black);
      nv.setXPosition((view.getCanvas().getWidth() + 100) * Math.random());
      nv.setYPosition((view.getCanvas().getHeight() + 100) * Math.random());
      String label = nv.getNode().getIdentifier();
      nv.getLabel().setText(label);
    }
    for(Iterator ie = view.getEdgeViewsIterator();ie.hasNext();) {
      EdgeView ev = (EdgeView)ie.next();
      ev.setUnselectedPaint(Color.blue);
      ev.setTargetEdgeEnd(EdgeView.BLACK_ARROW);
      ev.setTargetEdgeEndPaint(Color.CYAN);
      ev.setSourceEdgeEndPaint(Color.CYAN);
      ev.setStroke(new BasicStroke(5f));
    }
    SpringEmbeddedLayouter lay = null;
    if(doLayout) {
      lay = new SpringEmbeddedLayouter(view);
      lay.setGraphView(view);
      lay.doLayout(0);
    }

    view.getCanvas().setSize(width, height);
    view.fitContent();
    image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

    final Graphics2D g = (Graphics2D) image.getGraphics();
    view.getCanvas().paint(g);
    image = view.getCanvas(DGraphView.Canvas.NETWORK_CANVAS).getImage();
    if(lay !=null)
      lay.resetDoLayout();
    return image;
  }

  private static DGraphView generateGraphView(GraphPerspective gp) {
    DGraphView view = new DGraphView(gp);

    final int[] nodes = gp.getNodeIndicesArray();
    for(int node : nodes) {
      view.addNodeView(node);
    }

    final int[] edges = gp.getEdgeIndicesArray();
    for(int edge : edges) {
      view.addEdgeView(edge);
    }
    return view;
  }

  public GraphPerspective[] getListOfComplexes() {
    return gpComplexArray;
  }

  public String getColumnName(int col) {
    return columnNames[col];
  }

  public int getColumnCount() {
    return columnNames.length;
  }

  public int getRowCount() {
    return data.length;
  }

  public Object getValueAt(int row, int col) {
    return data[row][col];
  }

  public Class getColumnClass(int c) {
    return getValueAt(0, c).getClass();
  }

  private GraphPerspective convertComplexToNetwork(int[] complex, CyNetwork sourceNetwork) {
    GraphPerspective gpComplex;
    gpComplex = sourceNetwork.createGraphPerspective(complex);
    return gpComplex;
  }

  private GraphPerspective[] convertComplexListToNetworkList(ArrayList complexList, CyNetwork sourceNetwork) {
    GraphPerspective gpComplexArray[] = new GraphPerspective[complexList.size()];
    for(int i = 0;i < complexList.size();i++) {
      gpComplexArray[i] = convertComplexToNetwork((int[])complexList.get(i), sourceNetwork);
      if(checkTask())
        return null;
      taskMonitor.setPercentCompleted(((i * 100) / gpComplexArray.length));
    }
    return gpComplexArray;
  }

  /**
   * Utility method to get the names of all the nodes in a GraphPerspective
   *
   * @param gpInput The input graph perspective to get the names from
   * @return A concatenated set of all node names (separated by a comma)
   */
  private StringBuffer getNodeNameList(GraphPerspective gpInput) {
    Iterator i = gpInput.nodesIterator();
    StringBuffer sb = new StringBuffer();
    while(i.hasNext()) {
      Node node = (Node)i.next();
      sb.append(node.getIdentifier());
      if(i.hasNext()) {
        sb.append(", ");
      }
    }
    return (sb);
  }

  /*private Image convertNetworkToImage(GraphPerspective gpInput, int height, int width) {
      PGraphView view;
      //SpringEmbeddedLayouter lay;
      Image image;

      view = new PGraphView(gpInput);
      for(Iterator in = view.getNodeViewsIterator();in.hasNext();) {
        NodeView nv = (NodeView)in.next();
        String label = nv.getNode().getIdentifier();
        nv.getLabel().setText(label);
        nv.setWidth(40);
        nv.setHeight(40);
        nv.setShape(NodeView.ELLIPSE);
        nv.setUnselectedPaint(Color.red);
        nv.setBorderPaint(Color.black);
        nv.setXPosition(view.getCanvas().getLayer().getGlobalFullBounds().getWidth() * Math.random());
        nv.setYPosition((view.getCanvas().getLayer().getGlobalFullBounds().getHeight() + 100) * Math.random());
      }
      for(Iterator ie = view.getEdgeViewsIterator();ie.hasNext();) {
        EdgeView ev = (EdgeView)ie.next();
        ev.setUnselectedPaint(Color.blue);
        ev.setTargetEdgeEnd(EdgeView.BLACK_ARROW);
        ev.setTargetEdgeEndPaint(Color.CYAN);
        ev.setSourceEdgeEndPaint(Color.CYAN);
        ev.setStroke(new BasicStroke(5f));
      }
      //lay = new SpringEmbeddedLayouter(view);
      //lay.doLayout();
      image = view.getCanvas().getLayer().toImage(width, height, null);
      //double largestSide = view.getCanvas().getLayer().getFullBounds().width;
      //if(view.getCanvas().getLayer().getFullBounds().height > largestSide) {
      //  largestSide = view.getCanvas().getLayer().getFullBounds().height;
      //}
      return image;
    }*/
}