/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
 */
package com.scytl.products.ov.commons.sign;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;

import javax.json.Json;
import javax.json.JsonArrayBuilder;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;

import com.scytl.products.ov.commons.sign.beans.SignatureFieldsConstants;

/**
 * The Class SignatureMetadata.
 */
public class SignatureMetadata {

    /** The Constant FIELD. */
    private static final String FIELD = "field";

    /** The Constant VALUE. */
    private static final String VALUE = "value";

    /** The version. */
    private final String version;

    /** The signature. */
    private final String signature;

    /** The signed fields. */
    private final Map<String, String> signedFields;

    /**
     * Instantiates a new signature metadata.
     *
     * @param version
     *            the version
     * @param signedFields
     *            the signed fields
     * @param signature
     *            the signature
     */
    private SignatureMetadata(final String version,
            final Map<String, String> signedFields,
            final String signature) {
        this.version = version;
        this.signedFields = signedFields;
        this.signature = signature;
    }

    /**
     * To json object.
     *
     * @return the json object
     */
    public JsonObject toJsonObject() {
        JsonObjectBuilder mainObjectBuilder = Json.createObjectBuilder();
        mainObjectBuilder.add(SignatureFieldsConstants.SIG_FIELD_VERSION, version);

        final JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
        signedFields.forEach((k, v) -> arrayBuilder
            .add(Json.createObjectBuilder().add(FIELD, k).add(VALUE, v)));
        mainObjectBuilder.add(SignatureFieldsConstants.SIG_FIELD_SIGNED, arrayBuilder);
        mainObjectBuilder.add(SignatureFieldsConstants.SIG_FIELD_SIGNATURE, signature);

        return mainObjectBuilder.build();
    }

    /**
     * From json object.
     *
     * @param jsonObject
     *            the json object
     * @return the signature metadata
     */
    public static SignatureMetadata fromJsonObject(
            final JsonObject jsonObject) {

        final String version =
            jsonObject.getString(SignatureFieldsConstants.SIG_FIELD_VERSION);
        final String signature =
            jsonObject.getString(SignatureFieldsConstants.SIG_FIELD_SIGNATURE);

        final Map<String, String> signed = new LinkedHashMap<>();
        jsonObject.getJsonArray(SignatureFieldsConstants.SIG_FIELD_SIGNED)
            .getValuesAs(JsonObject.class).forEach(obj -> signed
                .put(obj.getString(FIELD), obj.getString(VALUE)));

        return new SignatureMetadata(version, signed, signature);
    }

    /**
     * Gets the version.
     *
     * @return the version
     */
    public String getVersion() {
        return version;
    }

    /**
     * Gets the signature.
     *
     * @return the signature
     */
    public String getSignature() {
        return signature;
    }

    /**
     * Gets the signed fields.
     *
     * @return the signed fields
     */
    public Map<String, String> getSignedFields() {
        return Collections.unmodifiableMap(signedFields);
    }

    /**
     * Builder.
     *
     * @return the signature metadata builder
     */
    public static SignatureMetadataBuilder builder() {
        return new SignatureMetadataBuilder();
    }

    /**
     * The Class SignatureMetadataBuilder.
     */
    public static class SignatureMetadataBuilder {

        /** The version. */
        private final String version;

        /** The signature. */
        private String signature;

        /** The signed fields. */
        private final Map<String, String> signedFields;

        /**
         * Instantiates a new signature metadata builder.
         */
        public SignatureMetadataBuilder() {
            version = "1.0";
            signedFields = new LinkedHashMap<>();
        }

        /**
         * With signature.
         *
         * @param signature
         *            the signature
         * @return the signature metadata builder
         */
        public SignatureMetadataBuilder withSignature(
                final String signature) {
            this.signature = signature;
            return this;
        }

        /**
         * Adds the signed field.
         *
         * @param name
         *            the name
         * @param value
         *            the value
         * @return the signature metadata builder
         */
        public SignatureMetadataBuilder addSignedField(final String name,
                final String value) {
            Objects.requireNonNull(name, "field name cannot be null");
            Objects.requireNonNull(value, "field value cannot be null");
            signedFields.put(name, value);
            return this;
        }

        /**
         * Builds the.
         *
         * @return the signature metadata
         */
        public SignatureMetadata build() {
            Objects.requireNonNull(signature,
                "field 'signature' cannot be null");
            return new SignatureMetadata(version, signedFields, signature);
        }

    }
}
