package com.horstmann.labrat;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;

public class CompareTask extends Task
{
   public void execute() throws BuildException
   {
      List<String> tokens1 = null;
      List<String> tokens2 = null;
      try
      {
         tokens1 = getTokens(file1);
         tokens2 = getTokens(file2);
      }
      catch (FileNotFoundException ex)
      {
         BuildException ex2 = new BuildException();
         ex2.initCause(ex);
         throw ex2;
      }
      // TODO: keep / remove

      while (tokens1.size() < tokens2.size())
         tokens1.add("");
      while (tokens2.size() < tokens1.size())
         tokens2.add("");

      boolean success = true;
      for (int i = 0; i < tokens1.size(); i++)
      {
         String token1 = tokens1.get(i);
         String token2 = tokens2.get(i);
         boolean b = compare(token1, token2);
         log((b ? "pass" : "fail") + "; Actual: " + token1);
         log("    Expected: " + token2);
         success &= b;
      }
      if (!success) 
         throw new BuildException();      
   }

   public List<String> getTokens(String file) throws FileNotFoundException
   {
      List<String> r = new ArrayList<String>();
      FileReader reader = new FileReader(file);
      Scanner in = new Scanner(reader);
      /*
       * TODO: Would this ever be useful for negative delimiters?
       */
      /* 
      String delimiter = "";
      if (token.equals("line")) // TODO: precompile
         delimiter = "[\\r][\\n]?|[\\n]";
      else if (token.equals("number"))
         delimiter = "[^0-9-]+";
      else if (token.equals("word"))
         delimiter = "[\\p{javaWhiteSpace}]+";
      else
         delimiter = token; // TODO: really?
      in.useDelimiter(delimiter);
      */
      String pattern = "";
      if (token.equals("line"))
      {
         while (in.hasNextLine())
            r.add(in.nextLine());
         return r;
      }
      else if (token.equals("word"))
      {      
         while (in.hasNext())
            r.add(in.next());
         return r;
      }
      else if (token.equals("number"))
         pattern = "[-+]?[0-9]+\\.?[0-9]*([eE][-+]?[0-9]+)?";
      else 
         pattern = token;
      String s;
      while ((s = in.findWithinHorizon(pattern, 0)) != null)
         r.add(s);
      return r;
   }

   public boolean compare(String a, String b)
   {
      if (token.equals("number"))
      {
         try
         {
            double x = Double.parseDouble(a);
            double y = Double.parseDouble(b);
            return Math.abs(x - y) <= tolerance;
         }
         catch (NumberFormatException ex)
         {
            return false;
         }
      }

      String[] as = a.split(ignoreRegex); // TODO: doesn't seem quite right, or
      // maybe concat afterwards?
      String[] bs = b.split(ignoreRegex);
      if (as.length != bs.length) return false;
      for (int i = 0; i < as.length; i++)
         if (ignoreCase)
         {
            if (!as[i].equalsIgnoreCase(bs[i])) return false;
         }
         else
         {
            if (!as[i].equals(bs[i])) return false;
         }
      return true;
   }

   private String file1; // TODO: file set???
   private String file2;

   private String token = "line";
   private int discard = 0;
   private int keep = 0;
   private double tolerance = 1.0E-12;
   private String ignoreRegex = "\\W"; // TODO
   private boolean ignoreCase = true;

   public void setDiscard(int discard)
   {
      this.discard = discard;
   }

   public void setFile1(String file1)
   {
      this.file1 = file1;
   }

   public void setFile2(String file2)
   {
      this.file2 = file2;
   }

   public void setIgnoreCase(boolean ignoreCase)
   {
      this.ignoreCase = ignoreCase;
   }

   public void setIgnoreRegex(String ignoreRegex)
   {
      this.ignoreRegex = ignoreRegex;
   }

   public void setKeep(int keep)
   {
      this.keep = keep;
   }

   public void setToken(String token)
   {
      this.token = token;
   }

   public void setTolerance(double tolerance)
   {
      this.tolerance = tolerance;
   }
}
