package com.frameworkset.util;

import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.log4j.Logger;

import com.frameworkset.util.DaemonThread.FileBean;

/**
 * <p>Title: </p>
 *
 * <p>Description: </p>
 *
 * <p>Copyright: Copyright (c) 2005</p>
 *
 * <p>Company: </p>
 *
 * @author biaoping.yin
 * @version 1.0
 */
public class DaemonThread extends java.lang.Thread 
{
	static class FileBean
	{
		 public FileBean(File file, 
				ResourceInitial init) {
			super();
			this.file = file;	
			this.inits = new ArrayList<ResourceInitial>();
			inits.add(init);
			this.oldModifiedTime =  file.lastModified();
			
		}
		 private boolean removeflag = false;
		 private File file;
		 private long lastModifiedTime;
		 private long oldModifiedTime; 
		 private List<ResourceInitial> inits;
		/**
		 * @return the file
		 */
		public File getFile() {
			return file;
		}
		/**
		 * @param file the file to set
		 */
		public void setFile(File file) {
			this.file = file;
		}
		/**
		 * @return the lastModifiedTime
		 */
		public long getLastModifiedTime() {
			return lastModifiedTime;
		}
		/**
		 * @param lastModifiedTime the lastModifiedTime to set
		 */
		public void setLastModifiedTime(long lastModifiedTime) {
			this.lastModifiedTime = lastModifiedTime;
		}
		/**
		 * @return the oldModifiedTime
		 */
		public long getOldModifiedTime() {
			return oldModifiedTime;
		}
		/**
		 * @param oldModifiedTime the oldModifiedTime to set
		 */
		public void setOldModifiedTime(long oldModifiedTime) {
			this.oldModifiedTime = oldModifiedTime;
		}
		
		
		public boolean checkChanged()
		{
			if(file == null)
				return false;
			 boolean exist = file.exists(); 
	         if(!exist)
	         {
	         	//init.destroy();
	         	return false;
	         }
	         this.lastModifiedTime = file.lastModified();
	         if(this.oldModifiedTime != this.lastModifiedTime)
	         {
	        	 //begin 1 resolved java.util.ConcurrentModificationException by biaoping.yin on 2015.03.09
//	             //System.out.println("Reload changed file：" + file.getAbsolutePath());
//	             log.debug("Reload changed file：" + file.getAbsolutePath());
//	             this.oldModifiedTime = this.lastModifiedTime;
//	             try {
//					init.reinit();
//					log.debug("Reload changed file " + file.getAbsolutePath() + " sucessed." );
//				} catch (Exception e) {
//					log.debug("Reload changed file " + file.getAbsolutePath() + " failed:" ,e);
//				} 
	             return true;
	         }
	         else
	         {
	        	 return false;
	         }
		}
		
		/**
		 * 1 resolved java.util.ConcurrentModificationException by biaoping.yin on 2015.03.09
		 */
		public void reinit()
		{
			 //System.out.println("Reload changed file：" + file.getAbsolutePath());
            log.debug("Reload changed file：" + file.getAbsolutePath());
            this.oldModifiedTime = file.lastModified();
            try {
            	for(int i = 0; inits != null && i < this.inits.size(); i++)
            	{
            		ResourceInitial init = inits.get(i);
            		try {
						init.reinit();
					} catch (Exception e) {
						log.debug("Reload changed file " + file.getAbsolutePath() + " failed:" ,e);
					}
            	}
            	
				log.debug("Reload changed file " + file.getAbsolutePath() + " sucessed." );
			} catch (Exception e) {
				log.debug("Reload changed file " + file.getAbsolutePath() + " failed:" ,e);
			} 
		}
		//end 1 resolved java.util.ConcurrentModificationException by biaoping.yin on 2015.03.09 
		public boolean isRemoveflag() {
			return removeflag;
		}
		public void setRemoveflag(boolean removeflag) {
			this.removeflag = removeflag;
		}
		public void addResourceInit(ResourceInitial init) {
			if(!this.contain(init))
				this.inits.add(init);
			
		}
		
		private boolean contain(ResourceInitial init)
		{
			
			if(init instanceof UUIDResource)
			{
				String uuid = ((UUIDResource)init).getUUID();
				for(int i = 0; i < this.inits.size(); i ++)
				{
					ResourceInitial initOld = this.inits.get(i);
					if(initOld instanceof UUIDResource)
					{
						if(((UUIDResource)initOld).getUUID().equals(uuid))
							return true;
					}
				}
				return false;
			}
			else
				return true;//兼容旧版本,没有实现UUIDResource接口的初始化资源初始化接口只允许存在一个ResourceInitial
		}
		 
	}
    private static Logger log = Logger.getLogger(DaemonThread.class);

//    private long lastModifiedTime;
//    private long oldModifiedTime;
    private long refresh_interval = 5000;
    private Map<String,FileBean> files = new HashMap<String,FileBean>();
//    private ResourceInitial init;
    private boolean started = false;
    private boolean stopped = false;
    public DaemonThread(String fileName,ResourceInitial init)
    {
    	this(fileName,5000,init);
    }
    
    public DaemonThread(long refresh_interval,String name)
    {
    	super(name);
    	this.refresh_interval = refresh_interval;
    	 this.setDaemon(true);
    }
    
    public void addFile(String fileName,ResourceInitial init)
    {
    	File file = new File(fileName);
        if(!file.exists())
        {
        	URL confURL = ResourceInitial.class.getClassLoader().getResource(fileName);
        	if(confURL != null)
        		file = new File(confURL.getPath() );
        }
        addFile(file,init);
//    	this.files.add(new FileBean(file,init));
//    	 log.debug("Add file " + file.getAbsolutePath() + " to damon thread which moniting file modified.");
    }
    private FileBean containFile(File file)
    {
    	if(this.files == null || files.size() <= 0 )
    		return null;
    	if(file == null)
    		return null;
    	FileBean f = files.get(file.getAbsolutePath());
    	if(f == null)
    		return null;
    	if(f.isRemoveflag())
			f.setRemoveflag(false);
    	return f;
    	
    }
    public  void addFile(File file,ResourceInitial init)
    {   
    	if(!file.exists())
    	{
    		log.debug(file.getAbsolutePath()+" 对应的文件不存在，忽略修改检测.");
    		return;
    	}
    	synchronized(lock)
    	{
    		FileBean f = this.containFile(file);
	    	if(f == null)
	    	{
	    		this.files.put(file.getAbsolutePath(), new FileBean(file,init));
//	    		log.debug(file.getAbsolutePath() + " has been monitored,ignore this operation.");
//	    		return;
	    	}
	    	else
	    	{
	    		f.addResourceInit(init);
	    	}
	    	 log.debug("Add file " + file.getAbsolutePath() + " to damon thread which moniting file modified.");
	    	
    	}
    }
    public  void removeFile(String filepath)
    {   
    	File file = new File(filepath);
        if(!file.exists())
        {
        	URL confURL = ResourceInitial.class.getClassLoader().getResource(filepath);
        	if(confURL != null)
        		file = new File(confURL.getPath() );
        }
    	removeFile( file);
    }
    public  void removeFile(File file)
    {   
    	
    	synchronized(lock)
    	{
    		if(this.files == null || files.size() <= 0 )
        		return ;
        	if(file == null)
        		return ;
        	FileBean f = files.get(file.getAbsolutePath());
        	if(f != null)
        	{
        		f.setRemoveflag(true);
        		log.debug("marked  file " + file.getAbsolutePath() + " to be removed from damon thread which moniting file modified.");
        	}
//        	for(FileBean f:files)
//        	{
//        		if(f.getFile() != null && f.getFile().getAbsolutePath().equals(file.getAbsolutePath()))
//        		{
////        			files.remove(f);
//        			f.setRemoveflag(true);
//        		}
//        	}
	    	 
    	}
    }
    
    
    
    public DaemonThread(String fileName,long refresh_interval,ResourceInitial init)
    {
    	super(fileName);
    	this.refresh_interval = refresh_interval > 0 ?refresh_interval:10000;
        File file = new File(fileName);
        if(!file.exists())
        {
        	URL confURL = ResourceInitial.class.getClassLoader().getResource(fileName);
        	if(confURL != null)
        		file = new File(confURL.getPath() );
        }
//        this.oldModifiedTime = file.lastModified();
        
//        this.init = init;
        if(!file.exists())
    	{
    		log.debug(file.getAbsolutePath()+" 对应的文件不存在，忽略修改检测.");
    		return;
    	}
        this.files.put(file.getAbsolutePath(),new FileBean(file,init));
        log.debug("Add file " + file.getAbsolutePath() + " to damon thread which moniting file modified.");
        this.setDaemon(true);
    }
    private Object lock = new Object();
    public DaemonThread(File file,ResourceInitial init)
    {

       
//        this.oldModifiedTime = file.lastModified();
//        this.file = file;
//        this.init = init;
//    	this.files.add(new FileBean(file,init));
    	 this.files.put(file.getAbsolutePath(),new FileBean(file,init));
    	log.debug("Add file " + file.getAbsolutePath() + " to damon thread which moniting file modified.");
        this.setDaemon(true);
    }


    public boolean started()
    {
        return started;
    }
    public void run()
    {
    	 log.info("Start check files is changed or not.if some files is changed,the resources of these files will be refreshed use ResourceInit interface.");
        started = true;
        for(;;)
        {
        	if(stopped)
        		break;
            //System.out.println("Check module file's last modified time.");
            try {
                sleep(refresh_interval);
            } catch (InterruptedException ex) {
//                notifyAll();
            	break;
            }
            if(files == null || files.size() == 0)
            	continue;
            List<FileBean> changedFiles = new ArrayList<FileBean>();

            synchronized(lock)
            {
            	//modified by biaoping.yin on 2015.03.09:忽略扫描异常，如果出现异常继续扫描
            	try
            	{
            		Iterator<Entry<String, FileBean>> entries = files.entrySet().iterator();
            		while(entries.hasNext())
            		{
            			if(stopped)
		            		break;
            			Entry<String, FileBean> entry = entries.next();
            			FileBean f = entry.getValue();
            			if(f.isRemoveflag())
		            		continue;
            			try
		            	{
		            		if(f.checkChanged())
		            			changedFiles.add(f);
		            	}
		            	catch(Exception e)
		            	{
		            		String filePath = f.getFile() != null?f.getFile().getAbsolutePath():"null";
		            		String tname = this.getName() != null?this.getName():"null";
		            		log.debug("check file["+filePath+"] modified thread["+tname+"]  exception occur:",e);
		            	}
            		}
		            
		            
            	}
            	catch(Exception e)
            	{
            		log.debug("check file modified thread["+this.getName()+"] error occur:",e);
            	}
            }
            
            if(changedFiles.size() > 0)
            {
            	for(FileBean f:changedFiles)
            	{
            		if(stopped)
	            		break;
            		try
	            	{
	            		f.reinit();
	            	}
	            	catch(Exception e)
	            	{
	            		String filePath = f.getFile() != null?f.getFile().getAbsolutePath():"null";
	            		String tname = this.getName() != null?this.getName():"null";
	            		log.debug("check file["+filePath+"] modified thread["+tname+"] exception occur:",e);
	            	}
            	}
            }
            
//            boolean exist = file.exists(); 
//            if(!exist)
//            {
//            	//init.destroy();
//            	break;
//            }
//            this.lastModifiedTime = file.lastModified();
//            if(this.oldModifiedTime != this.lastModifiedTime)
//            {
//                //System.out.println("Reload changed file：" + file.getAbsolutePath());
//                log.debug("Reload changed file：" + file.getAbsolutePath());
//                this.oldModifiedTime = this.lastModifiedTime;
//                init.reinit(); 
//                
//            }
        }
    }
    
    public void stopped()
    {
    	this.stopped = true;
    	synchronized(lock){
    		if(files != null)
	       	 {
	       		 files.clear();
	       		 files = null;
	       	 }
    		lock.notifyAll();
    	}
//    	synchronized(this)
//    	{
//    		this.notifyAll();
//    	}
//    	 
    	 
    }
    
    
}
