package cokr.xit.ens.modules.example.thread;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class MultiThreadSample {

	/**
	 * <pre>메소드 설명: Multi Thread 예제</pre>
	 * @throws InterruptedException
	 * @throws ExecutionException void 요청처리 후 응답객체
	 * @author: 박민규
	 * @date: 2021. 8. 30.
	 */
	public static void example() throws InterruptedException, ExecutionException {
		
		long startTme = System.currentTimeMillis();
		
		Calculator calculrator = new Calculator(0);
		HashSet<Integer> set1 = new HashSet<Integer>();
		HashSet<Integer> set2 = new HashSet<Integer>();
		
		Callable<Void> callable1 = () -> {
			for(int i=0; i<50; i++) {
				set1.add(calculrator.getNextIndex());
				log.info(String.format("callable1 => %05d", i));
//				Thread.sleep(500);
			}
			return null;
		};
		Callable<Void> callable2 = () -> {
			for(int i=0; i<100; i++) {
				set2.add(calculrator.getNextIndex());
				log.info(String.format("callable2 => %05d", i));
				Thread.sleep(300);
			}
			return null;
		};
		
		
		ExecutorService executorService = Executors.newFixedThreadPool(3);
		Future<Void> future1 = executorService.submit(callable1);
		Future<Void> future2 = executorService.submit(callable2);
		future1.get();
		future2.get();
		
		
		if(future1.isDone() && future2.isDone()) {
			System.out.println(set1.size());
			System.out.println(set2.size());
		}
		
		executorService.shutdown();	//executor 종료
		log.info(String.format("Executor Shutdown => %s", executorService.isShutdown()));
		
		long endTme = System.currentTimeMillis();
		log.info(String.format("수행시간::: %s seconds...", (endTme-startTme)/1000) );
	} 
	
	
	
	
	
	


	/**
	 * <pre>메소드 설명: Multi Thread 결과 리턴 예제
	 * 	-.runable로 작성한 Task를 호출하여 공유객체에 결과를 return 받는다.
	 * </pre>
	 * @throws InterruptedException
	 * @throws ExecutionException void 요청처리 후 응답객체
	 * @author: 박민규
	 * @date: 2021. 8. 30.
	 */
	@SuppressWarnings("unchecked")
	public static void runnableExample() throws InterruptedException, ExecutionException {
		
		long startTme = System.currentTimeMillis();

		
		List<TaskDtoSample> result = new ArrayList<TaskDtoSample>();
		for(int i=0; i<102; i++) {
			TaskDtoSample dto = new TaskDtoSample();
			dto.setId(i);
			result.add(dto);
		}
		
		//thread 갯수 설정
		int unitSize = Runtime.getRuntime().availableProcessors();
		int cntDiv = result.size()/unitSize;
		int cntMod = result.size()%unitSize;
		int cntThread = cntDiv+(cntMod>0?1:0);
		ExecutorService executorService = Executors.newFixedThreadPool(cntThread);	
		
		//Task 단위별 실행
		List<Future> listFuture = new ArrayList<>();	//future 목록
		for(int i=0; i<cntThread; i++) {
			List<TaskDtoSample> taskList = null;
			if(i==cntThread-1)
				taskList = result.subList(i*unitSize, cntMod>0?result.size():(i+1)*unitSize);
			else
				taskList = result.subList(i*unitSize, (i+1)*unitSize);
			
			Runnable task = new TaskRunnableReturnSample(taskList, "task"+i);	
			listFuture.add(executorService.submit(task, result));	//task 작업 실행
		}
		
		//task block 처리
		Iterator<Future> it = listFuture.iterator();
		while(it.hasNext()) {
			Future<List<TaskDtoSample>> future = it.next();
			List<TaskDtoSample> list = future.get();
			
			System.out.println(String.format("결과건수=%s", list.size()));
			System.out.println(String.format("결과=%s", list.toString()));
			
//			long endTme = System.currentTimeMillis();
//			log.info(String.format("수행시간::: %s seconds...", (endTme-startTme)/1000) );
		}
		
		 
//		for(TaskReturnDtoSample row : result)
//			log.info(String.format("수행결과::: %s ", row.getResult()));
			
		

//		boolean isShutdown = executorService.isShutdown();
//		List<Runnable> listRunnable = executorService.shutdownNow() ;
//		executorService.shutdown();
//		isShutdown = executorService.isShutdown();
//		boolean isTerminated = executorService.isTerminated();
//		boolean isIdle = executorService.awaitTermination(30, TimeUnit.SECONDS);
		
		executorService.shutdown();	//executor 종료
		log.info(String.format("Executor Shutdown => %s", executorService.isShutdown()));
		
		long endTme = System.currentTimeMillis();
		log.info(String.format("수행시간::: %s seconds...", (endTme-startTme)/1000) );
	} 
	
	

	/**
	 * <pre>메소드 설명: Multi Thread 예제
	 * 	-.callable로 작성한 Task를 호출하여 Task 처리단위별 결과를 return 받는다.
	 * </pre>
	 * @throws InterruptedException
	 * @throws ExecutionException void 요청처리 후 응답객체
	 * @author: 박민규
	 * @date: 2021. 8. 30.
	 */
	public static void callableExample() throws InterruptedException, ExecutionException {
		
		long startTme = System.currentTimeMillis();

		
		List<TaskDtoSample> result = new ArrayList<TaskDtoSample>();
		for(int i=0; i<102; i++) {
			TaskDtoSample dto = new TaskDtoSample();
			dto.setId(i);
			result.add(dto);
		}
		
		//thread 갯수 설정
//		int unitSize = Runtime.getRuntime().availableProcessors();
		int unitSize = 10;
		int cntDiv = result.size()/unitSize;
		int cntMod = result.size()%unitSize;
		int cntThread = cntDiv+(cntMod>0?1:0);
		ExecutorService executorService = Executors.newFixedThreadPool(cntThread);	
		List<Future> listFuture = new ArrayList<>();	//future 목록
		
		//Task 단위별 실행
		for(int i=0; i<cntThread; i++) {
			List<TaskDtoSample> taskList = null;
			if(i==cntThread-1)
				taskList = result.subList(i*unitSize, cntMod>0?result.size():(i+1)*unitSize);
			else
				taskList = result.subList(i*unitSize, (i+1)*unitSize);
			
			Callable<List<TaskDtoSample>> task = new TaskCallableExample(taskList);
			listFuture.add(executorService.submit(task));	//task 작업 실행
		}
		
		//task block 처리
		Iterator<Future> it = listFuture.iterator();
		while(it.hasNext()) {
			Future<List<TaskDtoSample>> future = it.next();
			List<TaskDtoSample> list = future.get();
			System.out.println(String.format("결과건수=%s", list.size()));
			System.out.println(String.format("결과=%s", list.toString()));
		}
		
		executorService.shutdown();	//executor 종료
		log.info(String.format("Executor Shutdown => %s", executorService.isShutdown()));
		
		long endTme = System.currentTimeMillis();
		log.info(String.format("수행시간::: %s seconds...", (endTme-startTme)/1000) );
	} 
	
	/**
	 * <pre>메소드 설명: Multi Thread 예제
	 * 	-.CompletionService로 Task를 호출 한다.
	 * </pre>
	 * @throws InterruptedException
	 * @throws ExecutionException void 요청처리 후 응답객체
	 * @author: 박민규
	 * @date: 2021. 8. 30.
	 */
	public static void completionServiceExample() throws InterruptedException, ExecutionException {
		
		long startTme = System.currentTimeMillis();
		
		
		List<TaskDtoSample> result = new ArrayList<TaskDtoSample>();
		for(int i=0; i<102; i++) {
			TaskDtoSample dto = new TaskDtoSample();
			dto.setId(i);
			result.add(dto);
		}
		
		//thread 갯수 설정
//		int unitSize = Runtime.getRuntime().availableProcessors();
		int unitSize = 10;
		int cntDiv = result.size()/unitSize;
		int cntMod = result.size()%unitSize;
		int cntThread = cntDiv+(cntMod>0?1:0);
		ExecutorService executorService = Executors.newFixedThreadPool(cntThread);	
		CompletionService completionService = new ExecutorCompletionService(executorService);
		List<Future> listFuture = new ArrayList<>();	//future 목록
		
		//Task 단위별 실행
		for(int i=0; i<cntThread; i++) {
			List<TaskDtoSample> taskList = null;
			if(i==cntThread-1)
				taskList = result.subList(i*unitSize, cntMod>0?result.size():(i+1)*unitSize);
			else
				taskList = result.subList(i*unitSize, (i+1)*unitSize);
			
			Callable<List<TaskDtoSample>> task = new TaskCallableExample(taskList);
			listFuture.add(completionService.submit(task));	//task 작업 실행
		}
		 
		//task block 처리-Case1. 순차 처리
//		Iterator<Future> it = listFuture.iterator();
//		while(it.hasNext()) {
//			Future<List<TaskReturnDtoSample>> future = it.next();
//			List<TaskReturnDtoSample> list = future.get();
//			System.out.println(String.format("결과건수=%s", list.size()));
//			System.out.println(String.format("결과=%s", list.toString()));
//		}
		//task block 처리-Case2. 비순차 처리
		int complete = 0;
		while (true) {
			if(complete == cntThread)	
				break; 
//			Future future = completionService.poll();
//			Future future = completionService.poll(30, TimeUnit.SECONDS);	
			Future future = completionService.take();
			if(future.isDone())	
				complete++; 	
			List<TaskDtoSample> list = (List<TaskDtoSample>) future.get();
			System.out.println(String.format("결과건수=%s", list.size()));
			System.out.println(String.format("결과=%s", list.toString()));
		}
		
		executorService.shutdown();	//executor 종료
		log.info(String.format("Executor Shutdown => %s", executorService.isShutdown()));
		
		
		long endTme = System.currentTimeMillis();
		log.info(String.format("수행시간::: %s seconds...", (endTme-startTme)/1000) );
	} 
}

