/*
 *  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 test.org.jikesrvm.basic.core.threads;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;

/**
 * Utility class to make it easier to generate deterministic output by caching output per thread and outputting it at end.
 */
abstract class XThread extends Thread {

  static class ThreadRecord {
    final String name;
    final LinkedList<String> messages = new LinkedList<String>();
    final Thread thread;

    ThreadRecord(final String name, final Thread thread) {
      this.name = name;
      this.thread = thread;
    }
  }

  private static final Map<Integer, ThreadRecord> records = new HashMap<Integer, ThreadRecord>();
  private static final ThreadRecord mainRecord = new ThreadRecord("Main", null);

  static {
    records.put(0, mainRecord);
  }

  private static int maxId = 1;
  static boolean holdMessages = !"false".equals(System.getProperty("tests.DeterministicOutput"));

  private final ThreadRecord record;
  private final int id;
  boolean completed;
  boolean running;

  public XThread(String name) {
    super(name);
    synchronized (XThread.class) {
      id = maxId++;
    }
    record = new ThreadRecord(name, this);
    records.put(id, record);
    tsay("creating");
  }

  public synchronized void start() {
    tsay("starting");
    super.start();
  }

  public void run() {
    tsay("run starting");
    running = true;
    try {
      performTask();
    } finally {
      tsay("run finishing");
    }
    completed = true;
  }

  abstract void performTask();

  void tsay(final String message) {
    if (holdMessages) {
      record.messages.add(message);
    } else {
      output(id, record.name, message);
    }
  }

  public static void say(final String message) {
    final Thread thread = Thread.currentThread();
    final int id = (thread instanceof XThread) ? ((XThread) thread).id : 0;

    final ThreadRecord record = records.get(id);
    if (holdMessages) {
      record.messages.add(message);
    } else {
      output(id, record.name, message);
    }
  }

  public static synchronized void joinOnAll() {
    for (ThreadRecord record : records.values()) {
      if (null != record.thread && record.thread.isAlive()) {
        try {
          record.thread.join();
        } catch (final InterruptedException ie) {
        }
      }
    }
  }

  public static synchronized void outputMessages() {
    joinOnAll();
    final Set<Integer> keySet = records.keySet();
    final ArrayList<Integer> ids = new ArrayList<Integer>(keySet.size());
    ids.addAll(keySet);
    Collections.sort(ids);

    for (final Integer id : ids) {
      final ThreadRecord record = records.get(id);
      for (final String message : record.messages) {
        final String name = record.name;
        output(id, name, message);
      }
    }
  }

  private static void output(final int id,
                             final String name,
                             final String message) {
    System.out.println("Thread " + id + " " + name + ": " + message);
  }
}
