package org.editorconfig.configmanagement;

import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
import com.intellij.openapi.editor.impl.TrailingSpacesStripper;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.FileDocumentManagerAdapter;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vfs.VirtualFile;
import org.editorconfig.Utils;
import org.editorconfig.core.EditorConfig;
import org.editorconfig.plugincomponents.SettingsProviderComponent;
import org.jetbrains.annotations.NotNull;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class EditorSettingsManager extends FileDocumentManagerAdapter {
    // Handles the following EditorConfig settings:
    private static final String trimTrailingWhitespaceKey = "trim_trailing_whitespace";
    private static final String insertFinalNewlineKey = "insert_final_newline";
    private static final Map<String, String> trimMap;
    static {
        Map<String, String> map = new HashMap<String, String>();
        map.put("true", EditorSettingsExternalizable.STRIP_TRAILING_SPACES_WHOLE);
        map.put("false", EditorSettingsExternalizable.STRIP_TRAILING_SPACES_NONE);
        trimMap = Collections.unmodifiableMap(map);
    }
    private static final Map<String, Boolean> newlineMap;
    static {
        Map<String, Boolean> map = new HashMap<String, Boolean>();
        map.put("true", Boolean.TRUE);
        map.put("false", Boolean.FALSE);
        newlineMap = Collections.unmodifiableMap(map);
    }

    private static final Logger LOG = Logger.getInstance("#org.editorconfig.configmanagement.EditorSettingsManager");

    @Override
    public void beforeDocumentSaving(@NotNull Document document) {
        // This is fired when any document is saved, regardless of whether it is part of a save-all or
        // a save-one operation
        final VirtualFile file = FileDocumentManager.getInstance().getFile(document);
        applySettings(file);
    }

    private static void applySettings(VirtualFile file) {
        if (file == null || !file.isInLocalFileSystem()) return;
        // Get editorconfig settings
        final String filePath = file.getCanonicalPath();
        final SettingsProviderComponent settingsProvider = SettingsProviderComponent.getInstance();
        final List<EditorConfig.OutPair> outPairs = settingsProvider.getOutPairs(filePath);
        // Apply trailing spaces setting
        final String trimTrailingWhitespace = Utils.configValueForKey(outPairs, trimTrailingWhitespaceKey);
        applyConfigValueToUserData(file, TrailingSpacesStripper.OVERRIDE_STRIP_TRAILING_SPACES_KEY,
                                   trimTrailingWhitespaceKey, trimTrailingWhitespace, trimMap);
        // Apply final newline setting
        final String insertFinalNewline = Utils.configValueForKey(outPairs, insertFinalNewlineKey);
        applyConfigValueToUserData(file, TrailingSpacesStripper.OVERRIDE_ENSURE_NEWLINE_KEY,
                                   insertFinalNewlineKey, insertFinalNewline, newlineMap);
    }

    private static <T> void applyConfigValueToUserData(VirtualFile file, Key<T> userDataKey, String editorConfigKey,
                                                       String configValue, Map<String, T> configMap) {
        if (configValue.isEmpty()) {
            file.putUserData(userDataKey, null);
        } else {
            final T data = configMap.get(configValue);
            if (data == null) {
                LOG.warn(Utils.invalidConfigMessage(configValue, editorConfigKey, file.getCanonicalPath()));
            } else {
                file.putUserData(userDataKey, data);
                LOG.debug("Applied " + editorConfigKey + " settings for: " + file.getCanonicalPath());
            }
        }
    }
}