/*-
 * ~~~~~~licensing~~~~~~
 * artifact-management
 * ==========
 * 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.base.support.osgi.bundle;

import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.Base64.Encoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;

import org.osgi.resource.Resource;

import ru.entaxy.platform.base.support.CommonUtils;

public class CapabilityDescriptorImpl implements CapabilityDescriptor {

    protected String namespace;

    protected Map<String, String> directives = new HashMap<>();
    protected Map<String, AttributeDescriptor> attributes = new HashMap<>();

    private static class AttributeDescriptor {

        public static final String MARKER_BASE64 = "@BASE64";

        private static final CharsetEncoder ENCODER = StandardCharsets.ISO_8859_1.newEncoder();

        private static final Encoder BASE64_ENCODER = Base64.getEncoder();

        private static final Decoder BASE64_DECODER = Base64.getDecoder();

        String type;
        Object value;

        public AttributeDescriptor(Object value) {
            this(value, CapabilityTypeHelper.getTypeName(value));
        }

        public AttributeDescriptor(Object value, String type) {
            this.type = type;
            this.value = value == null ? "" : value; // .toString();
        }

        public String getPackedStringValue(String data) {
            if (ENCODER.canEncode(data))
                return data;
            return BASE64_ENCODER.encodeToString(data.getBytes()).concat(MARKER_BASE64);
        }

        public String getUnpackedStringValue(String data) {
            if (data.endsWith(MARKER_BASE64))
                return new String(BASE64_DECODER.decode(data.substring(0, data.length() - MARKER_BASE64.length())));
            return data;
        }

        public Object getValue() {
            if (!CapabilityDescriptor.ATTRIBUTE_TYPES.STRING.equals(type))
                return value;
            return getUnpackedStringValue(value.toString());
        }

        public String getValueAsString() {
            if (value == null)
                return null;
            if (CapabilityDescriptor.ATTRIBUTE_TYPES.isList(type))
                return CapabilityTypeHelper.getListAsString((List<?>) value);
            if (CapabilityDescriptor.ATTRIBUTE_TYPES.STRING.equals(type))
                return "\"" + getPackedStringValue(value.toString()) + "\"";
            return value.toString();
        }

        public String getRawValueAsString() {
            if (value == null)
                return null;
            if (CapabilityDescriptor.ATTRIBUTE_TYPES.isList(type))
                return CapabilityTypeHelper.getListAsString((List<?>) value);
            if (CapabilityDescriptor.ATTRIBUTE_TYPES.STRING.equals(type))
                return "\"" + value.toString() + "\"";
            return value.toString();
        }

    }

    public CapabilityDescriptorImpl() {
        // TODO Auto-generated constructor stub
    }

    public CapabilityDescriptorImpl(String namespace) {
        this();
        namespace(namespace);
    }

    public String getAttributesAsString() {
        return this.attributes.entrySet().stream()
                .map(entry -> entry.getKey()
                        + /* (!ATTRIBUTE_TYPES.STRING.equals(entry.getValue().type)?( */":" + entry.getValue().type/*
                                                                                                                    * )
                                                                                                                    * :
                                                                                                                    * "")
                                                                                                                    */
                        + "=" + entry.getValue().getValueAsString())
                .collect(Collectors.joining(";"));
    }

    @Override
    public String getNamespace() {
        return this.namespace;
    }

    @Override
    public Map<String, String> getDirectives() {
        return this.directives;
    }

    @Override
    public Map<String, Object> getAttributes() {
        Map<String, Object> result = new HashMap<>();
        for (Map.Entry<String, AttributeDescriptor> entry : this.attributes.entrySet())
            result.put(entry.getKey(), entry.getValue().getValue());
        return result;
    }

    @Override
    public Map<String, Object> getRawAttributes() {
        Map<String, Object> result = new HashMap<>();
        for (Map.Entry<String, AttributeDescriptor> entry : this.attributes.entrySet())
            result.put(entry.getKey(), entry.getValue().value);
        return result;
    }

    @Override
    public Resource getResource() {
        // not implemented
        return null;
    }

    @Override
    public CapabilityDescriptor namespace(String namespace) {
        this.namespace = namespace;
        return this;
    }

    @Override
    public CapabilityDescriptor attribute(String name, Object value) {
        this.attributes.put(name, new AttributeDescriptor(value));
        return this;
    }

    @Override
    public CapabilityDescriptor attribute(String name, Object value, String type) {
        this.attributes.put(name, new AttributeDescriptor(value, type));
        return this;
    }

    public CapabilityDescriptor parseAttribute(String attributeData) {

        String nameType = attributeData.substring(0, attributeData.indexOf("="));
        if (!CommonUtils.isValid(nameType))
            return this;
        String[] nameTypeSplit = nameType.split(":");

        String name = nameTypeSplit[0].trim();

        String type = (nameTypeSplit.length > 1 ? nameTypeSplit[1] : CapabilityDescriptor.ATTRIBUTE_TYPES.STRING);
        if (!CommonUtils.isValid(type))
            type = CapabilityDescriptor.ATTRIBUTE_TYPES.STRING;
        type = type.trim();

        String value = attributeData.substring(attributeData.indexOf("=") + 1);
        if (!CommonUtils.isValid(value))
            value = "";
        value = value.trim();

        Object typedValue = CapabilityTypeHelper.getTypedValue(type, value);
        this.attribute(name, typedValue, type);

        return this;
    }


    @Override
    public CapabilityDescriptor attributes(Map<String, Object> attributes) {
        if (attributes == null)
            return this;
        for (Entry<String, Object> entry : attributes.entrySet()) {
            this.attribute(entry.getKey(), entry.getValue());
        }
        return this;
    }

}
