package net.argius.stew;

import java.io.*;
import java.nio.channels.*;
import java.util.*;
import java.util.regex.*;

/**
 * ConnectorConfiguration is a helper for ConnectorMap.
 */
public final class ConnectorConfiguration {

    static final String CONNECTOR_PROPERTIES_NAME = "connector.properties";

    private static final Pattern idPattern = Pattern.compile("^([^\\.]+)\\.name *=");

    /**
     * Loads configurations from a file.
     * @return
     * @throws IOException
     */
    public static ConnectorMap load() throws IOException {
        final File f = getPath();
        InputStream is = (f.exists())
                ? new FileInputStream(f)
                : new ByteArrayInputStream(new byte[0]);
        try {
            return load(is);
        } finally {
            is.close();
        }
    }

    /**
     * Loads configurations from a file.
     * @param is
     * @return
     * @throws IOException
     */
    public static ConnectorMap load(InputStream is) throws IOException {
        // cache for reuse
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        byte[] buffer = new byte[4096];
        for (int c; (c = is.read(buffer)) >= 0;) {
            bos.write(buffer, 0, c);
        }
        bos.flush();
        // create ID list
        List<String> idList = new ArrayList<String>();
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        BufferedReader reader = new BufferedReader(new InputStreamReader(bis));
        try {
            for (String line; (line = reader.readLine()) != null;) {
                Matcher matcher = idPattern.matcher(line);
                if (matcher.find()) {
                    idList.add(matcher.group(1));
                }
            }
        } finally {
            reader.close();
        }
        // read as Properties
        Properties props = new Properties();
        props.load(new ByteArrayInputStream(bos.toByteArray()));
        // creates a instance
        return new ConnectorMap(idList, props);
    }

    /**
     * Saves configurations to a file.
     * @param map
     * @throws IOException
     */
    public static void save(ConnectorMap map) throws IOException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        save(bos, map);
        byte[] bytes = bos.toByteArray();
        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
        FileOutputStream fos = new FileOutputStream(getPath());
        try {
            fos.getChannel().transferFrom(Channels.newChannel(bis), 0, bytes.length);
        } finally {
            fos.close();
        }
    }

    /**
     * Saves configurations to a file.
     * @param os
     * @param map
     * @throws IOException
     */
    public static void save(OutputStream os, ConnectorMap map) throws IOException {
        // import using store
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        map.toProperties().store(bos, "");
        // lines to elements
        List<String> lines = new ArrayList<String>();
        Scanner scanner = new Scanner(new ByteArrayInputStream(bos.toByteArray()));
        try {
            while (scanner.hasNextLine()) {
                String line = scanner.nextLine();
                if (!line.trim().startsWith("#")) {
                    lines.add(line);
                }
            }
        } finally {
            scanner.close();
        }
        // rewrites records sorted by ID 
        Comparator<String> c = new ConnectorPropertyComparator(new ArrayList<String>(map.keySet()));
        Collections.sort(lines, c);
        PrintWriter out = new PrintWriter(os);
        try {
            for (String line : lines) {
                out.println(line);
            }
            out.flush();
        } finally {
            out.close();
        }
    }

    public static long lastModified() {
        return getPath().lastModified();
    }

    private static File getPath() {
        return Bootstrap.getSystemFile(CONNECTOR_PROPERTIES_NAME);
    }

    private static final class ConnectorPropertyComparator implements
                                                          Comparator<String>,
                                                          Serializable {

        private final List<String> idList;

        ConnectorPropertyComparator(List<String> idList) {
            this.idList = idList;
        }

        @Override
        public int compare(String s1, String s2) {
            int index1 = getIdIndex(s1);
            int index2 = getIdIndex(s2);
            if (index1 == index2) {
                return s1.compareTo(s2);
            }
            return index1 - index2;
        }

        private int getIdIndex(String s) {
            String[] sa = s.split("\\.", 2);
            if (sa.length >= 2) {
                String id = sa[0];
                return idList.indexOf(id);
            }
            return -1;
        }

    }

}

@@ -589,24 +589,10 @@
     
-InputStream is =
+if
  (f.
@@ -600,21 +600,19 @@
 xists())
-%0A    
+ %7B%0A
         
@@ -619,18 +619,8 @@
     
-? new File
 Inpu
@@ -630,77 +630,49 @@
 ream
-(f)%0A                : new ByteArrayInputStream(new byte%5B0%5D);%0A
+ is = new FileInputStream(f);%0A   
         
 try 
@@ -659,38 +659,43 @@
 (f);%0A           
+ 
 try %7B%0A
+    
             retu
@@ -703,24 +703,28 @@
 n load(is);%0A
+    
         %7D fi
@@ -743,16 +743,20 @@
         
+    
 is.close
@@ -759,33 +759,82 @@
 lose();%0A        
-%7D
+    %7D%0A        %7D%0A        return new ConnectorMap();
 %0A    %7D%0A%0A    /**%0A
@@ -1257,32 +1257,73 @@
    bos.flush();%0A
+        byte%5B%5D data = bos.toByteArray();%0A
         // creat
@@ -1399,167 +1399,136 @@
     
-ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());%0A        BufferedReader reader = new BufferedReader(new InputStreamReader(bis));
+Scanner scanner = new Scanner(new ByteArrayInputStream(data));%0A        try %7B%0A            while (scanner.hasNextLine()) %7B
 %0A       
  try
@@ -1515,39 +1515,32 @@
 ine()) %7B%0A       
- try %7B%0A
             for 
@@ -1528,32 +1528,30 @@
             
-   for (
+final 
 String line;
@@ -1553,48 +1553,30 @@
 line
-; (line = reader.readLine()) != null;) %7B
+ = scanner.nextLine();
 %0A   
@@ -1783,20 +1783,21 @@
         
-read
+scann
 er.close
@@ -1929,33 +1929,20 @@
 tStream(
-bos.toByteArray()
+data
 ));%0A    
@@ -3448,17 +3448,16 @@
 ed by ID
- 
 %0A       

apply refactored ConnectorConfiguration.load