package basics.filesystem.scanner;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Vector;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import basics.application.Binder;
import basics.testing.Test;
import basics.unexpected.Problem;
import basics.utl.FormatterUtl;
import basics.utl.SUtl;

/**
 * JarScanner scans JAR files and sends a message to a listeners for every 
 * files found in the archive.
 * @author KStalmann
 */
public class JarScanner
{
private String [] pattern = null;
private JarFile   zf      = null;

private String [] expandFileMask(String [] in)
{
   if(in == null)
      return null;
   String [] out = new String [in.length];
   for(int i = 0; i < in.length; i++)
   {
      String p = in[i].toLowerCase();
      p = SUtl.replaceAll(p, ".", "\\.");
      p = SUtl.replaceAll(p, "?", ".?");
      p = SUtl.replaceAll(p, "*", ".*");
      out[i] = p;
   }
   return out;
}

public void setPattern(String [] pattern)
{
   this.pattern = expandFileMask(pattern);
}

public String [] getExpandedPattern()
{
   return pattern;
}

public boolean patternAccept(ZipEntry entry)
{
   if(pattern == null)
      return true;
   String f = entry.getName().toLowerCase();
   for(int i = 0; i < pattern.length; i++)
   {
      Pattern p = Pattern.compile(pattern[i]);
      Matcher m = p.matcher(f);
      if(m.matches())
         return true;
   }
   return false;
}

/**
 * Enthaelt die Listener
 */
private ArrayList<JarEventListener> jarEventListener = new ArrayList<JarEventListener>();

public JarScanner(JarEventListener fel)
{
   jarEventListener.add(fel);
}

public void addJarEventListener(JarEventListener listener)
{
   jarEventListener.add(listener);
}

@SuppressWarnings("unchecked")
public void startScan(String start) throws Problem
{
   if(start == null)
      return;
   File f = new File(start);
   try
   {
      zf = new JarFile(f);
   }
   catch(ZipException e1)
   {
      for(JarEventListener e : jarEventListener)
         e.nextEntry(f.getAbsolutePath(), "", "FAILED");
      return;
   }
   catch(IOException e2)
   {
      throw new Problem(start, e2);
   }
   int size = zf.size();
   if(size > 0)
   {
      for(Enumeration e = zf.entries(); e.hasMoreElements();)
      {
         ZipEntry ze = (ZipEntry)e.nextElement();
         try
         {
            rscan(zf, ze);
         }
         catch(Throwable e1)
         {
            throw new Problem("Archiv(" + zf.getName() + ") Entry(" + ze.getName() + "): "
               + FormatterUtl.stackTraceToString(e1));
         }
      }
   }
}

private void rscan(JarFile zf, ZipEntry entry) throws Exception
{
   if(!patternAccept(entry))
      return;
   StringBuilder output = new StringBuilder();
   int BUFFER = 2048;
   byte [] data = new byte [BUFFER];
   InputStream is = new BufferedInputStream(zf.getInputStream(entry));
   // int count;
   while(is.read(data, 0, BUFFER) != -1)
   {
      output.append(new String(data));
   }
   for(JarEventListener e : jarEventListener)
      e.nextEntry(zf.getName(), entry.getName(), output.toString());
}

public static void unittest()
{
   File p = new File(Binder.getBasedir(), "work/archive.zip");
   TestConsumer c = new TestConsumer();
   JarScanner s = new JarScanner(c);
   s.setPattern(new String [] { "*.*" });
   try
   {
      s.startScan(p.getAbsolutePath());
   }
   catch(Problem problem)
   {
      Test.handleAsFailure(problem);
   }
   Test.assertTrue(c.totalbyte >= 8276, "at least 18000 bytes expected");
   Test.assertTrue(c.files.size() == 3, "3 files expected, not " + c.files.size());
}

private static class TestConsumer implements JarEventListener
{
public Vector<String> files     = new Vector<String>();
public long           totalbyte = 0;

public void nextEntry(String jarname, String entryname, String output) throws Problem
{
   totalbyte += output.length();
   files.add(jarname + "::" + entryname + " len: " + output.length() + " byte");
   //   Test.printout(jarname + "::" + entryname + " len: " + output.length() + " byte");
}
};
}
