/**
 * Copyright © CENRA (2016)
 *
 * remi.clement@espaces-naturels.fr
 *
 * This software is a multi-platform and portable application based on
 * the scientifical toolbox to monitor wetlands. With this toolbox it’s
 * possible to calculate indicators «RhoMéO» to monitor quality,
 * fonctionalities and urban and agricultural pressures of wetlands.
 * Indicators are calculated using naturalist data entered and/or imported,
 * and geographic data downloaded from a geographic server.
 *
 * This software is governed by the CeCILL license under French law and
 * abiding by the rules of distribution of free software.  You can  use,
 * modify and/ or redistribute the software under the terms of the CeCILL
 * license as circulated by CEA, CNRS and INRIA at the following URL
 * "http://www.cecill.info".
 *
 * As a counterpart to the access to the source code and  rights to copy,
 * modify and redistribute granted by the license, users are provided only
 * with a limited warranty  and the software's author,  the holder of the
 * economic rights,  and the successive licensors  have only  limited
 * liability.
 *
 * In this respect, the user's attention is drawn to the risks associated
 * with loading,  using,  modifying and/or developing or reproducing the
 * software by the user in light of its specific status of free software,
 * that may mean  that it is complicated to manipulate,  and  that  also
 * therefore means  that it is reserved for developers  and  experienced
 * professionals having in-depth computer knowledge. Users are therefore
 * encouraged to load and test the software's suitability as regards their
 * requirements in conditions enabling the security of their systems and/or
 * data to be ensured and,  more generally, to use and operate it in the
 * same conditions as regards security.
 *
 * The fact that you are presently reading this means that you have had
 * knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.core.preferences.net;

import fr.cenra.rhomeo.api.preferences.UserPackagePreferenceGroup;
import fr.cenra.rhomeo.core.RhomeoCore;
import fr.cenra.rhomeo.core.RhomeoRuntimeException;
import fr.cenra.rhomeo.core.util.EncryptionResult;
import fr.cenra.rhomeo.core.util.SecretGenerator;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import org.springframework.stereotype.Component;

/**
 *
 * @author Alexis Manin (Geomatys)
 */
@Component
public class NetPreferences extends UserPackagePreferenceGroup<NetKey> {

    public static final NetKey UPDATE_URL = new NetKey("update_url", "http://updates-bao.cenra-outils.org/updates/update.json");
    public static final NetKey UPDATE_USER = new NetKey("update_user", "rhomeo");
    public static final NetKey UPDATE_PASSWORD = new PassNetKey("update_pass", null);
    public static final NetKey WFS_URL = new NetKey("wfs_url", "http://constellation.cenra-outils.org/WS/wfs/referential");

    private static final List<NetKey> KEYS = Collections.unmodifiableList(Arrays.asList(new NetKey[] {
       UPDATE_URL, UPDATE_USER, UPDATE_PASSWORD, WFS_URL
    }));

    private static final String UPDATE_KEY_PREF = "net_update_pass_key";
    private static final String UPDATE_SR_PREF = "net_update_pass_sr";

    private final SecretGenerator gen;

    public NetPreferences() {
        gen = SecretGenerator.getInstance().orElse(null);
        // Default values for password and secret key.
        if (getPreference(UPDATE_PASSWORD) == null) {
            prefs.put(UPDATE_PASSWORD.getKey(), "4Iru5sD2uzixbWHhu7R72s658w/l3yCz");
            try {
                final byte[] buf = new byte[16];
                int read = 0;
                try (final InputStream stream = NetPreferences.class.getResourceAsStream("defaultKey")) {
                    do {
                        read += stream.read(buf, read, buf.length - read);
                    } while (read < buf.length -1);
                    prefs.putByteArray(UPDATE_KEY_PREF, buf);
                }
                read = 0;
                try (final InputStream stream = NetPreferences.class.getResourceAsStream("defaultSR")) {
                    do {
                        read += stream.read(buf, read, 8 - read);
                    } while (read < 7);
                    prefs.putByteArray(UPDATE_SR_PREF, Arrays.copyOf(buf, 8));
                }
            } catch (IOException ex) {
                RhomeoCore.LOGGER.log(Level.FINE, "cannot set default values", ex);
            }

        }
    }

    @Override
    public List<NetKey> getKeys() {
        return KEYS;
    }

    @Override
    public void setPreference(NetKey key, String value) {
        if (UPDATE_PASSWORD.equals(key) && value != null && !(value = value.trim()).isEmpty()) {
            if (gen != null) {
                try {
                    EncryptionResult encrypted = gen.encrypt(value);
                    prefs.putByteArray(UPDATE_KEY_PREF, encrypted.getKey());
                    prefs.putByteArray(UPDATE_SR_PREF, encrypted.getSecureRandom());
                    value = encrypted.getOutput();
                } catch (GeneralSecurityException ex) {
                    throw new RhomeoRuntimeException(ex);
                }
            } else {
                throw new RhomeoRuntimeException("no security manager available. Passwords cannot be written into preferences.");
            }
        }
        super.setPreference(key, value);
    }

    @Override
    public int getPriority() {
        return 1;
    }

    public URL getUpdateURL() throws MalformedURLException {
        return new URL(getPreference(UPDATE_URL));
    }

    public URLConnection openConnection(final URL toReach) throws GeneralSecurityException, IOException {
        final URL updateURL = getUpdateURL();
        String user = getPreference(UPDATE_USER);
        if (toReach.getHost().equals(updateURL.getHost()) && toReach.getUserInfo() == null && user != null && !(user = user.trim()).isEmpty()) {
            final String pass = getPreference(UPDATE_PASSWORD);
            URLConnection con = toReach.openConnection();
            String dpw = null;
            if (pass !=null && !pass.isEmpty()) {
                final byte[] key = prefs.getByteArray(UPDATE_KEY_PREF, null);
                final byte[] sr = prefs.getByteArray(UPDATE_SR_PREF, null);
                if (key == null && sr == null) { // No encryption parameter
                    dpw = pass;
                } else if (gen == null) { // No cipher
                    throw new RhomeoRuntimeException("no security manager available. Passwords cannot be read from preferences.");
                } else {
                    dpw = gen.decrypt(pass, key, sr);
                }
            }
            final StringBuilder authInfo = new StringBuilder(user);
            if (dpw != null) {
                authInfo.append(':').append(dpw);
            }
            final String b64 = Base64.getEncoder().withoutPadding().encodeToString(
                    authInfo.toString().getBytes()
            );
            con.addRequestProperty("Authorization", "Basic ".concat(b64));
            return con;
        } else {
            return toReach.openConnection();
        }
    }
}
