// The MIT License (MIT)
//
// Copyright (c) 2017 Tim Jones
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package io.github.jonestimd.finance.config;

import java.awt.Window;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.Optional;
import java.util.Properties;

import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import com.typesafe.config.ConfigRenderOptions;
import io.github.jonestimd.finance.plugin.DriverConfigurationService;
import io.github.jonestimd.finance.plugin.DriverConfigurationService.DriverService;
import io.github.jonestimd.finance.swing.database.ConnectionDialog;

public class ConnectionConfig {
    public static final String CONNECTION_FILE_PROPERTY = "connection";
    public static final String DEFAULT_CONFIG_FILE = "~/.finances/connection.conf";
    public static final String CONNECTION_PATH = "connection.default";

    private final File configFile;
    private Config config;

    public ConnectionConfig() {
        this(CONNECTION_FILE_PROPERTY, DEFAULT_CONFIG_FILE);
    }

    protected ConnectionConfig(String fileProperty, String defaultFile) {
        String fileName = System.getProperty(fileProperty, defaultFile);
        if (fileName.startsWith("~/")) fileName = System.getProperty("user.home") + fileName.substring(1);
        this.configFile = new File(fileName);
        if (configFile.exists()) config = ConfigFactory.parseFile(configFile);
        else config = ConfigFactory.empty();
    }

    public Config root() {
        return config;
    }

    public Optional<Config> get(String path) {
        return config.hasPath(path) ? Optional.of(config.getConfig(path)) : Optional.empty();
    }

    public ConnectionConfig addPath(String path, Config config) {
        this.config = this.config.withValue(path, config.root());
        return this;
    }

    public void save(boolean isPrivate) {
        if (!configFile.getParentFile().exists()) configFile.getParentFile().mkdirs();
        ConfigRenderOptions renderOptions = ConfigRenderOptions.concise().setFormatted(true).setJson(false);
        try (FileWriter stream = new FileWriter(configFile)) {
            stream.write(config.root().render(renderOptions));
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
        if (configFile.exists() && isPrivate) {
            try {
                Files.setPosixFilePermissions(configFile.toPath(), PosixFilePermissions.fromString("rw-------"));
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
        }
    }

    public DriverService loadDriver() {
        return loadDriver(null).get();
    }

    public Optional<DriverService> loadDriver(Window initialFrame) {
        Optional<Config> configOption = get(ConnectionConfig.CONNECTION_PATH);
        if (! configOption.isPresent() && initialFrame != null) {
            configOption = new ConnectionDialog(initialFrame).showDialog();
            configOption.ifPresent(config -> addPath(ConnectionConfig.CONNECTION_PATH, config).save(true));
        }
        return configOption.map(DriverConfigurationService::forConfig);
    }

    public static Properties asProperties(Config config, String path) {
        final String prefix = path + ".";
        final Properties properties = new Properties();
        if (config.hasPath(path)) {
            config.getConfig(path).entrySet().forEach(entry -> {
                properties.setProperty(prefix + entry.getKey(), entry.getValue().unwrapped().toString());
            });
        }
        return properties;
    }
}

@@ -1326,65 +1326,46 @@
 ile.
+P
 at
-tribute.PosixFilePermissions;%0Aimport java.util.Optional
+h;%0Aimport java.nio.file.attribute.*
 ;%0Aim
@@ -1379,26 +1379,17 @@
 va.util.
-Properties
+*
 ;%0A%0Aimpor
@@ -3443,32 +3443,83 @@
 && isPrivate) %7B%0A
+            Path configPath = configFile.toPath();%0A
             try 
@@ -3572,29 +3572,20 @@
 s(config
-File.to
 Path
-()
 , PosixF
@@ -3650,97 +3650,1176 @@
 ch (
-IOException ex) %7B%0A                throw new RuntimeException(ex);%0A            %7D%0A        %7D
+UnsupportedOperationException ex) %7B%0A                setWindowsPermissions(configPath);%0A            %7D catch (IOException ex) %7B%0A                throw new RuntimeException(ex);%0A            %7D%0A        %7D%0A    %7D%0A%0A    private void setWindowsPermissions(Path path) %7B%0A        try %7B%0A            AclFileAttributeView aclAttr = Files.getFileAttributeView(path, AclFileAttributeView.class);%0A            UserPrincipalLookupService lookupService = path.getFileSystem().getUserPrincipalLookupService();%0A            aclAttr.setAcl(Arrays.asList(%0A                    getAclEntry(lookupService.lookupPrincipalByName(%22SYSTEM%22), Collections.emptySet()),%0A                    getAclEntry(lookupService.lookupPrincipalByName(System.getProperty(%22user.name%22)), EnumSet.allOf(AclEntryPermission.class))%0A            ));%0A        %7D catch (IOException ex) %7B%0A            throw new RuntimeException(ex);%0A        %7D%0A    %7D%0A%0A    private AclEntry getAclEntry(UserPrincipal user, Set%3CAclEntryPermission%3E permissions) throws IOException %7B%0A        return AclEntry.newBuilder().setPermissions(permissions)%0A                .setPrincipal(user)%0A                .setType(AclEntryType.ALLOW)%0A                .build();
 %0A   

Set file permissions on Windows