package pw.yumc.Residence.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.bukkit.Bukkit;
import org.bukkit.World;

import pw.yumc.Residence.ResidenceMain;

public class DataBackup {
    private final File BackupDir;

    ResidenceMain plugin;

    public DataBackup(final ResidenceMain plugin) {
        this.plugin = plugin;
        BackupDir = new File(plugin.getDataLocation(), "Backup");
    }

    public void backup() throws IOException {
        try {
            BackupDir.mkdir();
            final Date date = new Date();
            final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
            final File fileZip = new File(BackupDir, dateFormat.format(date) + ".zip");

            // Create the Source List, and add directories/etc to the file.
            final List<File> sources = new ArrayList<File>();

            final File saveFolder = new File(plugin.getDataLocation(), "Save");
            final File worldFolder = new File(saveFolder, "Worlds");
            if (!saveFolder.isDirectory()) {
                return;
            }
            File saveFile;
            for (final World world : Bukkit.getServer().getWorlds()) {
                saveFile = new File(worldFolder, "res_" + world.getName() + ".yml");
                if (saveFile.isFile()) {
                    sources.add(saveFile);
                }
            }
            packZip(fileZip, sources);
        } catch (final Exception e) {
            e.printStackTrace();
            return;
        }
    }

    private String buildPath(final String path, final String file) {
        if (path == null || path.isEmpty()) {
            return file;
        }

        return path + File.separator + file;
    }

    private void packZip(final File output, final List<File> sources) throws IOException {
        final ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(output));
        zipOut.setLevel(Deflater.DEFAULT_COMPRESSION);

        for (final File source : sources) {
            if (source.isDirectory()) {
                zipDir(zipOut, "", source);
            } else {
                zipFile(zipOut, "", source);
            }
        }

        zipOut.flush();
        zipOut.close();
    }

    private void zipDir(final ZipOutputStream zos, String path, final File dir) throws IOException {
        if (!dir.canRead()) {
            return;
        }

        final File[] files = dir.listFiles();
        path = buildPath(path, dir.getName());

        for (final File source : files) {
            if (source.isDirectory()) {
                zipDir(zos, path, source);
            } else {
                zipFile(zos, path, source);
            }
        }
    }

    private void zipFile(final ZipOutputStream zos, final String path, final File file) throws IOException {
        if (!file.canRead()) {
            return;
        }

        zos.putNextEntry(new ZipEntry(buildPath(path, file.getName())));

        final FileInputStream fis = new FileInputStream(file);
        final byte[] buffer = new byte[4092];
        int byteCount = 0;

        while ((byteCount = fis.read(buffer)) != -1) {
            zos.write(buffer, 0, byteCount);
        }

        fis.close();
        zos.closeEntry();
    }
}
