package basics.testing;

import java.io.File;
import java.lang.reflect.Method;
import basics.application.Args;
import basics.filesystem.file.LineReader;
import basics.filesystem.scanner.FileEventListener;
import basics.filesystem.scanner.FileSystemScanner;
import basics.utl.FormatterUtl;
import basics.utl.SysUtl;

public class Units extends Test implements FileEventListener
{
public static final String UNIT_TEST_METHOD = "unittest";
private Args               args             = null;
FileSystemScanner          fsScanner        = null;
int                        totalfiles       = 0;
int                        testedfiles      = 0;
int                        skipped          = 0;
StringBuilder              notTested        = new StringBuilder();

public static void main(String [] args)
{
   try
   {
      new Units(args).run();
   }
   catch(Throwable t)
   {
      printout("Starting tests failed. " + t.getMessage());
   }
}

public Units(String [] _args)
{
   args = new Args(_args);
   if(args.getSwitch("abort", false))
      abortOnError = true;
   File logfile = new File(LOGFILE);
   if(logfile.exists())
      try
      {
         logfile.delete();
      }
      catch(Exception e)
      {
      }
}

public void run()
{
   redirectOutput();
   String file = args.getOption("file", "");
   String classname = args.getOption("class", "");
   String sourcefolder = args.getOption("folder", "");
   if(file.trim().length() > 0)
   {
      LineReader lr = new LineReader(file);
      if(!lr.open())
         throw new RuntimeException("File with list of classes for test not found: " + file);
      while(lr.hasLine())
      {
         String nextclass = lr.nextLineTrimNotNull();
         if(nextclass.length() == 0 || nextclass.startsWith("#"))
            continue;
         try
         {
            test(nextclass);
         }
         catch(Throwable t)
         {
            printout("NOT TESTED [" + nextclass + "]. " + t.getMessage());
            //            printout("NOT TESTED [" + FormatterUtl.simpleName(nextclass) + "]. " + t.getMessage());
            t.printStackTrace();
         }
      }
      lr.close();
   }
   if(classname.length() > 0)
   {
      try
      {
         test(classname);
      }
      catch(Throwable t)
      {
         printout("NOT TESTED " + FormatterUtl.simpleName(classname) + ". " + t.getMessage());
      }
   }
   if(sourcefolder.length() > 0)
   {
      try
      {
         testFolder(sourcefolder);
      }
      catch(Throwable t)
      {
         printout("FOLDER NOT TESTED " + sourcefolder + ". " + t.getMessage());
      }
   }
   int q = (int)(((double)testedfiles / (double)totalfiles) * 100D);
   printout("Tested " + testedfiles + " of " + totalfiles + " (" + q + "% coverage), " + skipped
      + " interfaces, " + memory.toString());
   endRedirectOutput();
}

public void testFolder(String folder)
{
   fsScanner = new FileSystemScanner(this);
   fsScanner.setPattern(new String [] { "*.java" });
   File p = new File(folder);
   if(!p.exists())
      printout("NOT FOUND: " + p.getAbsolutePath());
   fsScanner.startScan(p.getAbsolutePath(), true);
   if(notTested.length() > 0)
      printout("NOT TESTED: " + notTested.toString());
}

public boolean acceptFile(File file)
{
   return true;
}

public void nextFile(File file)
{
   String classname = fsScanner.toClassName(fsScanner.toRelativeFilePath(file));
   if(classname != null)
   {
      try
      {
         test(classname);
      }
      catch(Throwable t)
      {
         notTested.append(FormatterUtl.simpleName(classname));// + ": " + t.getMessage());
         notTested.append(" ");
         //         printout("NOT TESTED " + classname + ". " + t.getMessage());
      }
   }
}

public void test(String clazz)
{
   try
   {
      Class<?> c = Class.forName(clazz);
      if(c.isInterface() || c.isAssignableFrom(Exception.class) || c.isAssignableFrom(Error.class))
      {
         skipped++;
         return;
      }
      totalfiles++;
      Method method = c.getMethod(UNIT_TEST_METHOD);
      if(method == null)
         throw new Exception("Missing method 'public static void " + UNIT_TEST_METHOD + "()'.");
      long t0 = System.currentTimeMillis();
      String errormessage = "";
      try
      {
         method.invoke(null);
         testedfiles++;
      }
      catch(Throwable t)
      {
         errormessage = "TEST ABORTED " + t.getCause() + ": " + FormatterUtl.stackTraceToString(t)
            + ", class: " + c.getName();
      }
      long t1 = System.currentTimeMillis();
      printout(FormatterUtl.simpleName(clazz) + " " + FormatterUtl.timestring(t1 - t0) + " /"
         + FormatterUtl.packageName(clazz) + " " + errormessage);
   }
   catch(ClassNotFoundException e)
   {
      //      printout("ERR: " + clazz + " not found.");
      throw new RuntimeException(clazz + " not found.");
   }
   catch(InstantiationException e)
   {
      //      printout("ERR: " + "Can instantiate " + clazz + ". ");
      throw new RuntimeException("Can instantiate " + clazz + ". ");
   }
   catch(IllegalAccessException e)
   {
      //      printout("ERR: " + "Not accessable " + clazz + ". ");
      throw new RuntimeException("Not accessable " + clazz + ". ");
   }
   catch(Throwable e)
   {
      String m = e.getMessage();
      if(m == null || m.trim().length() == 0)
         m = "Unknown reason. ";
      //      printout("ERR: " + m + ": " + clazz + ". ");
      throw new RuntimeException(m);
   }
}

public static void unittest()
{
}
}
