package org.bdware.sc.packer;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bdware.sc.util.FileUtil;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

public class JarPacker {
    public static final String zipFileName = "generatedlib/yjs.jar";
    private static final Logger LOGGER = LogManager.getLogger(JarPacker.class);
    //    public static boolean ignoreGson = false;
//    static ClassLoader cl = ClassLoader.getSystemClassLoader();
    ZipOutputStream outputZip;
    //    Set<String> already = new HashSet<>();
    private String base = "";

    public static void zipTest() {
        try {
            String zipPath = "./input/office/wps.jar";
            ZipFile zf = new ZipFile(zipPath);
            FileInputStream fin = new FileInputStream(zipPath);
            ZipInputStream zin = new ZipInputStream(fin);
            ZipEntry zine = zin.getNextEntry();
            byte[] cc = new byte[1000];

            assert zine != null;
            LOGGER.info("zin.read=" + zin.read(cc));
            ZipEntry ze = zf.getEntry(zine.getName());
            InputStream in = zf.getInputStream(ze);
            byte[] bb = new byte[1000];
            int count = in.read(bb);
            LOGGER.info("count=" + count);
            zin.close();
            zf.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void mergeZin(ZipInputStream zin, ZipOutputStream tempOut) throws IOException {
        ZipEntry ze;
        while (null != (ze = zin.getNextEntry())) {
            String entryName = ze.getName();
            byte[] b = new byte[1024 * 40];
            int count;
            if (entryName.contains("MANIFEST.MF")) {
                LOGGER.info("Ignore: " + entryName);
                continue;
            }

            try {
                tempOut.putNextEntry(new ZipEntry(ze.getName()));
                while ((count = zin.read(b)) > 0) {
                    tempOut.write(b, 0, count);
                }
                tempOut.closeEntry();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static byte[] input2byte(InputStream inStream) throws IOException {
        ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
        byte[] buff = new byte[100];
        int rc;
        while ((rc = inStream.read(buff, 0, 100)) > 0) {
            swapStream.write(buff, 0, rc);
        }
        return swapStream.toByteArray();
    }

    public static void pack(List<String> jarPaths) {
        new JarPacker().generateZip(jarPaths, zipFileName);
        LOGGER.info("finished!");
    }

    public static void main(String[] args) {
        List<String> paths = new ArrayList<>();
        paths.add("./bin");
        new JarPacker() {
            boolean inject = false;

            public byte[] transform(String inputFileName, byte[] arr) {
                if (!inject) {
                    inject = true;
                    addFile("META-INF/MANIFEST.MF", getMF());
                }
                return arr;
            }

            private byte[] getMF() {
                String sb = "Manifest-Version: 1.0\n" +
                        "Main-Class: com.yancloud.sc.SCAPI\n";
                return sb.getBytes();
            }
        }.generateZip(paths, "./generatedlib/yjs.jar");
        LOGGER.info("pack yjs.jar done!");
    }

    public void generateZip(List<String> dirs, String zipFileName) {
        int fileCount = 0;
        try {
            File dir = new File(FileUtil.getDir(zipFileName));
            if (!dir.exists()) {
                LOGGER.info("create directory " + dir.getAbsolutePath() + ": " + dir.mkdirs());
            }
            File zip = new File(zipFileName);
            if (!zip.exists()) {
                LOGGER.info("create file " + zip.getAbsolutePath() + ": " + zip.createNewFile());
            }
            FileOutputStream out = new FileOutputStream(zip);
            outputZip = new ZipOutputStream(out);
            for (String dirName : dirs) {
                File f = new File(dirName);
                File absolutef = f.getAbsoluteFile();
                base = absolutef.getAbsolutePath();
                fileCount += addToZip(absolutef);
            }
            LOGGER.info("fileCount=" + fileCount);
            outputZip.close();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private int addToZip(File f) throws IOException {
        String className;
        if (!f.isDirectory()) {
            String path = f.getAbsolutePath();
            if (accept(path)) {
                if (File.separatorChar == '/') { // MACOS
                    className = path.replaceAll(base, "").replaceAll("^/", "");
                } else { // Windows
                    className = path.substring(base.length() + 1);
                    className = className.replace("\\", "/");
                }
                addFile(className, input2byte(new FileInputStream(f)));
                return 1;
            }
            return 0;
        } else {
            int ret = 0;
            File[] files = f.listFiles();
            if (null != files) {
                for (File subFile : files) {
                    ret += addToZip(subFile);
                }
            }
            return ret;
        }
    }

    public boolean accept(String path) {
        return path.matches(".*class$");
    }

    public void addFile(String inputFileName, byte[] arr) {
        try {
            arr = transform(inputFileName, arr);
            outputZip.putNextEntry(new ZipEntry(inputFileName));
            outputZip.write(arr, 0, arr.length);
            // Close zip entry and file streams
            outputZip.closeEntry();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public byte[] transform(String inputFileName, byte[] arr) {
        return arr;
    }
}
