public double computeEncodingTime(IChunk visualChunk, IVisualModule module) {
    IChunk error = module.getModel().getDeclarativeModule().getErrorChunk();

    if (error.equals(visualChunk))
      return module.getModel().getProceduralModule().getDefaultProductionFiringTime();

    return 0.085;
  }
  @Override
  protected boolean removeSourceChunkInternal(IChunk chunkToRemove) {
    if (LOGGER.isDebugEnabled())
      LOGGER.debug("attempting to remove " + chunkToRemove + " from " + getName());

    if (_times.containsKey(chunkToRemove)) {
      if (LOGGER.isDebugEnabled()) LOGGER.debug("removing and setting status slots");

      double time = _times.remove(chunkToRemove);
      _sourceChunks.remove(time);

      chunkRemoved(chunkToRemove);

      if (LOGGER.isDebugEnabled()) {
        // TODO remove debugging code
        LOGGER.debug(getName() + " Source Chunks & Times " + _sourceChunks);
        for (IChunk chunk : _sourceChunks.values())
          if (chunk.isEncoded()) LOGGER.debug("WARNING : " + chunk + " has already been encoded");
      }

      return true;
    } else
      /*
       * TODO remove debugging code safety check while I debug this issue: in
       * some cases, the chunk (in the buffer) is being encoded and merged with
       * an existing chunk - now, the buffer should not contain any encoded
       * chunks, but somehow this one is still in here.. the problem is that the
       * merging process changes the hashcode which will mean that the above
       * check will fail.. this finds the problem and throws the appropriate
       * exception so we can stop the execution
       */
      /*
       * problem found: two buffers, with the same chunk, one removes and
       * encodes. solution: make all insertions copies
       */
      for (IChunk source : _times.keySet())
        if (source.equals(chunkToRemove))
          throw new IllegalStateException(
              "Mayday, hashcode has changed for encoded chunk " + chunkToRemove);

    if (LOGGER.isDebugEnabled()) LOGGER.debug("nothing to remove");
    return false;
  }
  /** when a chunk is matched against, we modify its insertion time.. */
  @Override
  protected boolean matchedInternal(IChunk chunk) {

    if (!_times.containsKey(chunk)) {
      if (LOGGER.isWarnEnabled()) LOGGER.warn(chunk + " was matched, but is not in the buffer?");
      return false;
    }

    /*
     * if LRA, we've done all we need to
     */
    EjectionPolicy policy = getEjectionPolicy();

    if (policy == EjectionPolicy.LeastRecentlyAdded || policy == EjectionPolicy.MostRecentlyAdded)
      return true;

    /*
     * now we can tweak the access time, but only if the policy is LRU or LRM
     */
    double oldTime = _times.get(chunk);
    double newTime = ACTRRuntime.getRuntime().getClock(getModel()).getTime();

    _sourceChunks.remove(oldTime);
    /*
     * make sure there are no collisions
     */
    while (_sourceChunks.containsKey(newTime) && !chunk.equals(_sourceChunks.get(newTime)))
      /*
       * problem of more than one chunk inserted at the same time (could happen
       * during path-integration)
       */
      newTime += 0.000001;

    _times.put(chunk, newTime);
    _sourceChunks.put(newTime, chunk);

    if (LOGGER.isDebugEnabled())
      LOGGER.debug(getName() + " Source Chunks & Times " + _sourceChunks);
    return true;
  }
  @Override
  protected IChunk addSourceChunkInternal(IChunk chunkToInsert) {
    if (LOGGER.isDebugEnabled()) LOGGER.debug("attempting to insert " + chunkToInsert);

    /*
     * ok, something will be changing..
     */
    IChunk errorChunk = getErrorChunk();

    /*
     * did something go wrong? set the states..
     */
    if (errorChunk.equals(chunkToInsert)
        || !isValidChunkType(chunkToInsert.getSymbolicChunk().getChunkType())) {
      if (LOGGER.isDebugEnabled())
        LOGGER.debug(getName() + " : " + chunkToInsert + " was error or invalid of chunk type");
      setStateChunk(errorChunk);
      chunkToInsert = null;
    }

    /*
     * all is good, let's set the chunk
     */
    if (chunkToInsert != null) {
      /*
       * are we bumping up against the maximum capacity?
       */
      ensureCapacity();

      double now = ACTRRuntime.getRuntime().getClock(getModel()).getTime();

      double time = Double.MIN_VALUE;

      /*
       * if LRA or LRU, time will be now, if modified, we set it as minimum
       * double value (can't be negative inf because of potential value
       * collisions, see below)
       */
      switch (getEjectionPolicy()) {
        case LeastRecentlyAdded:
        case LeastRecentlyUsed:
        case MostRecentlyAdded:
        case MostRecentlyUsed:
          time = now;
          break;
      }

      /*
       * problem of more than one chunk inserted at the same time (could happen
       * if a production fires multiple adds)
       */
      while (_sourceChunks.containsKey(time) && !chunkToInsert.equals(_sourceChunks.get(time)))
        time += 0.0000001;

      _sourceChunks.put(time, chunkToInsert);
      _times.put(chunkToInsert, time);

      if (LOGGER.isDebugEnabled())
        LOGGER.debug(getName() + " Source Chunks & Times " + _sourceChunks);

      IModel model = getModel();
      if (LOGGER.isDebugEnabled() || Logger.hasLoggers(model)) {
        IMessageBuilder sb = Logger.messageBuilder();
        sb.append(getName())
            .append(" inserted ")
            .append(chunkToInsert.getSymbolicChunk().getName());

        if (LOGGER.isDebugEnabled()) LOGGER.debug(sb.toString());

        Logger.log(model, Logger.Stream.BUFFER, sb);
      }

      chunkInserted(chunkToInsert);
    }

    return chunkToInsert;
  }