private final void work_status(final GearmanPacket packet, final ServerClient client) {
    /*
     * This is sent to update the server (and any listening clients)
     * of the status of a running job. The client should send these
     * periodically for long running jobs to update the percentage
     * complete. The job server should store this information so a client
     * who issued a background command may retrieve it later with a
     * GET_STATUS request.
     *
     * Arguments:
     * - NULL byte terminated job handle.
     * - NULL byte terminated percent complete numerator.
     * - Percent complete denominator.
     */

    final byte[] jobHandle = packet.getArgumentData(0);
    assert jobHandle != null;
    final ByteArray jobHandleBA = new ByteArray(jobHandle);

    final byte[] num = packet.getArgumentData(1);
    assert num != null;

    final byte[] den = packet.getArgumentData(2);
    assert den != null;

    final ServerJob job = ServerJobAbstract.getJob(jobHandleBA);
    if (job == null) {
      client.sendPacket(ServerStaticPackets.ERROR_JOB_NOT_FOUND, null /*TODO*/);
    } else {
      packet.setMagic(Magic.RES);
      job.setStatus(num, den);
      job.sendPacket(packet);
    }
  }
  private final void work_data(final GearmanPacket packet, final ServerClient client) {
    /*
     * This is sent to update the client with data from a running job. A
     * client should use this when it needs to send updates, send partial
     * results, or flush data during long running jobs. It can also be
     * used to break up a result so the client does not need to buffer
     * the entire result before sending in a WORK_COMPLETE packet.
     *
     * Arguments:
     * - NULL byte terminated job handle.
     * - Opaque data that is returned to the client.
     */

    final byte[] jobHandle = packet.getArgumentData(0);
    assert jobHandle != null;
    final ByteArray jobHandleBA = new ByteArray(jobHandle);

    final ServerJob job = ServerJobAbstract.getJob(jobHandleBA);
    if (job == null) {
      client.sendPacket(ServerStaticPackets.ERROR_JOB_NOT_FOUND, null /*TODO*/);
    } else {
      packet.setMagic(Magic.RES);
      job.sendPacket(packet);
    }
  }
  /**
   * Called when a text-based packet is received. When a packet with type -1 is received, it's
   * assumed to be a text-based packet. The first argument (The command argument in a text-based
   * packet) is checked against the set of known commands. Then, the command is executed or an
   * exception is returned to the client if the command is unknown.
   *
   * @param packet The received packet with a type of -1
   * @param client The client who received the packet
   */
  private final void text_packet(final GearmanPacket packet, final ServerClient client) {
    final String pkt = new String(packet.toBytes(), GearmanConstants.UTF_8);
    final String[] args = pkt.trim().split("[ \t]");

    if (args[0].equalsIgnoreCase("workers")) {
      text_workers(args, client);
      return;
    } else if (args[0].equalsIgnoreCase("status")) {
      text_status(args, client);
      return;
    } else if (args[0].equalsIgnoreCase("maxqueue")) {
      text_maxqueue(args, client);
      return;
    } else if (args[0].equalsIgnoreCase("shutdown")) {
      text_shutdown(args, client);
      return;
    } else if (args[0].equalsIgnoreCase("version")) {
      text_version(args, client);
      return;
    } else if (args[0].equalsIgnoreCase("throttle")) {
      update_throttle(args, client);
      return;
    } else if (args[0].equalsIgnoreCase("memstats")) {
      getMemStats(client);
      return;
    } else if (args[0].equalsIgnoreCase("workertime")) {
      worker_time_status(args, client);
      return;
    } else {
      client.sendPacket(ServerStaticPackets.TEXT_UNKNOWN_COMMAND, null /*TODO*/);
    }
  }
  private final void option_req(final GearmanPacket packet, final ServerClient client) {
    /*
     * A client issues this to set an option for the connection in the
     * job server. Returns a OPTION_RES packet on success, or an ERROR
     * packet on failure.
     *
     * Arguments:
     * - Name of the option to set. Possibilities are:
     * 		"exceptions" - Forward WORK_EXCEPTION packets to the client.
     */

    final byte[] option = packet.getArgumentData(0);
    assert option != null;

    final byte[] exceptions = new byte[] {'e', 'x', 'c', 'e', 'p', 't', 'i', 'o', 'n', 's'};

    if (Arrays.equals(option, exceptions)) {
      // exceptions option
      client.setForwardsExceptions(true);

      client.sendPacket(ServerStaticPackets.OPTION_RES_EXCEPTIONS, null /*TODO*/);

    } else {
      // unknown option
      client.sendPacket(ServerStaticPackets.ERROR_UNKNOWN_OPTION, null /*TODO*/);
    }
  }
  private final void get_status(final GearmanPacket packet, final ServerClient client) {

    /*
     * A client issues this to get status information for a submitted job.
     *
     * Arguments:
     *  - Job handle that was given in JOB_CREATED packet.
     */

    final byte[] jobHandle = packet.getArgumentData(0);
    assert jobHandle != null;
    final ByteArray jobHandleBA = new ByteArray(jobHandle);

    final ServerJob job = ServerJobAbstract.getJob(jobHandleBA);
    if (job == null) {
      // Send unknown job STATUS_RES packet
      final byte[] unknown = new byte[] {'0'};
      final GearmanPacket status_res =
          new GearmanPacket(
              Magic.RES, Type.STATUS_RES, jobHandle, unknown, unknown, unknown, unknown);
      client.sendPacket(status_res, null /*TODO*/);
      return;
    }

    final GearmanPacket status = job.createStatusResPacket();

    client.sendPacket(status, null /*TODO*/);
  }
  private final void submit_job(
      final GearmanPacket packet,
      final ServerClient client,
      ServerJob.JobPriority priority,
      boolean isBackground) {

    /*
     * A client issues this when a job needs to be run. The server will
     * then assign a job handle and respond with a JOB_CREATED packet.
     *
     * If on of the BG versions is used, the client is not updated with
     * status or notified when the job has completed (it is detached).
     *
     * The Gearman job server queue is implemented with three levels:
     * normal, high, and low. Jobs submitted with one of the HIGH versions
     * always take precedence, and jobs submitted with the normal versions
     * take precedence over the LOW versions.
     *
     * Arguments:
     * - NULL byte terminated function name.
     * - NULL byte terminated unique ID.
     * - Opaque data that is given to the function as an argument.
     */

    // Argument: function name
    final byte[] funcName = packet.getArgumentData(0);
    assert funcName != null;
    final ByteArray funcNameBA = new ByteArray(funcName);

    // Argument: unique ID
    final byte[] uniqueID = packet.getArgumentData(1);
    assert uniqueID != null;
    final ByteArray uniqueIDBA = new ByteArray(uniqueID);

    // Argument: data
    final byte[] data = packet.getArgumentData(2);
    assert data != null;

    /*
     * FWIX CHANGE
     */
    // final ServerFunction func = this.funcSetHigh.getFunction(funcNameBA);

    funcSet.createJob(funcNameBA, uniqueIDBA, data, priority, client, isBackground);
  }
  private final void work_exception(final GearmanPacket packet, final ServerClient client) {
    /*
     * This is to notify the server (and any listening clients) that
     * the job failed with the given exception.
     *
     * Arguments:
     * - NULL byte terminated job handle.
     * - Opaque data that is returned to the client as an exception.
     */

    // Note: The protocol states this packet notifies the server that the specified job
    // has failed. However, the C server does not fail the Job.  It is effectively a
    // WORK_WARNING packet that is only sent to clients that have specified they want
    // exceptions forwarded to them.  This server will do the same as long as the C
    // server does so.

    /*
     * This is sent to update the client with data from a running job. A
     * client should use this when it needs to send updates, send partial
     * results, or flush data during long running jobs. It can also be
     * used to break up a result so the client does not need to buffer
     * the entire result before sending in a WORK_COMPLETE packet.
     *
     * Arguments:
     * - NULL byte terminated job handle.
     * - Opaque data that is returned to the client.
     */

    final byte[] jobHandle = packet.getArgumentData(0);
    ;
    assert jobHandle != null;
    final ByteArray jobHandleBA = new ByteArray(jobHandle);

    final ServerJob job = ServerJobAbstract.getJob(jobHandleBA);
    if (job == null) {
      client.sendPacket(ServerStaticPackets.ERROR_JOB_NOT_FOUND, null /*TODO*/);
    } else {
      packet.setMagic(Magic.RES);
      job.sendPacket(packet);
    }
  }
  private final void echo_req(final GearmanPacket packet, final ServerClient client) {
    /*
     * When a job server receives this request, it simply generates a
     * ECHO_RES packet with the data. This is primarily used for testing
     * or debugging.
     *
     * Arguments:
     * - Opaque data that is echoed back in response.
     */

    packet.setMagic(Magic.RES);
    client.sendPacket(packet, null /*TODO*/);
  }
  public void getMemStats(final ServerClient client) {

    StringBuilder sb = new StringBuilder();
    sb.append(Main.memUsed());
    sb.append('\t');
    sb.append(Main.getThreadCount());
    sb.append('\t');
    sb.append(Main.getActiveThreadCount());
    sb.append('\t');
    sb.append(Main.getUpTime());
    sb.append('\n');
    client.sendPacket(GearmanPacket.createTEXT(sb.toString()), null /*TODO*/);
    return;
  }
  /**
   * Called when a CANT_DO packet comes in.<br>
   * <br>
   * <i> This is sent to notify the server that the client is no longer able to perform the given
   * function.<br>
   * <br>
   * Arguments:<br>
   * - Function name.<br>
   * </i>
   *
   * @param packet The CANT_DO packet
   * @param client The client who aquired the packet
   */
  private final void cant_do(final GearmanPacket packet, final ServerClient client) {
    /*
     * This is sent to notify the server that the client is no longer able to
     * perform the given function.
     *
     * Arguments:
     * - Function name.
     */

    // Function Name
    final byte[] funcName = packet.getArgumentData(0);
    assert funcName != null;
    if (funcName.length == 0) {
      // TODO send error
    }

    client.cant_do(new ByteArray(funcName));
  }
  /**
   * Called when a CAN_DO packet comes in.<br>
   * <br>
   * <i> CAN_DO:<br>
   * This is sent to notify the server that the client is able to perform the given function. The
   * client is then put on a list to be waken up whenever the job server receives a job for that
   * function.<br>
   * <br>
   * Arguments:<br>
   * - Function name.<br>
   * </i>
   *
   * @param packet The CAN_DO packet
   * @param client The client who acquired the packet.
   */
  private final void can_do(final GearmanPacket packet, final ServerClient client) {

    // Note: Currently the CAN_DO_TIMEOUT maps to this method, the timeout
    // feature will be fully implemented in the future.

    // Function Name
    final byte[] funcName = packet.getArgumentData(0);
    assert funcName != null;

    if (funcName.length == 0) {
      // TODO send error
    }

    ByteArray funcNameBA = new ByteArray(funcName);
    // funcNameBA = GMServerFunctionMap.GM_TASK_BA;

    funcSet.registerClient(funcNameBA, client);
  }
  private final void work_fail(final GearmanPacket packet, final ServerClient client) {
    /*
     * This is to notify the server (and any listening clients) that
     * the job failed.
     *
     * Arguments:
     * - Job handle.
     */

    final byte[] jobHandle = packet.getArgumentData(0);
    assert jobHandle != null;
    final ByteArray jobHandleBA = new ByteArray(jobHandle);

    final ServerJob job = ServerJobAbstract.getJob(jobHandleBA);
    if (job == null) {
      client.sendPacket(ServerStaticPackets.ERROR_JOB_NOT_FOUND, null /*TODO*/);
    } else {
      job.workComplete(packet);
    }
  }
  private final void set_client_id(final GearmanPacket packet, final ServerClient client) {
    /*
     * This sets the client ID in a job server so monitoring and reporting
     * commands can uniquely identify the various clients, and different
     * connections to job servers from the same client.
     *
     * Arguments:
     * - Unique string to identify the client instance.
     */

    // Get Client ID from packet
    final byte[] clientIdBytes = packet.getArgumentData(0);
    assert clientIdBytes != null;

    // Convert the worker ID into a String
    final String clientId = new String(clientIdBytes, GearmanConstants.UTF_8);

    // Set the client's client ID
    client.setClientId(clientId);
  }
  public void update_throttle(
      final String[] args, final ServerClient client) { // String function, Integer throttle){
    String function = args[1];
    Integer throttle = 0;
    StringBuilder sb = new StringBuilder();
    if (args[2].equalsIgnoreCase("NOTHROTTLE")) {
      System.out.println("REMOVE THROTTLE" + function);
      Main.removeThrottle(function);
      sb.append(function);
      sb.append('\t');
      sb.append("remove throttle");
      sb.append('\n');
    } else if (args[2].equalsIgnoreCase("KILL")) {

      System.out.println("KILL FUNCTION");
      RemoveClass remove = new RemoveClass(function);
      Thread t = new Thread(remove);
      t.start();

      sb.append(function);
      sb.append('\t');
      sb.append("kill");
      sb.append('\n');

    } else {

      throttle = Integer.parseInt(args[2]);
      ;
      if (throttle != null && throttle >= 0) {
        System.out.println("Putthing throttle: " + throttle + " for function " + function);
        Main.setThrottle(function, throttle);
        sb.append(function);
        sb.append('\t');
        sb.append(throttle);
        sb.append('\n');
      }
    }

    client.sendPacket(GearmanPacket.createTEXT(sb.toString()), null /*TODO*/);
    return;
  }
  private final void work_complete(final GearmanPacket packet, final ServerClient client) {
    /*
     * This is to notify the server (and any listening clients) that
     * the job completed successfully.
     *
     * Arguments:
     * - NULL byte terminated job handle.
     * - Opaque data that is returned to the client as a response.
     */

    final byte[] jobHandle = packet.getArgumentData(0);
    assert jobHandle != null;
    final ByteArray jobHandleBA = new ByteArray(jobHandle);

    final ServerJob job = ServerJobAbstract.getJob(jobHandleBA);
    if (job == null) {
      client.sendPacket(ServerStaticPackets.ERROR_JOB_NOT_FOUND, null /*TODO*/);
    } else {

      // Construct a WORK_COMPLETE response packet
      client.jobCOmplete();
      job.workComplete(packet);
    }
  }
  /**
   * Once a packet has been acquired from a client, it's processed here. The packet is processed in
   * compliance with the gearman protocol.
   *
   * @param packet The packet sent by the client
   * @param client The client associated with the client that sent the packet
   */
  public final void execute(final GearmanPacket packet, final ServerClient client) {

    switch (packet.getPacketType()) {

        // Text Packets
      case TEXT:
        text_packet(packet, client);
        return;

        // Binary Packets
      case CAN_DO:
        can_do(packet, client);
        return;
      case CAN_DO_TIMEOUT:
        can_do(packet, client);
        return;
      case CANT_DO:
        cant_do(packet, client);
        return;
      case ECHO_REQ:
        echo_req(packet, client);
        return;
      case GET_STATUS:
        get_status(packet, client);
        return;
      case GRAB_JOB:
        grab_job(packet, client);
        return;
      case GRAB_JOB_UNIQ:
        grab_job_uniq(packet, client);
        return;
      case OPTION_REQ:
        option_req(packet, client);
        return;
      case PRE_SLEEP:
        pre_sleep(packet, client);
        return;
      case RESET_ABILITIES:
        System.out.println("New worker has joined");
        reset_abilities(packet, client);
        return;
      case SET_CLIENT_ID:
        set_client_id(packet, client);
        return;
      case SUBMIT_JOB:
        submit_job(packet, client, ServerJob.JobPriority.MID, false);
        return;
      case SUBMIT_JOB_BG:
        submit_job(packet, client, ServerJob.JobPriority.MID, true);
        return;
      case SUBMIT_JOB_HIGH:
        submit_job(packet, client, ServerJob.JobPriority.HIGH, false);
        return;
      case SUBMIT_JOB_HIGH_BG:
        submit_job(packet, client, ServerJob.JobPriority.HIGH, true);
        return;
      case SUBMIT_JOB_LOW:
        submit_job(packet, client, ServerJob.JobPriority.LOW, false);
        return;
      case SUBMIT_JOB_LOW_BG:
        submit_job(packet, client, ServerJob.JobPriority.LOW, true);
        return;
      case WORK_COMPLETE:
        work_complete(packet, client);
        return;
      case WORK_DATA:
      case WORK_WARNING:
        work_data(packet, client);
        return;
      case WORK_EXCEPTION:
        work_exception(packet, client);
        return;
      case WORK_FAIL:
        work_fail(packet, client);
        return;
      case WORK_STATUS:
        work_status(packet, client);
        return;

        // Response Only Packets
      case NOOP:
      case JOB_CREATED:
      case NO_JOB:
      case ECHO_RES:
      case ERROR:
      case STATUS_RES:
      case OPTION_RES:
      case JOB_ASSIGN:
      case JOB_ASSIGN_UNIQ:

        // Packets Not Yet Implemented
      case ALL_YOURS:
      case SUBMIT_JOB_EPOCH:
      case SUBMIT_JOB_SCHED:
        client.sendPacket(ServerStaticPackets.ERROR_BAD_COMMAND, null /*TODO*/);
        return;

        // Unknown Command
      default:
        client.sendPacket(ServerStaticPackets.ERROR_BAD_COMMAND, null /*TODO*/);
        return;
    }
  }