package netmatch.qtool;

import edu.umd.cs.piccolo.PLayer;
import edu.umd.cs.piccolo.PNode;
import edu.umd.cs.piccolo.nodes.PPath;

import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;

public class QNode extends PPath implements PropertyChangeListener, QElement {

  public final static int NODE_INIT_WIDTH = 30;
  public final static int NODE_INIT_HEIGHT = 30;

  final static Color SELECTED_NODE_COLOR = new Color(255, 255, 0);
  final static Color NODE_COLOR = new Color(255, 153, 153);

  private String id;
  private String attr = "?";
  private QText text;
  private ArrayList edges = new ArrayList();
  private ArrayList paths = new ArrayList();
  private ArrayList loops = new ArrayList();


  public QNode(float x, float y, String id) {
    // create circle
    super(new Ellipse2D.Float(x - NODE_INIT_WIDTH / 2, y - NODE_INIT_HEIGHT / 2, NODE_INIT_WIDTH, NODE_INIT_HEIGHT));
    this.id = id;
    // paint it
    setPaint(NODE_COLOR);
    // add text
    text = new QText(this, attr);
    addPropertyChangeListener(PNode.PROPERTY_FULL_BOUNDS, this);
  }

  public String getId() {
    return id;
  }

  public boolean isSingle() {
    return loops.size() + edges.size() + paths.size() == 0;
  }

  public ArrayList getEdges() {// depricate ??
    return edges;
  }

  public ArrayList getPaths() {// depricate ??
    return paths;
  }

  public void addToLayer(PLayer layer) {
    layer.addChild(this);
    layer.addChild(text);
  }


  public void delete(PLayer nodeLayer, PLayer edgeLayer) {
    nodeLayer.removeChild(text);
    for(int i = 0;i < loops.size();i++) {// loops
      edgeLayer.removeChild((QLoop) loops.get(i));
      Network.lCount--;
    }
    for(int i = 0;i < edges.size();i++) {// edges
      QEdge edge = (QEdge) edges.get(i);
      if(edge.getSource() == this)
        edge.getTarget().removeEdge(edge);
      else
        edge.getSource().removeEdge(edge);
      Network.eCount--;
    }
    for(int i = 0;i < edges.size();i++) {// edges
      edgeLayer.removeChild((QEdge) edges.get(i));
    }

    for(int i = 0;i < paths.size();i++) {// paths
      QPath path = (QPath) paths.get(i);
      if(path.getSource() == this)
        path.getTarget().removePath(path);
      else
        path.getSource().removePath(path);
      Network.pCount--;
    }
    for(int i = 0;i < paths.size();i++) {// paths
      edgeLayer.removeChild((QPath) paths.get(i));
    }
    nodeLayer.removeChild(this);
  }

  public String getInfo() {
    int degree = edges.size() + paths.size();
    return "Node:\n" + "Id: " + id + "\n" + "Attribute: " + attr + "\n" + "Degree: " + degree + "\n" + "Loops: " + loops.size() + "\n";

  }

  public void decorateSelect() {
    setPaint(SELECTED_NODE_COLOR);
  }

  public void undecorateSelect() {
    setPaint(NODE_COLOR);
  }

  public void addEdge(QEdge edge) {
    int val = 0;
    if(edge.getSource() == this)
      val = calcValence(edge.getTarget());
    edges.add(edge);
    if(edge.getSource() == this)
      edge.setValence(val);
  }

  public void removeEdge(QEdge edge) {
    if(!edges.contains(edge))
      return;
    edges.remove(edge);
    if(edge.getSource() == this)
      updateValence(edge.getTarget(), edge.getValence());
    update();
  }

  public void addPath(QPath path) {
    int val = 0;
    if(path.getSource() == this)
      val = calcValence(path.getTarget());
    paths.add(path);
    if(path.getSource() == this)
      path.setValence(val);
  }

  public void removePath(QPath path) {
    if(!paths.contains(path))
      return;
    paths.remove(path);
    if(path.getSource() == this)
      updateValence(path.getTarget(), path.getValence());
    update();
  }

  public void addLoop(QLoop loop) {
    int val = calcLoopValence();
    loops.add(loop);
    loop.setValence(val);
  }

  public void removeLoop(QLoop loop) {
    if(!loops.contains(loop))
      return;
    loops.remove(loop);
    updateLoopValence(loop.getValence());
    update();
  }

  public void update() {
    text.update();// text
    for(int i = 0;i < loops.size();i++) {// loops
      ((QLoop) loops.get(i)).update();
    }
    for(int i = 0;i < edges.size();i++) {// edges
      ((QEdge) edges.get(i)).update();
    }
    for(int i = 0;i < paths.size();i++) {// paths
      ((QPath) paths.get(i)).update();
    }
  }

  public void resizeToText() {
    double h = text.getFullBoundsReference().getHeight();
    double w = text.getFullBoundsReference().getWidth();
    if(getFullBoundsReference().getHeight() < h * 1.2)
      setHeight(h * 1.2);
    if(getFullBoundsReference().getWidth() < w * 1.2)
      setWidth(w * 1.2);
  }

  public void propertyChange(PropertyChangeEvent evt) {
    update();
  }

  public void setAttr(String attr) {
    this.attr = attr;
    text.setText(attr);
    resizeToText();
  }

  public String getAttr() {
    return attr;
  }

  private int calcValence(QNode target) {
    int v = 0;
    for(int i = 0;i < edges.size();i++) {
      QEdge edge = (QEdge) edges.get(i);
      if(edge.getTarget() == target || (edge.getTarget() == this && edge.getSource() == target))
        v++;
    }
    for(int i = 0;i < paths.size();i++) {
      QPath path = (QPath) paths.get(i);
      if(path.getTarget() == target || (path.getTarget() == this && path.getSource() == target))
        v++;
    }
    return v;
  }

  private void updateValence(QNode target, int val) {
    for(int i = 0;i < edges.size();i++) {
      QEdge edge = (QEdge) edges.get(i);
      if((edge.getTarget() == target || (edge.getTarget() == this && edge.getSource() == target)) && edge.getValence() > val)
        edge.setValence(edge.getValence() - 1);
    }
    for(int i = 0;i < paths.size();i++) {
      QPath path = (QPath) paths.get(i);
      if((path.getTarget() == target || (path.getTarget() == this && path.getSource() == target)) && path.getValence() > val)
        path.setValence(path.getValence() - 1);
    }
  }

  private int calcLoopValence() {
    return loops.size();
  }

  private void updateLoopValence(int val) {
    for(int i = 0;i < loops.size();i++) {
      QLoop loop = (QLoop) loops.get(i);
      if(loop.getValence() > val)
        loop.setValence(loop.getValence() - 1);
    }
  }
}