package org.jassetmanager;

import com.sun.istack.internal.NotNull;

import javax.servlet.ServletContext;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

public class AssetBundle {
    private byte[] content;
    private boolean built;
    private final ServletContext context;
    private final AssetBundleConfiguration config;
    private static final byte[] ASSET_SEPARATOR = new byte[] { '\r', '\n' };

    public AssetBundle(@NotNull AssetBundleConfiguration config,
                       @NotNull ServletContext context) {

        this.context = context;
        this.config = config;
        this.content = new byte[0];
        this.built = false;
    }

    public boolean isBuilt() {
        return this.built;
    }

    public byte[] getContent() {
        return this.content;
    }

    public void build(@NotNull List<String> allFilePaths) throws IOException {
        this.content = new byte[0];
        this.built = false;

        Map<Integer, List<String>> contentMap = createContentMap(allFilePaths);
        readAndAppendFilesFromContentMap(contentMap);
        
        this.built = true;
    }

    private void readAndAppendFilesFromContentMap(Map<Integer, List<String>> contentMap) throws IOException {
        List<Integer> keys = new ArrayList<Integer>(contentMap.keySet());
        Collections.sort(keys);

        for (Integer position : keys) {
            List<String> files = contentMap.get(position);
            readAndAppendFiles(files);
        }
    }

    private void readAndAppendFiles(List<String> files) throws IOException {
        for (String file : files) {
            readAndAppendFile(file);
        }
    }

    private Map<Integer, List<String>> createContentMap(List<String> allFilePaths) {
        Map<Integer, List<String>> contentMap = new HashMap<Integer, List<String>>();

        for (String filePath : allFilePaths) {
            int position = this.config.getContentPosition(filePath);
            if (position == -1) {
                continue;
            }

            if (!(contentMap.containsKey(position))) {
                contentMap.put(position, new ArrayList<String>());
            }

            contentMap.get(position).add(filePath);
        }

        return contentMap;
    }

    private void readAndAppendFile(String filePath) throws IOException {
        byte[] buffer = new byte[1024];
        InputStream is = null;

        try {
            is = this.context.getResourceAsStream(filePath);
            if (is == null) {
                throw new IOException("Could not open stream to asset '" + filePath + "'");
            }

            while (is.available() > 0) {
                int length = is.read(buffer);

                if (length != -1) {
                    appendFileContent(buffer, length);
                }
            }
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
            } catch (Exception e) {

            }
        }
    }

    private void appendFileContent(byte[] append, int appendLength) {
        int newContentLength = this.content.length + ASSET_SEPARATOR.length + appendLength;

        byte[] newContent = new byte[newContentLength];
        System.arraycopy(this.content, 0, newContent, 0, this.content.length);
        System.arraycopy(append, 0, newContent, this.content.length, appendLength);
        System.arraycopy(ASSET_SEPARATOR, 0, newContent, (this.content.length + appendLength), ASSET_SEPARATOR.length);

        this.content = newContent;
    }
}

@@ -99,16 +99,54 @@
 ontext;%0A
+import java.io.ByteArrayOutputStream;%0A
 import j
@@ -185,32 +185,65 @@
 io.InputStream;%0A
+import java.security.PrivateKey;%0A
 import java.util
@@ -1425,24 +1425,89 @@
 ort(keys);%0A%0A
+        ByteArrayOutputStream to = new ByteArrayOutputStream();%0A%0A
         for 
@@ -1629,16 +1629,20 @@
 es(files
+, to
 );%0A     
@@ -1637,32 +1637,74 @@
 , to);%0A        %7D
+%0A%0A        this.content = to.toByteArray();
 %0A    %7D%0A%0A    priv
@@ -1749,16 +1749,42 @@
 g%3E files
+, ByteArrayOutputStream to
 ) throws
@@ -1868,16 +1868,20 @@
 ile(file
+, to
 );%0A     
@@ -2539,16 +2539,42 @@
 filePath
+, ByteArrayOutputStream to
 ) throws
@@ -2888,84 +2888,52 @@
     
-while (is.available() %3E 0) %7B%0A                int length = is.read(buffer
+to.write(ResourceUtil.readInputStream(is)
 );%0A
-%0A
     
@@ -2944,118 +2944,34 @@
     
-    if (length != -1) %7B%0A                    appendFileContent(buffer, length);%0A                %7D%0A            %7D
+to.write(ASSET_SEPARATOR);
 %0A   
@@ -3159,552 +3159,7 @@
    %7D
-%0A%0A    private void appendFileContent(byte%5B%5D append, int appendLength) %7B%0A        int newContentLength = this.content.length + ASSET_SEPARATOR.length + appendLength;%0A%0A        byte%5B%5D newContent = new byte%5BnewContentLength%5D;%0A        System.arraycopy(this.content, 0, newContent, 0, this.content.length);%0A        System.arraycopy(append, 0, newContent, this.content.length, appendLength);%0A        System.arraycopy(ASSET_SEPARATOR, 0, newContent, (this.content.length + appendLength), ASSET_SEPARATOR.length);%0A%0A        this.content = newContent;%0A    %7D
 %0A%7D%0A

Use ByteArrayOutputStream for content building.