/**
 * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.crunii.com</a>
 */
package com.crunii.micro.common.file;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel.MapMode;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author 田平 create 2017年9月5日上午11:05:47
 */
public class MultReadFile {
	protected Logger log = LoggerFactory.getLogger(getClass());
	private File file;
	private LineHandler handler = new LineHandlerImpl();
	private int threadSize = 2;// 默认2个线程，实际线程因为文件平均分片的问题不是绝对平均所有可能会少一个线程。
	private int lineSize = 0;// 文件行数，空行不算行数
	private int bufferSize = 1024 * 1024;
	private String charset = "UTF-8";// 源文件字符集

	public static void main(String[] args) {
		LineHandlerImpl handler = new LineHandlerImpl();
		String url = "C:\\Users\\Administrator\\Desktop\\start2.txt";
		File file = new File(url);
		MultReadFile readFile = new MultReadFile(file, handler, "GBK", 1024 * 1024 * 10, 3);
		readFile.read();
	}

	public void read() {
		RandomAccessFile raf = null;
		ExecutorService threadPool = null;
		try {
			threadPool = Executors.newFixedThreadPool(20);
			raf = new RandomAccessFile(file, "r");
			List<Range> ranges = new ArrayList<Range>();
			long section = raf.length() / threadSize;
			if (section == 0) {
				section = 1;
			}
			Range.sharding(ranges, raf, 0, section);
			CountDownLatch doneSignal = new CountDownLatch(ranges.size());
			List<RangeReader> readers = new ArrayList<RangeReader>();
			for (Range range : ranges) {
				MappedByteBuffer mb = raf.getChannel().map(MapMode.READ_ONLY, range.getStart(), range.getLength());
				RangeReader reader = new RangeReader(handler, mb, bufferSize, charset, doneSignal);
				threadPool.execute(reader);
				readers.add(reader);
			}
			doneSignal.await();
			for (RangeReader reader : readers) {
				lineSize = lineSize + reader.getLineSize();
			}
			log.info(file.getName() + " has " + lineSize + " rows.");
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			if (threadPool != null) {
				threadPool.shutdown();
			}
			if (raf != null) {
				try {
					raf.close();
				} catch (IOException e) {
					throw new RuntimeException(e);
				}
			}
		}

	}

	public MultReadFile(File file) {
		this.file = file;
	}

	public MultReadFile(File file, LineHandler handler) {
		this.file = file;
		this.handler = handler;
	}

	public MultReadFile(File file, LineHandler handler, String charset) {
		this.file = file;
		this.handler = handler;
	}

	public MultReadFile(File file, LineHandler handler, String charset, int bufferSize) {
		this.file = file;
		this.handler = handler;
		this.charset = charset;
		this.bufferSize = bufferSize;
	}

	public MultReadFile(File file, LineHandler handler, String charset, int bufferSize, int threadSize) {
		this.file = file;
		this.handler = handler;
		this.charset = charset;
		this.bufferSize = bufferSize;
		this.threadSize = threadSize;
	}

	/**
	 * @return the file
	 */
	public File getFile() {
		return file;
	}

	/**
	 * @param file the file to set
	 */
	public void setFile(File file) {
		this.file = file;
	}

	/**
	 * @return the handler
	 */
	public LineHandler getHandler() {
		return handler;
	}

	/**
	 * @param handler the handler to set
	 */
	public void setHandler(LineHandler handler) {
		this.handler = handler;
	}

	/**
	 * @return the threadSize
	 */
	public int getThreadSize() {
		return threadSize;
	}

	/**
	 * @param threadSize the threadSize to set
	 */
	public void setThreadSize(int threadSize) {
		this.threadSize = threadSize;
	}

	/**
	 * @return the lineSize
	 */
	public int getLineSize() {
		return lineSize;
	}

	/**
	 * @param lineSize the lineSize to set
	 */
	public void setLineSize(int lineSize) {
		this.lineSize = lineSize;
	}

	/**
	 * @return the bufferSize
	 */
	public int getBufferSize() {
		return bufferSize;
	}

	/**
	 * @param bufferSize the bufferSize to set
	 */
	public void setBufferSize(int bufferSize) {
		this.bufferSize = bufferSize;
	}

	/**
	 * @return the charset
	 */
	public String getCharset() {
		return charset;
	}

	/**
	 * @param charset the charset to set
	 */
	public void setCharset(String charset) {
		this.charset = charset;
	}

}
