package railo.commons.io.ini;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

import railo.commons.io.IOUtil;
import railo.commons.io.res.Resource;
import railo.runtime.type.Struct;
import railo.runtime.type.StructImpl;

/**
* read a ini file and allow to modifie and read the data
*/
public final class IniFile {

	private Map sections;
	private final Resource file;
 
	private static Map newMap() {return new LinkedHashMap();}
 
 /**
  * Constructor for the IniFile object
  *
  * @param file ini FIle
 * @throws IOException
  */
	 public IniFile(Resource file) throws IOException {
	     this.file=file;
	     sections = newMap();
	     InputStream is=null;
	     if(!file.exists())file.createFile(false);
	     try {
	    	 load(is=file.getInputStream());
	     }
	     finally {
	    	 IOUtil.closeEL(is);
	     }
	 }
	 
	 public IniFile(InputStream is) throws IOException {
	     sections = newMap();
	     load(is);
	     file=null;
	 }

 /**
  * Sets the KeyValue attribute of the IniFile object
  *
  * @param strSection  the section to set
  * @param key the key of the new value
  * @param value the value to set
  */
 public void setKeyValue(String strSection, String key, String value) {
     Map section = getSectionEL(strSection);
     if(section==null) {
         section=newMap();
         sections.put(strSection.toLowerCase(),section);
     }
     section.put(key.toLowerCase(), value);
 }

 /**
  * Gets the Sections attribute of the IniFile object
  *
  * @return   The Sections value
  */
 public Map getSections() {
   return sections;
 }

 /**
  * Gets the Section attribute of the IniFile object
  *
  * @param strSection  section name to get
  * @return         The Section value
 * @throws IOException
  */
 public Map getSection(String strSection) throws IOException {
     Object o=sections.get(strSection.toLowerCase());
     if(o==null) throw new IOException("section with name "+strSection+" does not exist");
     return (Map) o;
   }
 /**
  * Gets the Section attribute of the IniFile object, return null if section not exist
  *
  * @param strSection  section name to get
  * @return         The Section value
  */
 public Map getSectionEL(String strSection) {
     Object o=sections.get(strSection.toLowerCase());
     if(o==null) return null;
     return (Map) o;
   }

 /**
  * Gets the NullOrEmpty attribute of the IniFile object
  *
  * @param section  section to check
  * @param key      key to check
  * @return         is empty or not
  */
 public boolean isNullOrEmpty(String section, String key) {
   String value = getKeyValueEL(section, key);
   return (value == null || value.length() == 0);
 }

 /**
  * Gets the KeyValue attribute of the IniFile object
  *
  * @param strSection  section to get
  * @param key      key to get
  * @return         matching alue
 * @throws IOException
  */
 public String getKeyValue(String strSection, String key) throws IOException {
     Object o= getSection(strSection).get(key.toLowerCase());
     if(o==null) throw new IOException("key "+key+" doesn't exist in section "+strSection);
     return (String)o;

 }
 /**
  * Gets the KeyValue attribute of the IniFile object, if not exist return null
  *
  * @param strSection  section to get
  * @param key      key to get
  * @return         matching alue
  */
 public String getKeyValueEL(String strSection, String key) {
     Map map=getSectionEL(strSection);
     if(map==null) return null;
     Object o=map.get(key.toLowerCase());
     if(o==null) return null;
     return (String) o;

 }


 /**
  * loads the ini file
  * @param in  inputstream to read
 * @throws IOException
  */
 public void load(InputStream in) throws IOException {
   
     BufferedReader input = IOUtil.toBufferedReader(new InputStreamReader(in));
     String read;
     Map section = null;
     String sectionName;
     while ((read = input.readLine()) != null) {
       if (read.startsWith(";") || read.startsWith("#")) {
         continue;
       }
       else if (read.startsWith("[")) {
         // new section
         sectionName = read.substring(1, read.indexOf("]")).trim().toLowerCase();
         section = getSectionEL(sectionName);
         if (section == null) {
           section = newMap();
           sections.put(sectionName, section);
         }
       }
       else if (read.indexOf("=") != -1 && section != null) {
         // new key
         String key = read.substring(0, read.indexOf("=")).trim().toLowerCase();
         String value = read.substring(read.indexOf("=") + 1).trim();
         section.put(key, value);
       }
     }

 }

 /**
  * save back content to ini file
  * @throws IOException
  */
 public void save() throws IOException {
	 if(!file.exists())file.createFile(true);
     OutputStream out=IOUtil.toBufferedOutputStream(file.getOutputStream());
     Iterator it = sections.keySet().iterator();
     PrintWriter output = new PrintWriter(out);
     try {
     while(it.hasNext()) {
         String strSection = (String) it.next();
         output.println("[" + strSection + "]");
         Map section = getSectionEL(strSection);
         Iterator iit = section.keySet().iterator();
         while(iit.hasNext()) {
             String key = (String) iit.next();
             output.println(key + "=" + section.get(key));
        }
       }
     }
     finally {
    	 IOUtil.flushEL(output);
    	 IOUtil.closeEL(output);
    	 IOUtil.flushEL(out);
    	 IOUtil.closeEL(out);
     }
 }

 /**
  * removes a selection
  *
  * @param strSection  section to remove
  */
 public void removeSection(String strSection) {
     sections.remove(strSection);
 }
 
 /**
  * 
 * @param file
 * @return return a struct with all section an dkey list as value
 * @throws IOException
 */
public static Struct getProfileSections(Resource file) throws IOException {
     IniFile ini=new IniFile(file);
     Struct rtn=new StructImpl(Struct.TYPE_SYNC);
     Map sections = ini.getSections();
     Iterator it = sections.keySet().iterator();
     while(it.hasNext()) {
         String strSection=(String) it.next();
         Map section = ini.getSectionEL(strSection);
         Iterator iit = section.keySet().iterator();
         StringBuilder sb=new StringBuilder();
         while(iit.hasNext()) {
             if(sb.length()!=0)sb.append(',');
             sb.append(iit.next());
         }
         rtn.setEL(strSection,sb.toString());
     }
     return rtn;
 }
}