package basics.filesystem.file;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.RandomAccessFile;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import basics.application.Binder;
import basics.filesystem.scanner.FileEventListener;
import basics.filesystem.scanner.FileSystemScanner;
import basics.testing.Test;
import basics.unexpected.Problem;
import basics.utl.Empty;
import basics.utl.SUtl;
import basics.utl.StreamUtl;
import basics.utl.SysUtl;

public class FileUtl
{
public static final int ONE_MEGABYTE = 10240000;

// private static Logger logger = Logger.getLogger(FileUtl.class);
/**
 * Convert a uri to a standard conform url. Returns Null if the conversion failed.
 */
public static String toStandardUrl(String uri)
{
   try
   {
      return new File(uri).toURL().toExternalForm();
   }
   catch(MalformedURLException e)
   {
      return null;
   }
}

public static String toFilePath(String url)
{
   try
   {
      return new URL(url).getFile();
   }
   catch(MalformedURLException e)
   {
      return null;
   }
}

public static int copy(File source, File destination) throws IOException
{
   return StreamUtl.copy(new FileInputStream(source), new FileOutputStream(destination), true);
}

/**
 * Fast read for big files using an array as target. Only one read at a time.
 * @param file the file to read from.
 * @return a byte array of null if file was not read.
 */
public static final synchronized byte [] readBytesFromBigFile(String file)
{
   InputStream instream = null;
   byte [] target = new byte [0];
   try
   {
      instream = new BufferedInputStream(new FileInputStream(file));
      int truncsize = ONE_MEGABYTE * 5;
      byte [] trunc = new byte [truncsize];
      byte [] tmp = null;
      int bytesread = 0;
      while((bytesread = instream.read(trunc, 0, truncsize)) != -1)
      {
         tmp = new byte [target.length + bytesread];
         System.arraycopy(target, 0, tmp, 0, target.length);
         System.arraycopy(trunc, 0, tmp, target.length, bytesread);
         target = tmp;
         tmp = null;
      }
   }
   catch(Exception e)
   {
      target = null;
   }
   finally
   {
      if(instream != null) try
      {
         instream.close();
      }
      catch(Exception e)
      {
      }
   }
   return target;
}

public static String readBytes(String fileName)
{
   return readBytes(new File(fileName));
}

public static String readBytes(File file)
{
   return readBytes(file, "utf-8");
}

/**
 * reads uft-8
 * @param fileName
 * @param charset
 * @return
 */
public static String readBytes(String fileName, String charset)
{
   return readBytes(new File(fileName), charset);
}

/**
 * reads uft-8
 * @param file
 * @param charset
 * @return
 */
public static String readBytes(File file, String charset)
{
   String result = null;
   try
   {
      if(file.exists())
      {
         RandomAccessFile raf = new RandomAccessFile(file, "r");
         //         result = raf.readUTF();
         byte b[] = new byte [(int)raf.length()];
         raf.readFully(b);
         // OsUtl.print(charset + ", " + file.getName() + ">>>" +
         // b.length);
         if(Empty.is(charset)) result = new String(b);
         else result = new String(b, charset);
         // OsUtl.trace(" >>>" + result.length());
         raf.close();
      }
   }
   catch(Exception e)
   {
      SysUtl.trace("Reading " + file.getName() + " failed. Charset: " + charset + ". Error: "
         + e.getMessage());
   }
   finally
   {
   }
   return result;
}

/**
 * reads uft-8
 * @param file
 * @param charset
 * @return
 */
public static String readUTF8(File file)
{
   String result = null;
   try
   {
      if(file.exists())
      {
         RandomAccessFile raf = new RandomAccessFile(file, "r");
         result = raf.readUTF();
         raf.close();
      }
   }
   catch(Exception e)
   {
      SysUtl
         .trace("Reading " + file.getName() + " failed. Charset: UTF8. Error: " + e.getMessage());
   }
   finally
   {
   }
   return result;
}

public static String concat(String p1, String p2)
{
   if(p1 == null && p2 == null) return "";
   if(p1 == null) return p2;
   if(p2 == null) return p1;
   return new File(p1, p2).getAbsolutePath();
}

public static String addext(String p1, String p2)
{
   if(p1 == null && p2 == null) return "";
   if(p1 == null) return p1 = "";
   if(p2 == null) return p1;
   if(p1.endsWith(".") && p2.startsWith(".")) return p1 + p2.substring(1);
   else if(p1.endsWith(".") || p2.startsWith(".")) return p1 + p2;
   else return p1 + "." + p2;
}

public static boolean exists(String file)
{
   return file != null && exists(new File(file));
}

public static boolean exists(File file)
{
   return file.exists();
}

public static boolean delete(String file)
{
   return delete(new File(file));
}

public static boolean delete(File file)
{
   try
   {
      if(file.exists()) file.delete();
      return true;
   }
   catch(Exception e)
   {
      // logger.error(e.getMessage());
      return false;
   }
}

public static boolean renameFile(String pathOld, String pathNew)
{
   try
   {
      new File(pathOld).renameTo(new File(pathNew));
      return true;
   }
   catch(Exception e)
   {
      // logger.error(e.getMessage());
      return false;
   }
}

/**
 * gibt "" zurueck, wenn es keine extension gibt, sonst die extension ohne den punkt.
 */
public static String getExtension(String filename)
{
   if(filename == null) return "";
   int x = filename.lastIndexOf(".");
   return (x == -1) ? "" : filename.substring(x + 1);
}

/**
 * extrahiert den namen ohne pfad und extenstion.
 */
public static String removeUrlParams(String input)
{
   if(input == null) return "";
   int x = input.indexOf("?");
   return (x == -1) ? input : input.substring(0, x);
}

/**
 * extrahiert den namen ohne pfad und extension. a) bei foldern: verzeichnisnamen ohne pfad b) bei
 * dateien: den namen der datei ohne pfad
 */
public static String getNameOnly(String input)
{
   if(input == null) return "";
   int x = input.lastIndexOf(".");
   input = (x == -1) ? input : input.substring(0, x);
   x = input.lastIndexOf("/");
   return (x == -1) ? input : input.substring(x + 1);
}

/**
 * extrahiert den dateinamen ohne pfad aber mit extension.
 */
public static String getName(String input)
{
   if(input == null) return "";
   int x = input.lastIndexOf("/");
   return (x == -1) ? input : input.substring(x + 1);
}

/**
 * extrahiert den dateipfad ohne dateiname und extension.
 */
public static String getFolder(String input)
{
   if(input == null) return ".";
   int x = input.lastIndexOf("/");
   return (x == -1) ? "." : input.substring(0, x);
}

/**
 * transform path to java convention
 */
public static String slashify(String input)
{
   if(input == null) return "";
   return SUtl.replaceAll(input, "\\", "/");
}

public static long size(String file)
{
   try
   {
      return new File(file).length();
   }
   catch(Exception e)
   {
      return 0;
   }
}

public static boolean mkdir(String file)
{
   return mkdir(new File(file));
}

public static boolean mkdir(File f)
{
   try
   {
      return f.mkdirs();
   }
   catch(Exception e)
   {
      // logger.error("Cannot create folder (" + f + "). " +
      // e.getMessage());
      return false;
   }
}

public static boolean delete(File folder, String pattern)
{
   return delete(folder.getAbsolutePath(), pattern);
}

public static boolean delete(String folder, String pattern)
{
   if(pattern == null)
   {
      pattern = "";
   }
   else
   {
      pattern = SUtl.replaceAll(pattern, ".", "\\.");
      pattern = SUtl.replaceAll(pattern, "?", ".?");
      pattern = SUtl.replaceAll(pattern, "*", ".*");
   }
   File fileList[] = new File(folder).listFiles();
   if(fileList == null) return true;
   Pattern p = Pattern.compile(pattern);
   boolean ok = true;
   for(File f : fileList)
   {
      if(!f.isDirectory())
      {
         String name = f.getName().toLowerCase();
         Matcher m = p.matcher(name);
         if(m.matches())
         {
            if(!delete(f)) ok = false;
         }
      }
   }
   return ok;
}

public static InputStream getInputStream(String filepath) throws Problem
{
   FileInputStream fis = null;
   try
   {
      File file = new File(filepath);
      if(!file.exists()) throw new Problem("Not found: " + filepath);
      else if(!file.canRead()) throw new Problem("Can't read: " + filepath);
      else fis = new FileInputStream(file);
   }
   catch(Exception e)
   {
      throw new Problem(filepath, e);
   }
   return fis;
}

//public static Properties loadProperties (InputStream istream) throws Problem
//{
//   if (istream == null) throw new Problem("Failed reading properties, stream is NULL.");
//   Properties dbproperties = new Properties();
//   try
//   {
//      dbproperties.load(istream);
//      return dbproperties;
//   }
//   catch (Exception e)
//   {
//      throw new Problem("Failed reading properties from stream.", e);
//   }
//}
public static void writeBytes(String file, String buffer)
{
   delete(file);
   try
   {
      RandomAccessFile raf = new RandomAccessFile(file, "rw");
      raf.writeBytes(buffer);
      raf.close();
   }
   catch(Exception e)
   {
      SysUtl.trace("Writing " + file + " failed. Error: " + e.getMessage());
   }
   finally
   {
   }
}

public static void writeUTF8(String file, String buffer)
{
   delete(file);
   try
   {
      RandomAccessFile raf = new RandomAccessFile(file, "rw");
      raf.writeUTF(buffer);
      raf.close();
   }
   catch(Exception e)
   {
      SysUtl.trace("Writing " + file + " failed. Error: " + e.getMessage());
   }
   finally
   {
   }
}

public static synchronized int countFiles(String folder, String pattern)
{
   FileCounter fc = new FileCounter();
   FileSystemScanner scanner = new FileSystemScanner(fc);
   scanner.setPattern(new String [] { pattern });
   scanner.startScan(folder, false);
   return fc.getCount();
}

public static class FileCounter implements FileEventListener
{
int x = 0;

public int getCount()
{
   return x;
}

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

public void nextFile(File file)
{
   x++;
}
}

public static void silentClose(FileOutputStream fileOutputStream)
{
   try
   {
      fileOutputStream.close();
   }
   catch(Throwable t)
   {
   }
}

public static void silentClose(ObjectOutputStream objectOutputStream)
{
   try
   {
      objectOutputStream.close();
   }
   catch(Throwable t)
   {
   }
}

public static void silentClose(FileInputStream fileOutputStream)
{
   try
   {
      fileOutputStream.close();
   }
   catch(Throwable t)
   {
   }
}

public static void silentClose(ObjectInputStream objectOutputStream)
{
   try
   {
      objectOutputStream.close();
   }
   catch(Throwable t)
   {
   }
}

//@TODO insufficient test coverage 
public static void unittest()
{
   File p = new File(Binder.getBasedir(), "work");
   int x = FileUtl.countFiles(p.getAbsolutePath(), "*.txt");
   Test.assertTrue(x == 6, "6 txt files expected in " + p.getAbsolutePath() + "not " + x);
}
}
