/*
 *  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 org.mmtk.vm.VM;
import org.mmtk.utility.gcspy.GCspy;

import static org.jikesrvm.runtime.SysCall.sysCall;
import org.jikesrvm.objectmodel.JavaHeaderConstants;

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

/**
 * Generic GCspy Server Interpreter
 *
 * This class implements the GCspy server.
 * The server runs as a separate pthread and communicates with GCspy
 * clients. It handles commands from the client and passes data to it.
 * Mostly it forwards calls to the C gcspy library.
 */
@Uninterruptible public class ServerInterpreter extends org.mmtk.vm.gcspy.ServerInterpreter
  implements JavaHeaderConstants {


  /**
   * Create a new ServerInterpreter singleton.
   * @param name The name of the server
   * @param port The number of the port on which to communicate
   * @param verbose Whether the server is to run verbosely
   */
  @Interruptible
  public void init(String name, int port, boolean verbose) {
    if (org.jikesrvm.VM.BuildWithGCSpy) {
      if (VM.VERIFY_ASSERTIONS)
        VM.assertions._assert(!initialised, "Tried to re-init server interpreter");
      initialised = true;

      if (DEBUG)
        Log.writeln("-- Initialising main server on port ",port);

      Address tmp = GCspy.util.getBytes(name);
      server = sysCall.gcspyMainServerInit(port, MAX_LEN, tmp, verbose?1:0);

      if (DEBUG) {
        Log.writeln("gcspy_main_server_t address = "); Log.writeln(server);
      }

      GCspy.util.free(tmp);
      // Set up the list of ServerSpaces
      spaces = new org.jikesrvm.mm.mmtk.gcspy.ServerSpace[MAX_SPACES];
    }
  }

  /**
   * Add an event to the ServerInterpreter.
   * @param num the event number
   * @param name the event name
   */
  public void addEvent(int num, String name) {
    if (org.jikesrvm.VM.BuildWithGCSpy) {
      if (VM.VERIFY_ASSERTIONS)
        VM.assertions._assert(initialised,
                       "ServerInterpreter.addEvent: server not initiialised");

      Address tmp = GCspy.util.getBytes(name);
      sysCall.gcspyMainServerAddEvent(server, num, tmp);
      GCspy.util.free(tmp);
    }
  }

  /**
   * Set the general info for the ServerInterpreter.
   * @param info the information
   */
  public void setGeneralInfo(String info) {
    if (org.jikesrvm.VM.BuildWithGCSpy) {
      if (VM.VERIFY_ASSERTIONS)
        VM.assertions._assert(initialised,
                       "ServerInterpreter.setGeneralInfo: server not initiialised");

      Address tmp = GCspy.util.getBytes(info);
      sysCall.gcspyMainServerSetGeneralInfo(server, tmp);
      GCspy.util.free(tmp);
    }
  }

  /**
   * Start the server, running its main loop in a pthread.
   * @param wait Whether to wait for the client to connect
   */
  public void startServer(boolean wait) {
    if (org.jikesrvm.VM.BuildWithGCSpy) {
      if (DEBUG) { Log.write("Starting GCSpy server, wait="); Log.writeln(wait); }

      Address serverOuterLoop = sysCall.gcspyMainServerOuterLoop();
      sysCall.gcspyStartserver(server, wait?1:0, serverOuterLoop);
    }
  }

  /**
   * Are we connected to a GCspy client?
   * @param event The current event
   * @return true if we are connected
   */
  public boolean isConnected(int event) {
    if (org.jikesrvm.VM.BuildWithGCSpy) {
      if (DEBUG)
        Log.writeln("ServerInterpreter.isConnected, server=", server);

      if (!initialised)
        return false;
      int res = sysCall.gcspyMainServerIsConnected(server, event);
      return (res != 0);
    } else {
      return false;
    }
  }

  /**
   * Start compensation timer so that time spent gathering data is
   * not confused with the time spent in the application and the VM.
   */
  public void startCompensationTimer() {
    if (org.jikesrvm.VM.BuildWithGCSpy) {
      if (VM.VERIFY_ASSERTIONS)
        VM.assertions._assert(initialised,
                       "ServerInterpreter.startCompensationTimer: server not initiialised");

      sysCall.gcspyMainServerStartCompensationTimer(server);
    }
  }

  /**
   * Stop compensation timer so that time spent gathering data is
   * not confused with the time spent in the application and the VM.r
   */
  public void stopCompensationTimer() {
    if (org.jikesrvm.VM.BuildWithGCSpy) {
      if (VM.VERIFY_ASSERTIONS)
        VM.assertions._assert(initialised,
                       "ServerInterpreter.stopCompensationTimer: server not initiialised");

      sysCall.gcspyMainServerStopCompensationTimer(server);
    }
  }

  /**
   * Indicate that we are at a server safe point (e.g. the end of a GC).
   * This is a point at which the server can pause, play one, etc.
   * @param event The current event
   */
  public void serverSafepoint(int event) {
    if (org.jikesrvm.VM.BuildWithGCSpy) {
      if (DEBUG)
        Log.writeln("ServerInterpreter.serverSafepoint, server=", server);

      if (!initialised)
        return;
      sysCall.gcspyMainServerSafepoint(server, event);
    }
  }

  /**
   * Discover the smallest header size for objects.
   * @return the size in bytes
   */
  public int computeHeaderSize() {
    return JAVA_HEADER_BYTES+OTHER_HEADER_BYTES;
  }
}
