package filemanager;

import com.classpath.util.Characters;
import java.io.*;
import java.util.*;
import com.one.file.*;
import com.vmx.*;
import com.one.*;

public class filesystem
{
	/**
	 * Конструктор
	 */
	public filesystem()
	{
	}

	/**
	 * Проверка "это директория"?
	 *
	 * @param filename String  - имя файла
	 * @return boolean  True - да, False - нет
	 */
	public static boolean isDir(String fn)
	{
		if(fn.length() == 0)
		{
			return false;
		}
		
		if(fn.charAt(fn.length() - 1) == '/')
		{
			return true;
		}
		
		boolean r = false;
		
		if(options.accurateDirCheck)
		{
			try
			{
				FileConnection conn = (FileConnection)Connector.open("file:///" + fn, Connector.READ);
				r = conn.isDirectory();
				conn.close();
			}
			catch(Exception e)
			{
				r = false;
			}
		}

		return r;
	}

	/**
	 * Файл существует?
	 *
	 * @param fileName name of file to be examined
	 * @return true if file exists, false otherwise
	 */
	public static boolean isFileExist(String fileName)
	{
		FileConnection conn = null;
		boolean r = false;
		
		try
		{
			conn = (FileConnection)Connector.open("file:///" + fileName, Connector.READ);
			r = conn.exists();
			conn.close();
		}
		catch(Exception e)
		{
			//ErrScreen.showErrMsg(109, e);
			r = false;
		}
		
		return r;
	}

	/**
	 * Получить список доступных дисков телефона 0:/ 1:/ ...
	 *
	 * @throws IOException
	 * @return String[]  - список строк с именами доступных дисков
	 */
	/*
	public static String[] listRoots() throws IOException
	{
		String roots[] = null;
		Vector vector = new Vector();

		for(Enumeration enumeration = FileSystemRegistry.listRoots(); enumeration.hasMoreElements(); vector.addElement((String)enumeration.nextElement()));

		roots = new String[vector.size()];
		vector.copyInto(roots);

		return roots;
	}
	*/
	
	public static Enumeration listRoots()
	{
		try
		{
			return Connector.listRoots();
		}
		catch(Exception e)
		{
			//ErrScreen.showErrMsg(110, e);
			return null; // (new Vector()).elements();
		}
	}

	/**
	 * Получить список файлов и папок в данной папке.
	 * Работа с архивами прозрачная.
	 *
	 * @param path имя просматриваемой папки
	 * @param includeHidden включать скрытые файлы в список
	 * @return список файлов
	 */
	public static Enumeration list(String folder, boolean includeHidden)
	{
		Enumeration files = null;

		try
		{
			FileConnection fc = (FileConnection)Connector.open("file:///" + folder, Connector.READ);
			files = fc.list("*", includeHidden);
			fc.close();
		}
		catch(Exception e)
		{
			//ErrScreen.showErrMsg(114, e);
			e.printStackTrace();
		}

		return files;
	}

	/**
	 * Получить список файлов и папок в данной папке.
	 * Архивы считаются обычными файлами.
	 *
	 * @param path имя просматриваемой папки
	 * @param includeHidden включать скрытые файлы в список
	 * @return список файлов
	 */
	public static Enumeration listNoArchives(String folder, boolean includeHidden)
	{
		Enumeration files = null;

		try
		{
			FileConnection fc = (FileConnection)Connector.openDirectConnection("file:///" + folder, Connector.READ);
			files = fc.list("*", includeHidden);
			fc.close();
		}
		catch(Exception e)
		{
		}

		return files;
	}

	public static void deleteFileExFC(String fileName, boolean recursively, ProgressCallback callback) throws IOException
	{
		FileConnection conn = (FileConnection)Connector.open("file:///" + fileName);
		
		if(conn.isDirectory() && recursively)
		{
			if(fileName.charAt(fileName.length() - 1) == '/')
			{
				fileName = fileName.substring(0, fileName.length() - 1);
			}
			
			Enumeration en = conn.list("*", true);
			
			while(en.hasMoreElements())
			{
				deleteFileExFC(fileName + "/" + (String)(en.nextElement()), true, callback);
			}
		}
		
		if(callback != null)
		{
			callback.setText(fileName); // fileName.substring(fileName.lastIndexOf('/') + 1));
		}
		
		conn.delete();
		conn.close();
	}

	/**
	 * Удалить файл / папку
	 *
	 * @param fileName String  - имя файла
	 * @return boolean операция удачна
	 */
	public static boolean deleteFile(String fileName, boolean recursively, ProgressCallback callback)
	{
		FileConnection conn = null;
		
		try
		{
			if(recursively)
			{
				deleteFileExFC(fileName, true, callback);
			}
			else
			{
				if(callback != null)
				{
					callback.setText(fileName); // fileName.substring(fileName.lastIndexOf('/') + 1));
				}
				
				conn = (FileConnection)Connector.open("file:///" + fileName);
				conn.delete();
				conn.close();
			}
		}
		catch(Exception e)
		{
			return false;
		}
		
		return true;
	}

	/**
	 * Папка / файл скрытый ?
	 *
	 * @param fileName String
	 * @return boolean
	 */
	public static boolean isHidden(String fn)
	{
		FileConnection conn = null;
		boolean r = false;

		try
		{
			conn = (FileConnection)Connector.open("file:///" + fn, Connector.READ);
			r = conn.isHidden();
			conn.close();
		}
		catch(Exception e)
		{
			//System.out.println("is Hidden error");
			//e.printStackTrace();
			r = false;
		}
		
		return r;
	}

	/**
	 * Размер файла или папки
	 *
	 * @param fileName String  имя файла/папки
	 * @return long  размер в байтах
	 */
	public static long getSize(String fileName, boolean includeSubDirs)
	{
		long size = -1;
		
		try
		{
			FileConnection conn = (FileConnection)Connector.open("file:///" + fileName, Connector.READ);
			
			if(conn.isDirectory())
			{
				size = conn.directorySize(includeSubDirs);
				
				if(size < 0)
				{
					size = -2;
				}
			}
			else
			{
				size = conn.fileSize();
			}
			
			conn.close();
		}
		catch(Exception e)
		{
			size = -1;
		}
		
		return size;
	}

	public static long getCompressedSize(String fileName)
	{
		long size = -1;

		try
		{
			FileConnection conn = (FileConnection)Connector.open("file:///" + fileName, Connector.READ);

			if(conn.isDirectory())
			{
				size = -2;
			}
			else if(conn instanceof ArchiveConnection)
			{
				size = ((ArchiveConnection)conn).compressedSize();
			}

			conn.close();
		}
		catch(Exception e)
		{
			size = -1;
		}

		return size;
	}

	public static int[] getFileCount(String path, boolean includeHidden)
	{
		int[] res = new int[2];
		
		if(!path.endsWith("/"))
		{
			path += "/";
		}

		try
		{
			Enumeration files = list(path, includeHidden);
			String file;

			if(options.accurateDirCheck)
			{
				while(files.hasMoreElements())
				{
					file = (String)files.nextElement();

					if(isDir(path + file))
					{
						res[0]++;
					}
					else
					{
						res[1]++;
					}
				}
			}
			else
			{
				while(files.hasMoreElements())
				{
					file = (String)files.nextElement();

					if(file.charAt(file.length() - 1) == '/')
					{
						res[0]++;
					}
					else
					{
						res[1]++;
					}
				}
			}
		}
		catch(Exception e)
		{
		}

		return res;
	}

	/**
	 * Файл только для чтения ?
	 *
	 * @param filename String
	 * @return boolean
	 */
	public static boolean isReadOnly(String fn)
	{
		FileConnection conn = null;
		boolean r = false;

		try
		{
			conn = (FileConnection)Connector.open("file:///" + fn, Connector.READ);
			r = !conn.canWrite();
			conn.close();
		}
		catch(Exception e)
		{
			r = false;
		}
		
		return r;
	}

	/**
	 * Доступный размер диска
	 *
	 * @param filename String
	 * @return long
	 */
	public static long getDiskSpaceAvailable(String fn)
	{
		long size = -1;
		FileConnection conn = null;
		try
		{
			conn = (FileConnection)Connector.open("file:///" + fn, Connector.READ);
			size = conn.availableSize();
			conn.close();
		}
		catch(Exception e)
		{
		}
		return size;
	}

	/**
	 * Общий размер диска
	 *
	 * @param filename String
	 * @return long
	 */
	public static long getDiskSpaceTotal(String fn)
	{
		long size = -1;
		FileConnection conn = null;
		try
		{
			conn = (FileConnection)Connector.open("file:///" + fn, Connector.READ);
			size = conn.totalSize();
			conn.close();
		}
		catch(Exception e)
		{
		}
		return size;
	}

	/**
	 * Сделать файл Read-only
	 *
	 * @param filename String  файл/папка
	 * @param yes boolean да/нет
	 * @return boolean операция успешна да/нет
	 */
	public static boolean setReadOnly(String fn, boolean readOnly)
	{
	FileConnection conn = null;
	try
	{
		conn = (FileConnection)Connector.open("file:///" + fn, Connector.READ_WRITE);
		conn.setWritable(!readOnly);
		conn.close();
		}
		catch(Exception e)
		{
			return false;
		}
		return true;
	}

	/**
	 * Сделать файл скрытым
	 *
	 * @param filename String файл/папка
	 * @param yes boolean да/нет
	 * @return boolean операция успешна да/нет
	 */
	public static boolean setHidden(String fn, boolean hidden)
	{
	FileConnection conn = null;
	try
	{
		conn = (FileConnection)Connector.open("file:///" + fn, Connector.READ_WRITE);
		conn.setHidden(hidden);
		conn.close();
		}
		catch(Exception e)
		{
			return false;
		}
		return true;
	}

	/**
	 * Дата последнего изменения файла
	 *
	 * @param filename String имя файла
	 * @return long
	 */
	public static long lastModified(String fn)
	{
		long time = 0;
		
		FileConnection conn = null;

		try
		{
			conn = (FileConnection)Connector.open("file:///" + fn, Connector.READ);
			time = conn.lastModified();
			conn.close();
		}
		catch(Exception e)
		{
		}
		
		return time;
	}

	/**
	 * Переименовать файл (папку).
	 *
	 * Если новое имя отличается от старого только регистром,
	 * файл будет предварительно переименован во временное имя.
	 *
	 * @param path папка, в которой находится файл
	 * @param oldName старое имя файла
	 * @param newName новое имя файла
	 * @param callback для наблюдения за прогрессом
	 */
	public static void renameFile(String path, String oldName, String newName, ProgressCallback callback) throws IOException
	{
		if(callback != null)
		{
			callback.setText(path + oldName); // oldName.substring(oldName.lastIndexOf('/') + 1));
		}

		if(newName.equals(oldName))
		{
			return;
		}

		FileConnection conn = null;

		try
		{
			conn = (FileConnection)Connector.open("file:///" + path + oldName);

			if(Characters.toLowerCase(newName).equals(Characters.toLowerCase(oldName)))
			{
				conn.rename(Connector.createTempFileName() + (oldName.endsWith("/") ? "/" : ""));
			}

			conn.rename(newName);
			conn.close();
		}
		catch(IOException ioe)
		{
			try
			{
				conn.close();
			}
			catch(Exception x)
			{
			}

			throw ioe;
		}
	}
	
	/**
	 * Нерекурсивно создать папку
	 */
	public static boolean makeNewDirNotRec(String fn)
	{
		FileConnection conn = null;
		
		try
		{
			conn = (FileConnection)Connector.open("file:///" + fn, Connector.READ_WRITE);
			
			if(!conn.exists() || !conn.isDirectory())
			{
				conn.mkdir();
			}
			
			conn.close();
			
			return true;
		}
		catch(Exception e)
		{
			return false;
		}
	}

	/**
	 * Рекурсивно создать папку (т.е можно создавать Folder1/Folder2 где не
	 * существует ещё даже Folder1)
	 */
	public static boolean makeNewDir(String fn)
	{
		try
		{
			if(!makeNewDirNotRec(fn))
			{
				if(fn.charAt(fn.length() - 1) == '/')
				{
					fn = fn.substring(0, fn.length() - 1);
				}

				if(makeNewDir(fn.substring(0, fn.lastIndexOf('/'))))
				{
					return makeNewDirNotRec(fn + "/");
				}
			}
			else
			{
				return true;
			}
		}
		catch(Exception x)
		{
		}

		return false;
	}

	/**
	 * Копировать содержимое папки.
	 *
	 * @param oldName
	 * @param newName
	 * @param callback
	 * @return
	 */
	public static boolean copyDirectoryContents(String oldName, String newName, ProgressCallback callback, Vector errors)
	{
		if(oldName.charAt(oldName.length() - 1) != '/')
		{
			oldName += "/";
		}
		
		if(newName.charAt(newName.length() - 1) != '/')
		{
			newName += "/";
		}
		
		boolean r = true;
		
		Enumeration files = list(oldName, true);
		String s;

		while(files.hasMoreElements())
		{
			s = (String)files.nextElement();
			r &= copyFile(oldName + s, newName + s, callback, errors);
		}
		
		return r;
	}

	/**
	 * Копировать файл.
	 * Папки копируются рекурсивно.
	 *
	 * В случае возникновения ошибки в переданный вектор кладется пара вида
	 * new String[] { имя файла, ошибка }
	 *
	 * @param oldFileName имя исходного файла
	 * @param newFileName имя конечного файла
	 * @param callback кому  сообщать о прогрессе
	 * @param errors вектор, в который складывать ошибки
	 * @return true, если копирование удалось
	 */
	public static boolean copyFile(String oldFileName, String newFileName, ProgressCallback callback, Vector errors)
	{
		if(callback != null)
		{
			if(callback.getCancelFlag())
			{
				return true;
			}

			callback.setText(AuxClass.getSharedNamePart(oldFileName, newFileName));
		}
		
		FileConnection fc1 = null, fc2 = null;
		
		int bufsize = AuxClass.COPYBUFSIZE;
		
		//System.out.println (oldFileName + " -> " + newFileName);

		if(newFileName.startsWith("null:/"))
		{
			return true;
		}
		
		try
		{
			fc1 = (FileConnection)Connector.open("file:///" + oldFileName, Connector.READ);
			
			if(!fc1.exists())
			{
				throw new IOException("Source file does not exist");
			}
			
			if(fc1.isDirectory())
			{
				fc2 = (FileConnection)Connector.open("file:///" + newFileName);
				
				if(fc2.exists() && !fc2.isDirectory())
				{
					fc2.delete();
				}
				
				if(!fc2.exists())
				{
					fc2.close();
					makeNewDir(newFileName);
				}
				else
				{
					fc2.close();
				}
				
				fc1.close();
				
				return copyDirectoryContents(oldFileName, newFileName, callback, errors);
			}
			
			fc2 = (FileConnection)Connector.open("file:///" + newFileName);
			
			if(!fc2.exists())
			{
				fc2.create();
			}
			else
			{
				fc2.truncate(0);
			}
			
			byte[] buf = new byte[bufsize];
			
			InputStream is = fc1.openInputStream();
			OutputStream os = fc2.openOutputStream();
			int len;
			
			if(callback != null)
			{
				len = (int)fc1.fileSize();

				if(callback.getMax() <= len)
				{
					callback.setProgress(0);
					callback.setMax(len);
				}
				
				while((len = is.read(buf)) > 0)
				{
					os.write(buf, 0, len);
					callback.progress(len);
				}
			}
			else
			{
				while((len = is.read(buf)) > 0)
				{
					os.write(buf, 0, len);
				}
			}
			
			os.close();
			is.close();
			fc2.close();
			fc1.close();
		}
		catch(IOException x)
		{
			//ErrScreen.showErrMsg(137, e);

			x.printStackTrace();
			errors.addElement(new String[] { oldFileName, AuxClass.formatException(x) });

			return false;
		}
		
		return true;
	}
	
	/**
	 * Создать новый файл, записать туда text в UTF-8
	 * и опционально BOM UTF-8 сигнатуру.
	 */
	public static boolean makeNewFile(String fn, String text)
	{
		try
		{
			FileConnection fc = (FileConnection)Connector.open("file:///" + fn);
			
			if(fc.exists())
			{
				fc.truncate(0);
			}
			else
			{
				fc.create();
			}
			
			DataOutputStream dos = fc.openDataOutputStream();
			
			dos.write(StringEncoder.encodeString(text, StringEncoder.ENC_UTF8));
			
			dos.close();
			fc.close();
		}
		catch(Exception e)
		{
			return false;
		}
		
		return true;
	}

	/**
	 * Создать новый файл длиной len
	 * и заполнить его байтами b.
	 */
	public static boolean makeNewFile(String filename, String fill, int len)
	{
		try
		{
			FileConnection fc = (FileConnection)Connector.open("file:///" + filename);

			if(fc.exists())
			{
				fc.truncate(0);
			}
			else
			{
				fc.create();
			}

			OutputStream os = fc.openOutputStream();

			if(fill != null && fill.length() > 0)
			{
				int b = fill.charAt(0);

				try
				{
					byte[] buf = new byte[len];

					for(int i = 0; i < len; i++)
					{
						buf[i] = (byte)b;
					}

					os.write(buf);
				}
				catch(OutOfMemoryError oome)
				{
					for(int i = 0; i < len; i++)
					{
						os.write(b);
					}
				}
			}
			else
			{
				Random rnd = new Random();

				for(int i = 0; i < len; i++)
				{
					os.write(rnd.nextInt());
				}
			}

			os.close();
			fc.close();

			return true;
		}
		catch(IOException ioe)
		{
		}

		return false;
	}

	/**
	 * Преобразовывает число байт в нормальную строку с размером
	 */
	public static String getSizeString(long bytes, boolean fine)
	{
		String res;

		if(bytes < 1024)
		{
			res = Long.toString(bytes) + " " + Locale.getString(null, Locale.BYTE);
		}
		else if(bytes < 1048576)
		{
			res = AuxClass.doubleToString((double)bytes / 1024, 2) + " " + Locale.getString(null, Locale.KB);
		}
		else if(bytes < 1073741824)
		{
			res = AuxClass.doubleToString((double)bytes / 1048576, 2) + " " + Locale.getString(null, Locale.MB);
		}
		else
		{
			res = AuxClass.doubleToString((double)bytes / 1073741824, 2) + " " + Locale.getString(null, Locale.GB);
		}

		if(fine && bytes >= 1024)
		{
			res += "\n" + AuxClass.formatNumber(bytes) + " " + Locale.getString(null, Locale.BYTE);
		}

		return res;
	}

	public static Enumeration listFilesOfType(String filename, String module, boolean includeHidden)
	{
		if(module == null)
		{
			module = ModuleRegistry.getModule(AuxClass.getFileExtension(filename));

			if(module == null)
			{
				Vector res = new Vector(1);
				res.addElement(filename);

				return res.elements();
			}
		}
		
		String folder = filename.substring(0, filename.lastIndexOf('/') + 1);
		Enumeration items;

		if(folder.equals(main.currentPath))
		{
			items = main.FileSelect.elements();
		}
		else
		{
			Vector vector = new Vector();
			Enumeration files = list(folder, includeHidden);

			if(files == null)
			{
				return null;
			}

			while(files.hasMoreElements())
			{
				vector.addElement(new FileItem(folder, (String)files.nextElement(), false));
			}

			if(options.listSortBy > 0)
			{
				FileItem.sortFileList(vector, options.listSortBy, options.listSortIgnoreCase, !options.listSortReverseOrder, 1, vector.size() - 1);
			}

			items = vector.elements();
		}

		Vector res = new Vector();
		FileItem file;

		while(items.hasMoreElements())
		{
			file = (FileItem)items.nextElement();

			if(module.equals(ModuleRegistry.getModule(file.getType())))
			{
				res.addElement(file.getFullName());
			}
		}

		return res.elements();
	}
	
//	private static void out(String s)
//	{
//		System.out.println("[filesystem] " + s);
//	}
}
