package nxm.audio.test.junit.lib;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;

import org.junit.Before;

import nxm.sys.lib.Midas;
import nxm.sys.lib.Shell;
import nxm.sys.lib.StringUtil;
import nxm.sys.test.junit.NMTestCase;

/** This will test the playerdaemon class.
 *
 *  @author  Zachary Matthews
 *  @since   NeXtMidas 4.1.2
 */
public final class playerdaemonTest extends NMTestCase {

  /** public constructor to allow running this test case directly with JUnit. */
  public playerdaemonTest (String methodName) { this(methodName, null); }

  /** Creates a new instance of the class.
      @param methodName The name of the method being called.
      @param midas      The midas context to use when running the tests.
   */
  public playerdaemonTest (String methodName, Midas midas) {
    super(methodName, midas);
  }

  private boolean skippingBecauseHeadless = false;

  @Before
  public void setUp () {
    if (Shell.isHeadless()) {
      skippingBecauseHeadless = true;
      return;
    }
  }

  /** Mocks up the serialization of playerdaemon by serializing a class similar to
   *  playerdaemon that has a field that uses Collections.synchronizedMap() of a TreeMap.
   *  This is done in order to minimize the complexity of instantiating a playerdaemon object.
   */
  public void testSerializable () {
    if (skippingBecauseHeadless) {
      getMidas().info("Skipping testSerializable because HEADLESS");
      return;
    }
    SerializeMapTreeMap serMapTreeMap = new SerializeMapTreeMap();
    serMapTreeMap.addValue(0, "a");
    serMapTreeMap.addValue(1, "b");
    serMapTreeMap.addValue(2, "c");
    File ramFile = null;
    try {
      ramFile = File.createTempFile("serializemaptreemapserializationtest", "temp");
      FileOutputStream fos = new FileOutputStream(ramFile);
      ObjectOutputStream oos = new ObjectOutputStream(fos);
      oos.writeObject(serMapTreeMap);
      oos.flush();
      fos.close();
      FileInputStream fis = new FileInputStream(ramFile);
      ObjectInputStream ois = new ObjectInputStream(fis);
      SerializeMapTreeMap pdNew = (SerializeMapTreeMap)ois.readObject();
      fis.close();
      assertEquals("nested class map size matches",3,pdNew.getMap().size());
      assertEquals("nested class map entry matches","c",pdNew.getMap().get(2));
    }
    catch (Exception e) {
      fail("Error during serialization:"+e);
    }
    finally {
      ramFile.delete();
    }
  }

  /** Tests serialization of a synchronized Map that uses Collections.synchronizedMap()
   *  of a TreeMap. Map is not serializable, but because TreeMap is serializable,
   *  the offending Map field should serialize every single time.
   */
  private static class SerializeMapTreeMap implements Serializable {
    private static final long serialVersionUID = 7759440176935878365L;
    private transient Map<Integer,String> map = Collections.synchronizedMap(new TreeMap<Integer,String>());

    private Map<Integer,String> getMap () {
      return map;
    }

    private void addValue (Integer key, String val) {
      map.put(key, val);
    }

    public String toString() {
      if (map==null) return "";
      else           return StringUtil.toStringBuilder(map,null,",","=").toString();
    }

    /** Overwrite default serialization
     *
     *  @param oos Object Output Stream
     *  @throws IOException Exception thrown by writeObject
     */
    private void writeObject (ObjectOutputStream oos) throws IOException {
      oos.defaultWriteObject();
      oos.writeObject(map);
    }

    /** Overwrite default deserialization
     *
     *  @param ois Object Input Stream
     *  @throws ClassNotFoundException Exception thrown by readObject
     *  @throws IOException Exception thrown by readObject
     */
    @SuppressWarnings("unchecked")
    private void readObject (ObjectInputStream ois) throws ClassNotFoundException, IOException {
      ois.defaultReadObject();
      map=(Map<Integer,String>)ois.readObject();
    }
  }
}
