
// VisSpace.java
// Michele Loreti and the jSpace Developers, 2017
// Andrew Davison, May 2023, ad@coe.psu.ac.th

/* The tuple code in this class is based on the
   implementation of the SequentialSpace class

   VisSpace implements the Space class so that it can 
   be used as a drop-in replacement for SequentialSpace. 

   It adds tuple visualization support
   by calling SpaceViewer in each tuple method, and also
   slows down the operations using a delay so that
   the visualization can be better understood. 

  Usage:
    > compile.bat VisSpace.java
*/

import java.util.*;

import org.jspace.*;


public class VisSpace implements Space
{
  private static final int DELAY = 2000;  // ms
       // the default slowdown is 2 secs, which is SLOW!

  private LinkedList<Tuple> tuples = new LinkedList<>();
  private SpaceViewer viewer;
  private int delay;


  public VisSpace()
  {  this(null, DELAY);  }

  public VisSpace(int delay)
  {  this(null, delay);  }

  public VisSpace(String title)
  {  this(title, DELAY);  }


  public VisSpace(String title, int delay)
  {  viewer = SpaceViewer.view(title);
     this.delay = delay;
  }


  public synchronized int size()
  {  return tuples.size();   } 


  public synchronized boolean put(Object ... fields) 
                                throws InterruptedException 
  {
    // viewing details
    String id = Thread.currentThread().getName();
    String tupleStr = shorten(Arrays.deepToString(fields));
                                       // make a string copy
    // pr(id, "put", tupleStr);
    viewer.addTuple(id, tupleStr);
    SpaceUtils.pause(delay);

    // tuple space code
  	tuples.add(new Tuple(Arrays.copyOf(fields, fields.length)));
  	notifyAll();
  	return true;
  }  // end of put()


  private String shorten(String s)
  /* reduce the size of the tuple string so it
     can be nicely drawn by SpaceViewer
  */ 
  { s = s.replaceAll("java.lang.","");
    if (s.length() > 33)
      s = s.substring(0,30) + "...";
    return s;
  }  


  // read a tuple ----------------------



  public synchronized Object[] get(TemplateField ... fields) 
                                    throws InterruptedException
  // Remove a tuple matching the template; blocking
  {
    // viewing details
    String id = Thread.currentThread().getName();
    String queryStr = shorten(Arrays.deepToString(fields));
                                       // make a string copy
    // pr(id, "get", queryStr);
    viewer.addQuery(id, queryStr);
    SpaceUtils.pause(delay);

    while (true) {
      Tuple tup = findTuple( new Template(
                Arrays.copyOf(fields, fields.length)), true);

      if (tup != null) {
        // more viewing details
        String tupleStr = shorten(Arrays.deepToString(
                                     tup.getTuple()));  // copy
        // pr(id, "get linking", tupleStr);
        viewer.linkQueryTuple(queryStr, tupleStr, true);
          /* a 'link' is a line drawn by SpaceViewer between
             the query and matching tuple */
        SpaceUtils.pause(delay);  
        // after a delay, delete viewing stuff
        viewer.removeLink(queryStr, tupleStr);
        viewer.removeQuery(queryStr);
        viewer.removeTuple(tupleStr);

        // tuple space code
        notifyAll();
        return tup.getTuple();
      }
      wait();
    }
  } /* get */


  private void pr(String id, String op, String msg)
  // debugging print
  {
    if (id == null)
      System.out.println(op + ": " + msg);
    else
      System.out.println("(" + id + ") " + op + ": " + msg);
  }  // end of pr()



  public synchronized Object[] getp(TemplateField ... fields)
  // Tries to remove a tuple matching the template; non-blocking
  {
    // viewing details
    String id = Thread.currentThread().getName();
    String queryStr = shorten(Arrays.deepToString(fields));  // copy
    // pr(id, "getp", queryStr);
    viewer.addQuery(id, queryStr);
    SpaceUtils.pause(delay);

    Tuple tup = findTuple( new Template(
               Arrays.copyOf(fields,fields.length)), true);

    if (tup != null) {
      // more viewing details
      String tupleStr = shorten(Arrays.deepToString(
                                    tup.getTuple()));  // copy
      // pr(id, "getp linking", tupleStr);
      viewer.linkQueryTuple(queryStr, tupleStr, true);
      SpaceUtils.pause(delay);
      // after a delay, delete viewing stuff
      viewer.removeLink(queryStr, tupleStr);
      viewer.removeQuery(queryStr);
      viewer.removeTuple(tupleStr);

      notifyAll();
      return tup.getTuple();
    }
    else {
      viewer.removeQuery(queryStr);
      // pr(id, "getp", "No tuple found");
      return null;
    }
  } /* getp */



  public synchronized Object[] query(TemplateField ... fields) 
                            throws InterruptedException
  // Reads a tuple matching the template; blocking
  {
    // viewing details
    String id = Thread.currentThread().getName();
    String queryStr = shorten(Arrays.deepToString(fields));  // copy
    // pr(id, "query", queryStr);
    viewer.addQuery(id, queryStr);
    SpaceUtils.pause(delay);

    while(true) {
      Tuple tup = findTuple( new Template(
               Arrays.copyOf(fields, fields.length)), false);

      if (tup != null) {
        // more viewing details
        String tupleStr = shorten(Arrays.deepToString(
                                    tup.getTuple()));  // copy
        // pr(id, "query linking", tupleStr);      
        viewer.linkQueryTuple(queryStr, tupleStr, false);
        SpaceUtils.pause(delay);
        // after a delay, delete viewing stuff
        viewer.removeLink(queryStr, tupleStr);
        viewer.removeQuery(queryStr);

        return tup.getTuple();
      }
      wait();
    }
  } /* query */



  public synchronized Object[] queryp(TemplateField ... fields)
  // Reads a tuple matching the template; non-blocking
  {
    // viewing details
    String id = Thread.currentThread().getName();
    String queryStr = shorten(Arrays.deepToString(fields));  // copy
    // pr(id, "queryp", queryStr);
    viewer.addQuery(id, queryStr);
    SpaceUtils.pause(delay);

    Tuple tup = findTuple( new Template(
               Arrays.copyOf(fields,fields.length)), false);

    if (tup != null) {
      // more viewing details
      String tupleStr = shorten(Arrays.deepToString(
                                         tup.getTuple()));  // copy
      // System.out.println("queryp linking: " + tupleStr);
      viewer.linkQueryTuple(queryStr, tupleStr, false);
      SpaceUtils.pause(delay);
      // after a delay, delete viewing stuff
      viewer.removeLink(queryStr, tupleStr);
      viewer.removeQuery(queryStr);

      return tup.getTuple();
    }
    else {
      viewer.removeQuery(queryStr);
      // pr(id, "queryp", "No tuple found");
      return null;
    }
  }  // end of queryp()



  private synchronized Tuple findTuple(Template template,
                                       boolean toRemove)
  {
    Iterator<Tuple> tuplesIter = tuples.iterator();
    while (tuplesIter.hasNext()) {
      Tuple t = tuplesIter.next();
      if (template.match(t)) {
        if (toRemove)
          tuplesIter.remove();
        return t;
      }
    }
    return null;
  } /* findTuple */



  // read multiple tuples ----------------------


  public synchronized LinkedList<Object[]> 
                                getAll(TemplateField ... fields)
  // Removes tuples matching the template; blocking
  {
    // viewing details
    String id = Thread.currentThread().getName();
    String queryStr = shorten(Arrays.deepToString(fields));  // copy
    // pr(id, "getAll", queryStr);
    viewer.addQuery(id, queryStr);
    SpaceUtils.pause(delay);

    LinkedList<Object[]> tups = findAllTuples(new Template(
               Arrays.copyOf(fields,fields.length)),true);

    if (tups.size() > 0) {
      // more viewing details
      List<String> tupleStrs = new ArrayList<>();
      for (Object[] tup : tups) {
        String tupleStr = shorten(Arrays.deepToString(tup));  // copy
        // pr(id, "getAll - linking", tupleStr);
        viewer.linkQueryTuple(queryStr, tupleStr, true);
        tupleStrs.add(tupleStr);
      }
      SpaceUtils.pause(delay);
      // after a delay, delete viewing stuff
      for (String tupleStr : tupleStrs)
        viewer.removeLink(queryStr, tupleStr);
      viewer.removeQuery(queryStr);

      notifyAll();
    }
    return tups;
  } /* getAll */



  public synchronized LinkedList<Object[]>  
                            queryAll(TemplateField ... fields)
  // Reads tuples matching the template; non-blocking
  {
    // viewing details
    String id = Thread.currentThread().getName();
    String queryStr = shorten(Arrays.deepToString(fields));  // copy
    // pr(id, "queryAll", queryStr);
    viewer.addQuery(id, queryStr);
    SpaceUtils.pause(delay);

    LinkedList<Object[]> tups = findAllTuples( new Template(
               Arrays.copyOf(fields,fields.length)), false);

    if (tups != null) {
      // more viewing details
      List<String> tupleStrs = new ArrayList<>();
      for (Object[] tup : tups) {
        String tupleStr = shorten(Arrays.deepToString(tup));  // copy
        // pr(id, "queryAll - linking", tupleStr);
        viewer.linkQueryTuple(queryStr, tupleStr, false);
        tupleStrs.add(tupleStr);
      }
      SpaceUtils.pause(delay);
      // after a delay, delete viewing stuff
      for (String tupleStr : tupleStrs)
        viewer.removeLink(queryStr, tupleStr);
      viewer.removeQuery(queryStr);
    }
    else {
      viewer.removeQuery(queryStr);
      pr(id, "queryAll", "No tuples found");
    }
    return tups;
  }  // end of queryAll()



  private synchronized LinkedList<Object[]> 
              findAllTuples(Template template, boolean toRemove)
  {
    LinkedList<Object[]> tups = new LinkedList<Object[]>();
    Iterator<Tuple> tuplesIter = tuples.iterator();
    Tuple t;
    while (tuplesIter.hasNext()) {
      t = tuplesIter.next();
      if (template.match(t)) {
        tups.add(t.getTuple());
        if (toRemove)
          tuplesIter.remove();
      }
    }
    return tups;
  } /* findAllTuples */


}  // end of VisSpace class