/**
 *
 */
package brn.gui.linktable.diagram.custom.editors;

import java.util.Collection;
import java.util.Iterator;

import jist.swans.net.NetAddress;

import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.operations.OperationHistoryFactory;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.emf.transaction.util.TransactionUtil;
import org.eclipse.gmf.runtime.common.core.command.CommandResult;
import org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand;

import brn.gui.Activator;
import brn.gui.datasource.Source;
import brn.gui.diagram.SimulatorResource;
import brn.gui.graph.model.Graph;
import brn.gui.graph.model.LinkTableLink;
import brn.gui.graph.model.ModelFactory;
import brn.gui.graph.model.Node;
import brn.sim.data.LinkTableData;

/**
 * @author kurth
 *
 */
public class LinkTableResouce extends SimulatorResource {

  private int linkTableId;

  public LinkTableResouce(URI uri, Source simulator) {
    super(uri, simulator);
    this.linkTableId = -1;
  }

  public void setLinkTableId(int id) {
    this.linkTableId = id;
  }

  /**
   * NOTE the transaction is only executed if necessary, otherwise
   * drag and drop will not work properly
   *
   * TODO look for deleted links and discard them from diagram
   */
  public void refresh() throws CoreException {
    if (null == simulator || !simulator.isConnected())
      throw new CoreException(Activator.createError("Simulator not available"));

    final Graph model = (Graph) getEObject("model");
    LinkTableData linkTableData = (LinkTableData)
        simulator.getContribution(this.linkTableId);

    final Collection<LinkTableData.Node> collNodes = linkTableData.getNodes();
    final Collection<LinkTableData.Link> collLinkInfo = linkTableData.getLinks();

    if (!changed(collNodes, collLinkInfo, model))
      return;

    TransactionalEditingDomain editingDomain =
      TransactionUtil.getEditingDomain(model);

    AbstractTransactionalCommand command = new AbstractTransactionalCommand(
        editingDomain, "Update node", null) { //$NON-NLS-1$
      protected CommandResult doExecuteWithResult(IProgressMonitor monitor,
          IAdaptable info) throws ExecutionException {

        Iterator<LinkTableData.Node> iter = collNodes.iterator();
        while (null != iter && iter.hasNext()) {
          LinkTableData.Node node = iter.next();
          Node modelNode = getModelNode(node);

          if (null == modelNode)
            createNode(model, node);
          else
            updateNode(modelNode, node);
        }

        Iterator<LinkTableData.Link> iterL = collLinkInfo.iterator();
        while (null != iterL && iterL.hasNext()) {
          LinkTableData.Link link = iterL.next();

          LinkTableLink modelLink = getModelLink(link);
          if (null == modelLink)
            createLink(link);
          else
            updateLink(link, modelLink);
        }

        return CommandResult.newOKCommandResult();
      }
    };

    try {
      OperationHistoryFactory.getOperationHistory().execute(command,
          null/*new SubProgressMonitor(null, 1)*/, null);
    } catch (ExecutionException e) {
      throw new CoreException(
          Activator.createError("Error during simulator communication", e));
    }
  }

  /**
   * Check for changes in simulator.
   *
   * @param collNodes
   * @param collLinkInfo
   * @param model
   * @return true if changes found, false otherwise
   */
  private boolean changed(Collection<LinkTableData.Node> collNodes,
    Collection<LinkTableData.Link> collLinkInfo, Graph model) {

    Iterator<LinkTableData.Node> iter = collNodes.iterator();
    while (null != iter && iter.hasNext()) {
      LinkTableData.Node node = iter.next();
      Node modelNode = getModelNode(node);

      if (null == modelNode
          ||modelNode.getId() != node.nodeId
          ||modelNode.getX() != node.locX
          ||modelNode.getY() != node.locY)
        return true;
    }

    Iterator<LinkTableData.Link> iterL = collLinkInfo.iterator();
    while (null != iterL && iterL.hasNext()) {
      LinkTableData.Link link = iterL.next();
      if (link.getMetric() >= 9999)
        continue;

      LinkTableLink modelLink = getModelLink(link);
      if (null == modelLink
          ||modelLink.getMetric() != link.getMetric())
//          ||modelLink.getSeq() != link.getSeq())
        return true;
    }

    return false;
  }

  private void updateLink(LinkTableData.Link link, LinkTableLink modelLink) {
    if (link.getMetric() >= 9999) {
      if (null != modelLink.getSource())
        modelLink.getSource().getSourceConnections().remove(modelLink);
      modelLink.setSource(null);

      if (null != modelLink.getTarget())
        modelLink.getTarget().getTargetConnections().remove(modelLink);
      modelLink.setTarget(null);
    }

    modelLink.setMetric(link.getMetric());
    modelLink.setSeq((int)link.getSeq());
  }

  private LinkTableLink createLink(LinkTableData.Link link) {
    // TODO really??
    if (link.getMetric() >= 9999)
      return null;

    LinkTableLink modelLink = ModelFactory.eINSTANCE.createLinkTableLink();
    setID(modelLink, getLinkId(link));

    NetAddress netAddress = (NetAddress)link.getFrom();
    String from = String.valueOf(netAddress.getId());
    String to = String.valueOf(((NetAddress)link.getTo()).getId());
    Node modelNodeSrc = (Node) getEObjectByID(from);
    Node modelNodeDst = (Node) getEObjectByID(to);

    // not created yet? next time ...
    if  (null == modelNodeSrc || null == modelNodeDst)
      return null;

    modelLink.setSource(modelNodeSrc);
    modelLink.setTarget(modelNodeDst);

    modelNodeSrc.getSourceConnections().add(modelLink);
    modelNodeDst.getTargetConnections().add(modelLink);

    modelLink.setMetric(link.getMetric());
    modelLink.setSeq((int)link.getSeq());

    return modelLink;
  }

  private LinkTableLink getModelLink(LinkTableData.Link link) {
    return (LinkTableLink) getEObjectByID(getLinkId(link));
  }

  private String getLinkId(LinkTableData.Link link) {
    StringBuilder b = new StringBuilder();
    b.append(link.getFrom());b.append(':');
    b.append(link.getTo());
    return b.toString();
  }

  private Node createNode(Graph model, LinkTableData.Node node) {
    Node modelNode;
    modelNode = ModelFactory.eINSTANCE.createNode();
    setID(modelNode, getNodeId(node));
    model.getNodes().add(modelNode);

    modelNode.setId(node.nodeId);
    modelNode.setX(node.locX);
    modelNode.setY(node.locY);

    return modelNode;
  }

  private void updateNode(Node modelNode, LinkTableData.Node node) {
    modelNode.setId(node.nodeId);
    modelNode.setX(node.locX);
    modelNode.setY(node.locY);
  }

  private Node getModelNode(LinkTableData.Node node) {
    return (Node) getEObjectByID(getNodeId(node));
  }

  private String getNodeId(LinkTableData.Node node) {
    return String.valueOf(node.nodeId);
  }

  public int getLinkTableId() {
    return linkTableId;
  }
}
