/*
 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
 *
 *  This file is licensed to You under the Common Public License (CPL);
 *  You may not use this file except in compliance with the License. You
 *  may obtain a copy of the License at
 *
 *      http://www.opensource.org/licenses/cpl1.0.php
 *
 *  See the COPYRIGHT.txt file distributed with this work for information
 *  regarding copyright ownership.
 */
package org.jikesrvm.mm.mmtk.gcspy;

import org.mmtk.utility.Log;
import static org.jikesrvm.runtime.SysCall.sysCall;
import org.jikesrvm.VM;
import org.mmtk.utility.gcspy.GCspy;
import org.mmtk.utility.gcspy.drivers.AbstractDriver;

import org.vmmagic.unboxed.*;
import org.vmmagic.pragma.*;

/**
 * ServerSpace.java
 *
 * This class implements the GCspy Space abstraction.
 * Here, it largely to forward calls to the gcspy C library.
 */
@Uninterruptible public class ServerSpace extends org.mmtk.vm.gcspy.ServerSpace {

  /**
   * Create a new GCspy Space
   *
   * @param serverInterpreter The server that owns this space
   * @param serverName The server's name
   * @param driverName The space driver's name
   * @param title Title for the space
   * @param blockInfo A label for each block
   * @param tileNum Max number of tiles in this space
   * @param unused A label for unused blocks
   * @param mainSpace Whether this space is the main space
   */
  public ServerSpace(
              org.mmtk.vm.gcspy.ServerInterpreter serverInterpreter,
              String serverName,
              String driverName,
              String title,
              String blockInfo,
              int tileNum,
              String unused,
              boolean mainSpace) {
    if (VM.BuildWithGCSpy) {
      spaceId = serverInterpreter.addSpace(this);
      driver = sysCall.gcspyMainServerAddDriver(serverInterpreter.getServerAddress());

      // Convert Strings to char *
      Address serverNameAddr = GCspy.util.getBytes(serverName);
      Address driverNameAddr = GCspy.util.getBytes(driverName);
      Address titleAddr      = GCspy.util.getBytes(title);
      Address blockInfoAddr  = GCspy.util.getBytes(blockInfo);
      Address unusedAddr     = GCspy.util.getBytes((unused == null) ? DEFAULT_UNUSED_STRING : unused);

      // Add the driver to the server and initialise it
      if (DEBUG) Log.writeln("--   Setting up driver");
      sysCall.gcspyDriverInit(driver, -1, serverNameAddr, driverNameAddr,
                                 titleAddr, blockInfoAddr, tileNum,
                                 unusedAddr, mainSpace ? 1 : 0);
    }
  }


  /****************************************************************************
   *
   * Interface to the GCspy C library
   */


  /**
   * Tell the native driver the tile name.
   * @param i the number of the tile
   * @param start the starting address of the tile
   * @param end the end address
   */
  public void setTilename(int i, Address start, Address end) {
    if (VM.BuildWithGCSpy)
      sysCall.gcspyDriverSetTileNameRange(driver, i, start, end);
  }

  /**
   * Tell the native driver the tile name.
   * @param i the number of the tile
   * @param format the name of the tile, a format string
   * @param value The value for the format string
   */
  public void setTilename(int i, Address format, long value) {
    if (VM.BuildWithGCSpy)
      sysCall.gcspyDriverSetTileName(driver, i, format, value);
  }

  /**
   * Tell the native driver the tile names.
   * @param i the number of the tile
   * @param format The name, including format tags
   * @param value The value for the format string
   */
  public void setTilename(int i, String format, long value) {
    if (VM.BuildWithGCSpy) {
      Address tileName = GCspy.util.getBytes(format);
      setTilename(i, tileName, value);
      GCspy.util.free(tileName);
    }
  }

  /**
   * Tell the C driver to resize
   * @param size the new driver size
   */
  public void resize(int size) {
    if (VM.BuildWithGCSpy)
      sysCall.gcspyDriverResize(driver, size);
  }

  /**
   * Start a transmission
   */
  public void startCommunication() {
    if (VM.BuildWithGCSpy)
      sysCall.gcspyDriverStartComm(driver);
  }

  /**
   * Add a stream to the native driver
   * @param streamId the stream's ID
   * @return the address of the C gcspy_gc_stream_t
   */
  public Address addStream(int streamId) {
    if (VM.BuildWithGCSpy)
      return sysCall.gcspyDriverAddStream(driver, streamId);
    else
      return Address.zero();
  }

  /**
   * Start transmitting a stream.
   * @param streamId The stream's ID
   * @param len The number of items in the stream
   */
  public void stream(int streamId, int len) {
    if (VM.BuildWithGCSpy)
      sysCall.gcspyDriverStream(driver, streamId, len);
  }

  /**
   * Send a byte
   * @param value The byte
   */
  public void streamByteValue(byte value) {
    if (VM.BuildWithGCSpy)
      sysCall.gcspyDriverStreamByteValue(driver, value);
  }

  /**
   * Send a short
   * @param value The short
   */
  public void streamShortValue(short value) {
    if (VM.BuildWithGCSpy)
      sysCall.gcspyDriverStreamShortValue(driver, value);
  }

  /**
   * Send an int
   * @param value The int
   */
  public void streamIntValue(int value) {
    if (VM.BuildWithGCSpy)
      sysCall.gcspyDriverStreamIntValue(driver, value);
  }

  /**
   * End of this stream
   */
  public void streamEnd() {
    if (VM.BuildWithGCSpy)
      sysCall.gcspyDriverEndOutput(driver);
  }

  /**
   * Start to send a summary
   * @param streamId The stream's ID
   * @param len The number of items to be sent
   */
  public void summary(int streamId, int len) {
    if (VM.BuildWithGCSpy)
      sysCall.gcspyDriverSummary(driver, streamId, len);
  }

  /**
   * Send a summary value
   * @param val The value
   */
  public void summaryValue(int val) {
    sysCall.gcspyDriverSummaryValue(driver, val);
  }

  /**
   * End the summary
   */
  public void summaryEnd() {
    if (VM.BuildWithGCSpy)
      sysCall.gcspyDriverEndOutput(driver);
  }

  /**
   * Send all the control info for the space
   * @param space The GCspy driver for this space
   * @param tileNum The number of tiles
   */
  public void sendControls(AbstractDriver space, int tileNum) {
    // start the stream
    if (VM.BuildWithGCSpy) {
      sysCall.gcspyDriverInitOutput(driver);
      sysCall.gcspyIntWriteControl(
                              driver/* NOTE driver->interpreter in sys.C*/,
                              spaceId,
                              tileNum);

      // send a control for each tile
      for (int i = 0; i < tileNum; ++i)
        streamByteValue(space.getControl(i));

      // end the stream
      sysCall.gcspyDriverEndOutput(driver);
    }
  }

  /**
   * Send info for this space
   * @param info A pointer to the information (held as C string)
   */
  public void spaceInfo(Address info) {
    if (VM.BuildWithGCSpy)
      sysCall.gcspyDriverSpaceInfo(driver, info);
  }
}
