package test.distributed;

import org.testng.Assert;
import org.testng.ITestResult;
import org.testng.TestListenerAdapter;
import org.testng.TestNG;
import org.testng.annotations.Test;
import org.testng.remote.SuiteDispatcher;
import org.testng.xml.XmlClass;
import org.testng.xml.XmlSuite;
import org.testng.xml.XmlTest;

import test.BaseDistributedTest;
import testhelper.OutputDirectoryPatch;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;

public class DistributedTest extends BaseDistributedTest {

  private List<Thread> m_hostThreads = new ArrayList<Thread>();

  protected Thread startSlave(final String filename) {
    Thread result = new Thread(new Runnable() {
      @Override
      public void run() {
        TestNG.main(new String[] { "-slave", filename, "-d", OutputDirectoryPatch.getOutputDirectory() });
      }
    });
    result.setName("Slave-" + filename);
    result.start();
    m_hostThreads.add(result);
    return result;
  }

  private File createMasterProperties(String strategy)
  throws IOException
  {
    String fileName = "remote";

    File result = File.createTempFile(fileName, ".properties");
    result.deleteOnExit();
    Properties p = new Properties();
    p.setProperty("testng.hosts", "localhost:" + m_ports[0] + " localhost:" + m_ports[1]);
    p.setProperty("testng.master.strategy", strategy);
    p.setProperty("testng.verbose", "0");
    p.setProperty("testng.master.adpter", "org.testng.remote.adapter.DefaultMastertAdapter");
    FileOutputStream fos = new FileOutputStream(result);
    p.store(fos, "Automatically generated by tests");
    fos.close();

    return result;
  }

  private File createSlaveProperties(String port)
  throws IOException
  {
    String fileName = "remote";

    File result = File.createTempFile(fileName, ".properties");
    result.deleteOnExit();
    Properties p = new Properties();
    p.setProperty("testng.verbose", "0");
    p.setProperty("slave.port", port);
    p.setProperty("testng.slave.adpter", "org.testng.remote.adapter.DefaultWorkerAdapter");
    FileOutputStream fos = new FileOutputStream(result);
    p.store(fos, "Automatically generated by tests");
    fos.close();

    return result;
  }

  private XmlSuite createSuite(String name, Class[] classes) {
    XmlSuite result = new XmlSuite();
    result.setName(name);

    for (Class c : classes) {
      XmlTest test1 = new XmlTest(result);
      test1.setName(c.getName());
      XmlClass class1 = new XmlClass(c);
      test1.getXmlClasses().add(class1);
    }

    return result;
  }

//  @ Configuration(beforeTestClass = true)
  private void startSlaves() throws IOException{
	  int port = new Random().nextInt(50000) + 2000;
	  m_ports = new String[] { Integer.toString(port), Integer.toString(port+1)};

	  File slaveFile = createSlaveProperties(m_ports[0]);
	  startSlave( slaveFile.getCanonicalPath());

	  slaveFile = createSlaveProperties(m_ports[1]);
	  startSlave( slaveFile.getCanonicalPath());
  }

  private String[] m_ports = new String[2];

  public TestListenerAdapter twoHosts(String strategy) throws IOException {
    TestNG tng = new TestNG();
    tng.setOutputDirectory(OutputDirectoryPatch.getOutputDirectory());

    File masterFile = createMasterProperties(strategy);
    tng.setMaster(masterFile.getAbsolutePath());

    XmlSuite suite = createSuite("DistributedSuite1", new Class[] { Test1.class, Test2.class });
    tng.setXmlSuites(Arrays.asList(new XmlSuite[] { suite }));

    TestListenerAdapter result = new TestListenerAdapter();
    tng.addListener(result);
    tng.run();

    String[] passed = {
        "f1", "f2"
    };
    String[] failed = {};
    String[] skipped = {};

    verifyTests("Passed", passed, toMap(result.getPassedTests()));
    verifyTests("Failed", failed, toMap(result.getFailedTests()));
    verifyTests("Skipped", skipped, toMap(result.getSkippedTests()));

    return result;
  }

  @Test
  public void twoHostsWithTestStrategy() throws IOException {
    startSlaves();
    TestListenerAdapter listener = twoHosts(SuiteDispatcher.STRATEGY_TEST);

    boolean found1 = false;
    boolean found2 = false;
    for (ITestResult tr : listener.getPassedTests()) {
      String host = tr.getHost();
      if (! found1) {
        found1 = host.contains(m_ports[0]);
      }
      if (! found2) {
        found2 = host.contains(m_ports[1]);
      }
    }
    Assert.assertTrue(found1, "No tests ran on port " + m_ports[0]);
    Assert.assertTrue(found2, "No tests ran on port " + m_ports[1]);
  }

  @Test
  public void twoHostsWithSuiteStrategy() throws IOException {
    startSlaves();
    twoHosts(SuiteDispatcher.STRATEGY_SUITE);
  }

  private Map<String, ITestResult> toMap(List<ITestResult> results) {
    Map<String, ITestResult> result = new HashMap<String, ITestResult>();
    for (ITestResult tr : results) {
      result.put(tr.getName(), tr);
    }

    return result;
  }
  private void ppp(String string) {
    System.out.println("[DistributedTest] " + string);
  }
}

