/*-
 * ~~~~~~licensing~~~~~~
 * application-impl
 * ==========
 * Copyright (C) 2020 - 2025 EmDev LLC
 * ==========
 * You may not use this file except in accordance with the License Terms of the Copyright
 * Holder located at: https://entaxy.ru/eula . All copyrights, all intellectual property
 * rights to the Software and any copies are the property of the Copyright Holder. Unless
 * it is explicitly allowed the Copyright Holder, the User is prohibited from using the
 * Software for commercial purposes to provide services to third parties.
 * 
 * The Copyright Holder hereby declares that the Software is provided on an "AS IS".
 * Under no circumstances does the Copyright Holder guarantee or promise that the
 * Software provided by him will be suitable or not suitable for the specific purposes
 * of the User, that the Software will meet all commercial and personal subjective
 * expectations of the User, that the Software will work properly, without technical
 * errors, quickly and uninterruptedly.
 * 
 * Under no circumstances shall the Copyright Holder or its Affiliates is not liable
 * to the User for any direct or indirect losses of the User, his expenses or actual
 * damage, including, downtime; loss of bussines; lost profit; lost earnings; loss
 * or damage to data, property, etc.
 * ~~~~~~/licensing~~~~~~
 */
package ru.entaxy.platform.integration.applications.impl.jar;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.jar.Attributes;
import java.util.jar.Attributes.Name;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;

public class JarBuilder {

    public enum Status {
        NEW,
        OPENED,
        FINALIZED
    }

    public static interface OsgiAttributes {
        Name NAME = new Name("Bundle-Name");
        Name SYMBOLIC_NAME = new Name("Bundle-SymbolicName");
        Name DESCRIPTION = new Name("Bundle-Description");
        Name DOC_URL = new Name("Bundle-DocURL");
        Name MANIFEST_VERSION = new Name("Bundle-ManifestVersion");
        Name VENDOR = new Name("Bundle-Vendor");
        Name VERSION = new Name("Bundle-Version");
    }


    protected Manifest manifest;

    protected Status status = Status.NEW;

    protected File targetFile;

    protected Map<Name, Object> osgiInfo = new HashMap<>();

    protected JarOutputStream jarOutputStream;

    public class OsgiInfo {

        public OsgiInfo() {
            String fileName = targetFile.getName();
            if (fileName.lastIndexOf('.') > 0)
                fileName = fileName.substring(0, fileName.lastIndexOf('.'));
            osgiInfo.put(OsgiAttributes.NAME, fileName);
            osgiInfo.put(OsgiAttributes.SYMBOLIC_NAME, fileName);
            osgiInfo.put(OsgiAttributes.DESCRIPTION, fileName);
            osgiInfo.put(OsgiAttributes.DOC_URL, "https://entaxy.ru/");
            osgiInfo.put(OsgiAttributes.VENDOR, "Entaxy");
            osgiInfo.put(OsgiAttributes.VERSION, "1.0.0");
        }

        public OsgiInfo name(String name) {
            osgiInfo.put(OsgiAttributes.NAME, name);
            return this;
        }

        public OsgiInfo symbolicName(String name) {
            osgiInfo.put(OsgiAttributes.SYMBOLIC_NAME, name);
            return this;
        }

        public OsgiInfo description(String desc) {
            osgiInfo.put(OsgiAttributes.DESCRIPTION, desc);
            return this;
        }

        public OsgiInfo docUrl(String url) {
            osgiInfo.put(OsgiAttributes.DOC_URL, url);
            return this;
        }

        public OsgiInfo vendor(String vendor) {
            osgiInfo.put(OsgiAttributes.VENDOR, vendor);
            return this;
        }

        public OsgiInfo version(String version) {
            osgiInfo.put(OsgiAttributes.VERSION, version);
            return this;
        }

        public void update() {
            updateOsgiInfo();
        }

    }

    public JarBuilder(File targetFile) {
        this.targetFile = targetFile;
        this.targetFile.getAbsoluteFile().getParentFile().mkdirs();
        this.manifest = new Manifest();
        initializeManifest();
        this.osgiInfo.put(OsgiAttributes.MANIFEST_VERSION, "2");
    }

    public boolean isReady() {
        return Status.FINALIZED.equals(status);
    }

    protected void initializeManifest() {
        this.manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
    }

    public void updateOsgiInfo() {
        if (Status.NEW.equals(status))
            for (Entry<Name, Object> entry : osgiInfo.entrySet())
                this.manifest.getMainAttributes().put(entry.getKey(), entry.getValue());
    }

    public OsgiInfo getOsgiInfo() {
        return new OsgiInfo();
    }

    public void putAttribute(String name, String value) {
        if (status.equals(Status.NEW))
            this.manifest.getMainAttributes().putValue(name, value);
    }

    public void open() throws FileNotFoundException, IOException {
        status = Status.OPENED;
        jarOutputStream = new JarOutputStream(new FileOutputStream(targetFile), this.manifest);
    }

    public void addFile(File source, String targetLocation) throws IllegalStateException, IOException {
        if (!Status.OPENED.equals(status))
            throw new IllegalStateException("JarBuilder is not in OPENED status");
        JarEntry entry = new JarEntry(targetLocation);
        entry.setTime(source.lastModified());
        jarOutputStream.putNextEntry(entry);

        try (
                BufferedInputStream in = new BufferedInputStream(new FileInputStream(source))) {
            byte[] buffer = new byte[1024];
            while (true) {
                int count = in.read(buffer);
                if (count == -1) {
                    break;
                }
                jarOutputStream.write(buffer, 0, count);
            }
        } finally {
            jarOutputStream.closeEntry();
        }
    }

    public File close() throws IOException {
        jarOutputStream.close();
        status = Status.FINALIZED;
        return targetFile;
    }

}
