package tehnut.assembler;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class ZipFile {

    String outputZip;
    Side side;
    Map<File, String> relativePath = new HashMap<>();

    /**
     *
     * @param side - The {@link Side} to build the zip for.
     */
    public ZipFile(Side side) {
        this.side = side;
        this.outputZip = Assembler.getZipName(side) + ".zip";
    }

    /**
     * Generates a list of all files to be added to the zip folder.
     *
     * @return - Returns itself for chaining if needed.
     */
    public ZipFile generateFileList() {
        generateFiles(new File(Assembler.getWorkingDirectory() + "/config"), side == Side.CLIENT ? "minecraft/config" : "config");
        generateFiles(new File(Assembler.getWorkingDirectory() + "/mods/" + side.toString()), side == Side.CLIENT ? "minecraft/mods" : "mods");
        generateFiles(new File(Assembler.getWorkingDirectory() + "/mods/" + Side.COMMON.toString()), side == Side.CLIENT ? "minecraft/mods" : "mods");
        generateFiles(new File(Assembler.getWorkingDirectory() + "/extra/" + side.toString()), "");
        generateFiles(new File(Assembler.getWorkingDirectory() + "/extra/" + Side.COMMON.toString()), "");

        return this;
    }

    /**
     * Zips up the files generated from {@code generateFileList()}.
     *
     * @return - Returns itself for chaining if needed.
     */
    public ZipFile zipIt() {

        try {
            byte[] buffer = new byte[1024];

            ZipOutputStream outputStream = new ZipOutputStream(new FileOutputStream(outputZip));
            for (File file : relativePath.keySet()) {

                if (file.isFile()) {
                    FileInputStream inputStream = new FileInputStream(file);
                    outputStream.putNextEntry(new ZipEntry(relativePath.get(file)));

                    int length;
                    while ((length = inputStream.read(buffer)) > 0)
                        outputStream.write(buffer, 0, length);

                    outputStream.closeEntry();
                    inputStream.close();
                } else {
                    addDirToArchive(outputStream, file, "");
                }
            }

            outputStream.close();

        } catch (Exception e) {
            e.printStackTrace();
        }

        Assembler.info("Zip file created: " + outputZip);

        return this;
    }

    /**
     * @param folder   - The folder to scan for files.
     * @param fileType - The type of folder. Generally {@code config}, {@code mods}, or blank for extras.
     */
    @SuppressWarnings("ConstantConditions")
    private void generateFiles(File folder, String fileType) {
        if (folder != null && folder.isDirectory()) {
            for (File file : folder.listFiles()) {
                String relPath = fileType.equals("") ? file.getName() : fileType + File.separator + file.getName();
                relativePath.put(file, relPath);
                Assembler.info("Added file: " + relPath);
            }
        }
    }

    /**
     * Recursively add all the files in a directory. Also handles sub-directories in a directory.
     *
     * @param outputStream - The ZipOutputStream to add our entries to
     * @param srcFile      - The file to add
     * @param parent       - The parent directory so the file structure is kept
     */
    @SuppressWarnings("ConstantConditions")
    private void addDirToArchive(ZipOutputStream outputStream, File srcFile, String parent) {
        for (File file : srcFile.listFiles()) {
            if (file.isDirectory()) {
                addDirToArchive(outputStream, file, parent.equals("") ? srcFile.getName() : parent + File.separator + srcFile.getName());
                continue;
            }

            try {
                byte[] buffer = new byte[1024];

                FileInputStream fis = new FileInputStream(file);
                String entry = parent.equals("") ? srcFile.getName() + File.separator + file.getName() : parent + File.separator + srcFile.getName() + File.separator + file.getName();
                outputStream.putNextEntry(new ZipEntry(entry));

                int length;

                while ((length = fis.read(buffer)) > 0)
                    outputStream.write(buffer, 0, length);

                outputStream.closeEntry();
                fis.close();

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

@@ -296,16 +296,29 @@
 utputZip
+, parent = %22%22
 ;%0A    Si
@@ -518,24 +518,92 @@
 ide = side;%0A
+        if (side == Side.CLIENT)%0A            parent = %22minecraft/%22;%0A
         this
@@ -927,50 +927,16 @@
 %22), 
-side == Side.CLIENT ? %22minecraft/config%22 :
+parent +
  %22co
@@ -1041,48 +1041,16 @@
 )), 
-side == Side.CLIENT ? %22minecraft/mods%22 :
+parent +
  %22mo
@@ -1160,48 +1160,16 @@
 )), 
-side == Side.CLIENT ? %22minecraft/mods%22 :
+parent +
  %22mo
@@ -1996,16 +1996,95 @@
 file)));
+%0A                    Assembler.info(%22Zipping file: %22 + relativePath.get(file));
 %0A%0A      
@@ -2409,16 +2409,126 @@
 m, file,
+ !file.getParentFile().getName().equalsIgnoreCase(side.toString()) ? parent + file.getParentFile().getName() :
  %22%22);%0A  
@@ -3228,30 +3228,19 @@
 eType + 
-File.separator
+%22/%22
  + file.
@@ -3334,18 +3334,19 @@
 nfo(%22Add
-ed
+ing
  file: %22
@@ -4027,38 +4027,27 @@
  : parent + 
-File.separator
+%22/%22
  + srcFile.g
@@ -4297,38 +4297,27 @@
 getName() + 
-File.separator
+%22/%22
  + file.getN
@@ -4333,30 +4333,19 @@
 arent + 
-File.separator
+%22/%22
  + srcFi
@@ -4363,22 +4363,11 @@
 ) + 
-File.separator
+%22/%22
  + f
@@ -4444,16 +4444,74 @@
 entry));
+%0A                Assembler.info(%22Zipping file: %22 + entry);
 %0A%0A      

Fix file structure in zip