package com.github.qihootest.leo.toolkit.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;


/**
 * 对文件的底层操作,包括读、写,文件的及目录的创建、删除
 * @author lianghui ([email protected])
 *
 */
public class FileUtil {
	private static LogUtil log=LogUtil.getLogger(FileUtil.class);//日志记录
	private static List<String>filelist=new ArrayList<>();
	
	//文件的读写
	
	/**
	 * 从文件中以字节流方式读取并返回
	 * @param pathName
	 * @return FileInputStream
	 */
	public static FileInputStream readToFileInputStream(String pathName) {
		try {
			return new FileInputStream(pathName);
		} catch (FileNotFoundException e) {
			log.error("字节流方式读取文件失败:"+pathName);
			log.error(e.getMessage());
			return null;
		}
	}
	
	/**
	 * 读取字符流,指定编码
	 * @param patnName 文件位置,相对或绝对路径
	 * @param enCoding 读取时使用的编码
	 * @return InputStreamReader
	 */
	public static InputStreamReader readToInputStreamReader(String patnName,String enCoding) {
		try {
			return new InputStreamReader(readToFileInputStream(patnName), enCoding);
		} catch (Exception e) {
			log.error("读取文件失败:"+patnName);
			log.error(e.getMessage());
			return null;
		}
	}
	
	/**
	 * 读取字符流,指定编码
	 * @param pathName
	 * @param enCoding
	 * @return BufferedReader
	 */
	public static BufferedReader readToBufferedReader(String pathName,String enCoding) {
		try {
			return new BufferedReader(readToInputStreamReader((pathName), enCoding));
		} catch (Exception e) {
			log.error("读取文件失败:"+pathName);
			log.error(e.getMessage());
			return null;
		}
	}
	
	/**
	 *  读取字符流,指定编码
	 * @param pathName
	 * @param enCoding
	 * @return String
	 */
	public static String readToString(String pathName,String enCoding) {
		BufferedReader buff=readToBufferedReader(pathName,enCoding);
		String line="";
		StringBuilder  str=new StringBuilder();
		try {
			while ((line = buff.readLine()) != null) { 
				str.append(line).append("\r\n"); 
			 } 
		} catch (Exception e) {
			log.error(e.getMessage());
			str.append("");
		}
		
		return String.valueOf(str);
	}

	/**
	 *  读取字符流,指定编码
	 * @param input
	 * @param enCoding
	 * @return String
	 */
	public static String readInputStreamToString(InputStream input,String enCoding) {
		BufferedReader reader ;
		StringBuilder sb = new StringBuilder();
		String line = null;
		try {
			reader = new BufferedReader(new InputStreamReader(input,enCoding));
			while ((line = reader.readLine()) != null) {
				sb.append(line + "\r\n");
			}
		} catch (IOException e) {
			log.error(e.getMessage());
		} finally {
			try {
				input.close();
			} catch (IOException e) {
				log.error(e.getMessage());
			}
		}
		return sb.toString();

	}
	
	/**
	 * 读取字符流,指定编码,按每行数据返回List
	 * @param pathName
	 * @param enCoding
	 * @return List<String>
	 */
	public static List<String> readLineToList(String pathName,String enCoding) {
		BufferedReader br=null;
		String line="";
		List<String>lines=new ArrayList<>();
		
		br=readToBufferedReader(pathName, enCoding);
		if (null==br) return null;
		try {
			while((line=br.readLine())!=null){
				lines.add(line.trim());
			}
			br.close();
		} catch (Exception e) {
			log.error("按行读取文件失败:"+pathName);
			log.error(e.getMessage());
		}
		return lines;
	}
	
	/**
	 * 创建到文件的输出流
	 * @param pathName
	 * @return FileOutputStream
	 */
	public static  FileOutputStream getFileOutStream(String pathName) {
		//先创建一空白文件
		try {
			createFile(pathName);
			return new FileOutputStream(pathName);
		} catch (FileNotFoundException e) {
			log.error("创建输出到文件失败:"+pathName);
			log.error(e.getMessage());
			return null;
		}
	}
	
	/**
	 * 按字节写文件
	 * @param byteStream
	 * @param pathName
	 * @return boolean
	 */
	public static boolean writeByteArray(byte[] byteStream,String pathName) {
		boolean flag=false;
		try {
			//先创建一空白文件
			FileOutputStream outStream = getFileOutStream(pathName);
			outStream.write(byteStream);
			outStream.flush();
			outStream.close();
			flag=true;
		} catch (IOException e) {
			log.error("写入文件失败"+pathName);
			log.error(e.getMessage());
			flag=false;
		}
		return flag;
	}
	
	/**
	 * 把字节流写入文件
	 * @param inStream
	 * @param pathName
	 * @return boolean
	 */
	public static boolean writeStream(InputStream inStream,String pathName) {
		byte[] temp;
		try {
			temp=new byte[inStream.available()];
			inStream.read(temp);
			return writeByteArray(temp,pathName);
		} catch (Exception e) {
			log.error("写入文件失败1"+pathName);
			log.error(e.getMessage());
			return false;
		}
		
	}

	/**
	 * 把字符串按指定编码写入文件
	 * @param str
	 * @param pathName
	 * @param enCoding
	 * @return boolean
	 */
	public static boolean writeString(String str,String pathName,String enCoding) {
		byte[] b;
		try {
			b=str.getBytes(enCoding);
		} catch (UnsupportedEncodingException e) {
			log.error("字符串按编码 "+enCoding+"转换字节错误");
			log.error(e.getMessage());
			return false;
		}
		return writeByteArray(b,pathName);
	}
	
	/**
	 * 把List中的字符串按行写入文件
	 * @param strList
	 * @param pathName
	 * @param enCoding
	 * @return boolean
	 */
	public static boolean writeString(List<String> strList,String pathName,String enCoding) {
		if (null==strList ) {
			log.error("待写入文件"+pathName+"的字符列表为null");
			return false;
		}		
		StringBuffer tmp=new StringBuffer();
		for (int i = 0; i < strList.size(); i++) {
			tmp.append(strList.get(i)+"\r\n");
		}
		return writeString(tmp.toString(), pathName, enCoding);
	}
	
	
	
	//文件的删除、创建、改名等操作

	/**
	 * 说明:删除指定的文件
	 * 
	 * @param file    	文件的完整绝对路径或者相对路径
	 * @return boolean 		删除成功返回true,删除失败、指定路径不存在返回false
	 */
	public static boolean delFile(File file) {
		if (file.isFile() == true) {
			return file.delete();
		}else{
			log.error("文件:"+file+" 不存在,或者不是文件类型");
		}
		return false;
	}

	
	/**
	 * 说明:删除指定的文件
	 * 
	 * @param pathName  文件的完整绝对路径或者相对路径
	 * @return boolean 删除成功返回true,删除失败、指定路径不存在返回false
	 */
	public static boolean delFile(String pathName) {
		File path = new File(pathName);
		return delFile(path);
	}

	/**
	 * 说明:/删除指定文件夹下所有文件
	 * 
	 * @param folderPath     文件夹完整绝对路径或者相对路径
	 * @return boolean  删除成功返回true,删除失败、指定路径不存在返回false
	 */
	public static boolean delAllFile(String folderPath) {
		File path = new File(folderPath);
		if (!path.exists()) {
//			log.error("删除指定的文件夹:"+folderPath+"不存在");
			return false;
		}
		if (!path.isDirectory()) {
//			log.error("删除指定的:"+folderPath+"不是个文件夹");
			return false;
		}
		
		String[] tempList = path.list();
		File temp = null;
		for (int i = 0; i < tempList.length; i++) {
			if ((path.toString()).endsWith(File.separator)) {
				temp = new File(path + tempList[i]);
			} else {
				temp = new File(path + File.separator + tempList[i]);
			}
			if (temp.isFile()) {
				delFile(temp);
			}
			if (temp.isDirectory()) {
				delAllFile(path + File.separator + tempList[i]);// 先删除文件夹里面的文件
				delFolder(path + File.separator + tempList[i]);// 再删除空文件夹
			}
		}
		return true;
	}

	/**
	 * 说明:删除文件夹
	 * 
	 * @param folderPath  文件夹完整绝对路径或者相对路径
	 */
	public static void delFolder(String folderPath) {

		try {
			delAllFile(folderPath); // 删除完里面所有内容
			String filePath = folderPath;
			filePath = filePath.toString();
			File myFilePath = new File(filePath);
			myFilePath.delete(); // 删除空文件夹
		} catch (Exception e) {
			log.error("删除文件夹:"+folderPath+"出错");
			log.error(e.getMessage());
		}
	}

	/**
	 * 创建目录
	 * @param folderPath
	 * @return boolean 创建成功或者已存在返回true,不存在且创建失败返回false
	 */
	public static boolean createDictory(String folderPath){
		if (null==folderPath) return false;
		File path=new File(folderPath);
		if(path.exists()){
			return   true;
		}else{
			if (new File(folderPath).mkdirs()) {
				return  true;
			}
			if(!path.exists()){
				log.error("创建文件夹:"+folderPath+"失败");
				return   false;
			}
		}
		return true;//永远不会执行到这里,不写这句就报语法错误
	}
	
	/**
	 * 创建指定的文件,空白文件,如果上层目录不存在则会创建
	 * @param pathName
	 * @return boolean
	 */
	public static boolean createFile(String pathName){
		File pathNameFile=new File(pathName);
		if(!pathNameFile.exists()){
			StringBuilder dictoryPath=new StringBuilder(pathName);
			dictoryPath.delete(getFileNameIndexFromPath(pathName), pathName.length());
			if ( !createDictory(dictoryPath.toString()) )return false;			
			try {
				pathNameFile.createNewFile();
				return true;
			} catch (IOException e) {
				log.error("创建文件:"+pathName+"失败");
				log.error(e.getMessage());
				return false;
			}
		}
		return true;
	}
	
	/**
	 * 判断文件或文件夹是否存在
	 * @param path
	 * @return boolean 存在返回true 
	 */
	public static boolean isExist(String path){
		return new File(path).exists();
	}
	
	/**
	 * 返回文件扩展名
	 * @param fileName
	 * @return String
	 */
	public static String getExtensionName(String fileName) {   
		if ((fileName != null) && (fileName.length() > 0)) {   
            int dot = fileName.lastIndexOf('.');   
            if ((dot >-1) && (dot < (fileName.length() - 1))) {   
                return fileName.substring(dot + 1).toLowerCase();   
            }   
        }   
        return "";   
    }   
	/**
	 * 返回指定文件夹下所有文件路径名
	 * @param folderPath
	 * @return List<String>
	 */
	public static List<String> getFilesFromFolder(String folderPath) {
		FileUtil myfile=new FileUtil();
		return myfile.getFilesOfFolder(folderPath);
	}
	
	/**
	 * 返回文件绝对路径内的上层目录
	 * @param absolutePath
	 * @return String
	 */
	public static String getParentPath(String absolutePath) {
		if (!isExist(absolutePath)) {
			log.info("文件不存在:"+absolutePath);
			return null;
		}
		File file=new File(absolutePath);
		return file.getParent();
	}
	
	/**
	 * 返回文件绝对路径内的文件名
	 * @param absolutePath
	 * @return String
	 */
	public static String getFileName(String absolutePath) {
		if (!isExist(absolutePath)) {
			log.info("文件不存在:"+absolutePath);
			return null;
		}
		File file=new File(absolutePath);
		return file.getName();
	}
	/**
	 * 返回文件夹下指定扩展名的文件列表
	 * @param folderPath
	 * @param extensionName
	 * @return List<String>
	 */
	public static List<String> getFilesFromFolder(String folderPath,String extensionName) {
		List<String>files=getFilesFromFolder(folderPath);
		List<String> tmpList=new ArrayList<>();
		
		if (null==files) return tmpList;
		if (null==extensionName || extensionName.length()<1) return tmpList;
		
		for (String filename : files) {
			String exname=getExtensionName(filename);
			if (exname.indexOf(extensionName.toLowerCase())>-1) tmpList.add(filename);
		}
		return tmpList;
	}
	
	/**
	 * 关闭stream,并且不抛出异常
	 * @param stream 待关闭的输入流
	 */
	public static void closeStreamIgnoreExpection(InputStream stream) {
		try{
			if(stream!=null){
				stream.close();
			}
		}catch (Exception e) {
			// do nothing here
		}
	}
	
	/**
	 * 重命名文件,输入参数为完全路径名
	 * @param oldPathName
	 * @param newPathName
	 * @return boolean
	 */
	public static boolean renameFile(String oldPathName, String newPathName) {
		if (StringUtil.IsNullOrEmpty(oldPathName) || StringUtil.IsNullOrEmpty(newPathName))return false;
		if (oldPathName.equals(newPathName))return false;
			
		// 新的文件名和以前文件名不同时,才有必要进行重命名
		File oldfile = new File(oldPathName);
		File newfile = new File(newPathName);
		
		if (newfile.exists()){// 若在该目录下已经有一个文件和新文件名相同,则不允许重命名
				log.error(newPathName + "已经存在!");
				return false;
		}else {
				oldfile.renameTo(newfile);
				return true;
		}
	}
	
	/**
	 * 拷贝单个文件 源和目的文件都为完全路径名
	 * @param srcPathName
	 * @param distPathName
	 * @return boolean
	 */
	public static boolean  copyFile(String srcPathName,String distPathName) {
		if (StringUtil.IsNullOrEmpty(srcPathName) || StringUtil.IsNullOrEmpty(srcPathName)) return false;
		if (distPathName.equals(srcPathName)) 		return false;
		return writeStream(readToFileInputStream(srcPathName), distPathName);
	}
	
	/**
	 * 拷贝文件夹
	 * @param srcFolderPath
	 * @param distFolderPath
	 * @return boolean
	 */
	public static boolean copyFolder(String srcFolderPath,String distFolderPath) {
		if (StringUtil.IsNullOrEmpty(srcFolderPath) || StringUtil.IsNullOrEmpty(distFolderPath)) return false;
		if (srcFolderPath.toLowerCase().equals(distFolderPath)) return false;
		 // 新建目标目录
		createDictory(distFolderPath);
        // 获取源文件夹当前下的文件或目录   
		File[] file = (new File(srcFolderPath)).listFiles();
		for (int i = 0; i < file.length; i++) {
			if (file[i].isFile()) {
				// 源文件
				String sourceFile = file[i].getAbsolutePath();
				// 目标文件
				String targetFile =new File(distFolderPath).getAbsolutePath() +"/"+file[i].getName();
				copyFile(sourceFile,targetFile);
			}
			if (file[i].isDirectory()) {
				// 准备复制的源文件夹
				String dir1 = srcFolderPath + "/" + file[i].getName();
				// 准备复制的目标文件夹
				String dir2 = distFolderPath + "/" + file[i].getName();
				copyFolder(dir1, dir2);
			}
		}

		return true;
	}
	
	/**
	 * 从文件完全路径中,获取最后一个"/"或"\"的位置
	 * @param filePathName
	 * @return int
	 */
	public static int getFileNameIndexFromPath(String filePathName){
		if (filePathName.toLowerCase().lastIndexOf("/")>-1){
			return  filePathName.toLowerCase().lastIndexOf("/");
		}else{
			return  filePathName.toLowerCase().lastIndexOf("\\");
		}
	}

	/**
	 * 返回目录下所有文件列表
	 * @param folderPath
	 * @return List<String>
	 */
	private  List<String> getFilesOfFolder(String folderPath) {
		File dir = new File(folderPath); 
        File[] files = dir.listFiles(); 
        if (files == null) {
//        	log.info("此目录下未取得任何文件:"+folderPath);
            return null; 
        }
        
        for (int i = 0; i < files.length; i++) { 
	            if (files[i].isDirectory()) { 
	            	getFilesOfFolder(files[i].getAbsolutePath()); 
	            } else { 
	                filelist.add(files[i].getAbsolutePath());                    
	            }     
         } 
		
		return filelist;
	}
	
}