package org.flashtool.system;

import java.io.*;
import java.util.*;
	 
import lombok.extern.slf4j.Slf4j;

@Slf4j
	/**
	 * The CommentedProperties class is an extension of java.util.Properties
	 * to allow retention of comment lines and blank (whitespace only) lines
	 * in the properties file.
	 *
	 */
public class CommentedPropertiesFile extends java.util.Properties {
	 
	    /**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		/**
	     * Use a Vector to keep a copy of lines that are a comment or 'blank'
	     */
	    private LinkedList<String> lines = new LinkedList<String>();
	    private Properties commented = new Properties();
	 
	    /**
	     * Load properties from the specified InputStream.
	     * Overload the load method in Properties so we can keep comment and blank lines.
	     * @param   inStream   The InputStream to read.
	     */
	    public void load(File f) throws IOException
	    {
	    	FileInputStream fin = new FileInputStream(f);
	    	load(fin);
	    	fin.close();
	    	FileReader fread = new FileReader(f);
	    	Scanner sc = new Scanner(fread);
	    	while (sc.hasNextLine()) {
	    		String line = sc.nextLine();
	    		if (line.trim().startsWith("#") || line.trim().length()==0) {
	    			if (!line.trim().startsWith("##") && line.trim().contains("=")) {
	    				setCommentedProperty(line.trim().split("=")[0].substring(1).trim(),line.trim().split("=")[1].trim());
	    			} else {
	    				lines.add(line);
	    			}
	    		}
	    		else {
	    			// Adding a key
	    			lines.add(line.trim().split("=")[0].trim());
	    		}
	    	}
	    	fread.close();
	    }
	 
	    /**
	     * Write the properties to the specified OutputStream.
	     *
	     * Overloads the store method in Properties so we can put back comment 
	     * and blank lines.                                                  
	     *
	     * @param out   The OutputStream to write to.
	     * @param header Ignored, here for compatability w/ Properties.
	     *
	     * @exception IOException
	     */
	    public void store(OutputStream out, String header) throws IOException
	    {
        // The spec says that the file must be encoded using ISO-8859-1.
	        PrintWriter writer
	        = new PrintWriter(new OutputStreamWriter(out, "ISO-8859-1"));
	 
	        // We ignore the header, because if we prepend a commented header
	        // then read it back in it is now a comment, which will be saved
	        // and then when we write again we would prepend Another header...
	 
	        Iterator<String> i = lines.iterator();
	        while (i.hasNext()) {
	        	String line = i.next();
	        	if (containsKey(line))
	        		writer.println(line+"="+getProperty(line));
	        	else if (commented.containsKey(line)) writer.println("#"+line.substring(2)+"="+commented.getProperty(line));
	        	else writer.println(line);
	        }
	        writer.flush ();
	        writer.close();
	        out.flush();
	        out.close();
	    }
	    
	    public void updateWith(File f) throws IOException {
	    	CommentedPropertiesFile p = new CommentedPropertiesFile();
	    	p.load(f);
	    	Iterator<Object> i = p.keySet().iterator();
	    	while (i.hasNext()) {
	    		String key = (String)i.next();
	    		unComment(key);
	    		setProperty(key,p.getProperty(key));
	    	}
	    	Iterator<Object> i1 = p.commentedKeySet().iterator();
	    	while (i1.hasNext()) {
	    		String key = ((String)i1.next()).substring(2);
	    		comment(key);
	    		setCommentedProperty(key,p.getCommentedProperty(key));
	    	}
	    }
	 
	    public String getCommentedProperty(String key) {
	    	return commented.getProperty("_c"+key);
	    }
	    
	    public Set commentedKeySet() {
	    	return commented.keySet();
	    }
	    
	    public void updateProperty(String key, String value) {
	    	if (isCommented(key) && !isNotCommented(key)) unComment(key);
	    	setProperty(key,value);
	    }
	    
	    public void updateCommentedProperty(String key, String value) {
	    	if (isNotCommented(key) && !isCommented(key)) comment(key);
	    	setCommentedProperty(key,value);
	    }
	    
	    public void setCommentedProperty(String key, String value) {
	    	commented.setProperty("_c"+key, value);
	    	if (lines.indexOf("_c"+key)<0) lines.add("_c"+key);
	    }
	    
	    public Object setProperty(String key, String value) {
	    	if (lines.indexOf(key)<0) lines.add(key);
	    	return super.setProperty(key,value);
	    }
	    
	    public boolean isCommented(String key) {
	    	return commented.containsKey("_c"+key);
	    }

	    public boolean isNotCommented(String key) {
	    	return containsKey(key);
	    }
	    
	    public void removeCommented(String key) {
	    	commented.remove("_c"+key);
	    }
	    
	    public void remove(String key) {
	    	super.remove(key);
	    }
	    
	    public void unComment(String key) {
	    	if (getCommentedProperty(key)!=null) {
		    	setProperty(key, getCommentedProperty(key));
		    	removeCommented(key);
		    	int index = lines.indexOf("_c"+key);
		    	lines.set(index, key);
	    	}
	    	
	    }
	    
	    public void comment(String key) {
	    	if (getProperty(key)!=null) {
	    		commented.setProperty("_c"+key, getProperty(key));
	    		remove(key);
	    		int index = lines.indexOf(key);
	    		lines.set(index, "_c"+key);
	    	}
	    }

	    /**
	     * Add a comment or blank line or comment to the end of the CommentedProperties.
	     *
	     * @param   line The string to add to the end, make sure this is a comment
	     *             or a 'whitespace' line.
	     */
	    public void addComment(String line) {
	        lines.add(line);
	    }

}