package kr.gmtc.gw.comp.thread;

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

import kr.gmtc.gw.comp.thread.handler.CustomThreadOnTerminate;
import kr.gmtc.gw.comp.thread.handler.CustomThreadWork;

public class CustomThread extends Thread{
	
	/* 대기시간 없음 */
	public static final long NO_SLEEP = 0;
	/* 1밀리 초 */
	public static final long SLEEP_MILLI_SEC = 1;
	/* 1초 */
	public static final long SLEEP_SECOND = 1000;
	/* 30초 */
	public static final long SLEEP_HALF_MINIUTE = 30000;
	/* 1분 */
	public static final long SLEEP_MINIUTE = 60000;
	
	public final String controllClassName;
	public final long repeatMiliSec;
	public final CustomThreadWork definedWork;
	public final CustomThreadOnTerminate definedTerminate;
	public final Logger logger;
	private boolean running;

	/** 
	 * 인터럽트를 받을 시 스레드가 종료됨.<br>
	 * {@link Thread#sleep(long)} 기반으로 재실행 간격을 설정하므로 정확한 실행시간을 보장하지 않음.<br>
	 * 정확한 실행시간 보장이 필요 할 경우 sleep 간격을 짧게 설정하고 호출위치에서 시간확인<br>
	 * 정상적인 종료는 {@link #gracefulStop()}으로 종료함
	 * @param threadName 스레드 이름
	 * @param controllClass 스레드 관리 클래스, 일반적으로 this 사용
	 * @param repeatMiliSec Sleep 시간(밀리 초), 0이하의 경우 대기시간 없음
	 * @param definedWork 반복할 작업
	 * @param definedTerminate 스레드가 인터럽트에 의해 종료될 경우 할 작업
	 * @param autoStart 생성즉시 실행
	 */
	
	public CustomThread(String threadName, Object controllClass, long repeatMiliSec, 
						CustomThreadWork definedWork, CustomThreadOnTerminate definedTerminate, boolean autoStart) {
		
		if (definedWork == null) {
			throw new IllegalArgumentException("[CustomThread] - definedWork is null.");
		}
		
		this.definedWork = definedWork;
		this.definedTerminate = definedTerminate;
		this.controllClassName = controllClass == null ? "" : controllClass.getClass().getSimpleName();
		this.repeatMiliSec = repeatMiliSec > 0 ? repeatMiliSec : 0;
		this.logger = LoggerFactory.getLogger(CustomThread.class);
		this.running = false;
		
		setName(threadName);
		setDaemon(true);
		if (autoStart) {
			this.start();
		}
	}
	
	
	@Override
	public void run() {
		logger.info("[CustomThread-"+getName()+"] Started.");
		while ( this.running && !this.isInterrupted()) {
			try {
				try {
					this.definedWork.work();
				} finally {
					if (this.repeatMiliSec > 0) {
						Thread.sleep(this.repeatMiliSec);
					}
				}
			} catch(InterruptedException e) { // 인터럽트 수신시 종료
				logger.error("[CustomThread-"+getName()+"] Interrupted. "+ e.toString());
				Thread.currentThread().interrupt();
				break;
			} catch(Exception e) { // 처리되지 않은 예외 로깅, 예외에 의한 무한루프에 주의
				logger.error("[CustomThread-"+getName()+"] " + e.toString() + ":" + e.getStackTrace()[0] );
			}
		}
		
		if(this.definedTerminate != null) {
			this.definedTerminate.onTerminate();
		}

		logger.error("[CustomThread-"+ getName()+"] Stoped.");
		
	}
	
	@Override
	public String toString() {
		
		return "CustomThread [controllClass=" + this.controllClassName + ", threadName=" + getName() +
				", runnig=" + this.running + ", alive=" + isAlive()+ ", repeatMiliSec=" + this.repeatMiliSec + 
				", definedTerminate=" + (this.definedTerminate == null ? "no" : "yes") + "]";
	}
	
	@Override
	public synchronized void start() {
		this.running = true;
		super.start();
	}

	public synchronized void start(int priority) {
		setPriority(priority);
		
		this.start();
	}
	
	/**
	 * 스레드 정상종료, 진행중인 작업 완료 후 종료됨.
	 */
	public void gracefulStop() {
		this.running = false;
	}
}
