/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.sdkuilib.internal.repository;

import com.android.prefs.AndroidLocation;
import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.ISdkLog;

import org.eclipse.jface.dialogs.MessageDialog;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;

/**
 * Controller class to get settings values. Settings are kept in-memory.
 * Users of this class must first load the settings before changing them and save
 * them when modified.
 * <p/>
 * Settings are enumerated by constants in {@link ISettingsPage}.
 */
public class SettingsController {

    private static final String SETTINGS_FILENAME = "androidtool.cfg"; //$NON-NLS-1$

    private final Properties mProperties = new Properties();

    /** The currently associated {@link ISettingsPage}. Can be null. */
    private ISettingsPage mSettingsPage;

    private final UpdaterData mUpdaterData;

    public SettingsController(UpdaterData updaterData) {
        mUpdaterData = updaterData;
    }

    //--- Access to settings ------------

    /**
     * Returns the value of the {@link ISettingsPage#KEY_FORCE_HTTP} setting.
     *
     * @see ISettingsPage#KEY_FORCE_HTTP
     */
    public boolean getForceHttp() {
        return Boolean.parseBoolean(mProperties.getProperty(ISettingsPage.KEY_FORCE_HTTP));
    }

    /**
     * Returns the value of the {@link ISettingsPage#KEY_ASK_ADB_RESTART} setting.
     *
     * @see ISettingsPage#KEY_ASK_ADB_RESTART
     */
    public boolean getAskBeforeAdbRestart() {
        return Boolean.parseBoolean(mProperties.getProperty(ISettingsPage.KEY_ASK_ADB_RESTART));
    }

    /**
     * Returns the value of the {@link ISettingsPage#KEY_SHOW_UPDATE_ONLY} setting.
     *
     * @see ISettingsPage#KEY_SHOW_UPDATE_ONLY
     */
    public boolean getShowUpdateOnly() {
        String value = mProperties.getProperty(ISettingsPage.KEY_SHOW_UPDATE_ONLY);
        if (value == null) {
            return true;
        }
        return Boolean.parseBoolean(value);
    }

    /**
     * Sets the value of the {@link ISettingsPage#KEY_SHOW_UPDATE_ONLY} setting.
     *
     * @param enabled True if only compatible non-obsolete update items should be shown.
     * @see ISettingsPage#KEY_SHOW_UPDATE_ONLY
     */
    public void setShowUpdateOnly(boolean enabled) {
        setSetting(ISettingsPage.KEY_SHOW_UPDATE_ONLY, enabled);
    }

    /**
     * Returns the value of the {@link ISettingsPage#KEY_MONITOR_DENSITY} setting
     * @see ISettingsPage#KEY_MONITOR_DENSITY
     */
    public int getMonitorDensity() {
        String value = mProperties.getProperty(ISettingsPage.KEY_MONITOR_DENSITY, null);
        if (value == null) {
            return -1;
        }

        try {
            return Integer.parseInt(value);
        } catch (NumberFormatException e) {
            return -1;
        }
    }

    /**
     * Sets the value of the {@link ISettingsPage#KEY_MONITOR_DENSITY} setting.
     *
     * @param density the density of the monitor
     * @see ISettingsPage#KEY_MONITOR_DENSITY
     */
    public void setMonitorDensity(int density) {
        mProperties.setProperty(ISettingsPage.KEY_MONITOR_DENSITY, Integer.toString(density));
    }

    /**
     * Internal helper to set a boolean setting.
     */
    void setSetting(String key, boolean value) {
        mProperties.setProperty(key, Boolean.toString(value));
    }

    //--- Controller methods -------------

    /**
     * Associate the given {@link ISettingsPage} with this {@link SettingsController}.
     * <p/>
     * This loads the current properties into the setting page UI.
     * It then associates the SettingsChanged callback with this controller.
     * <p/>
     * If the setting page given is null, it will be unlinked from controller.
     *
     * @param settingsPage An {@link ISettingsPage} to associate with the controller.
     */
    public void setSettingsPage(ISettingsPage settingsPage) {
        mSettingsPage = settingsPage;

        if (settingsPage != null) {
            settingsPage.loadSettings(mProperties);

            settingsPage.setOnSettingsChanged(new ISettingsPage.SettingsChangedCallback() {
                public void onSettingsChanged(ISettingsPage page) {
                    SettingsController.this.onSettingsChanged();
                }
            });
        }
    }

    /**
     * Load settings from the settings file.
     */
    public void loadSettings() {
        FileInputStream fis = null;
        String path = null;
        try {
            String folder = AndroidLocation.getFolder();
            File f = new File(folder, SETTINGS_FILENAME);
            path = f.getPath();
            if (f.exists()) {
                fis = new FileInputStream(f);

                mProperties.load(fis);

                // Properly reformat some settings to enforce their default value when missing.
                setShowUpdateOnly(getShowUpdateOnly());
                setSetting(ISettingsPage.KEY_ASK_ADB_RESTART, getAskBeforeAdbRestart());
            }

        } catch (Exception e) {
            ISdkLog log = mUpdaterData.getSdkLog();
            if (log != null) {
                log.error(e, "Failed to load settings from .android folder. Path is '%1$s'.", path);
            }
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                }
            }
        }
    }

    /**
     * Saves settings to the settings file.
     */
    public void saveSettings() {

        FileOutputStream fos = null;
        String path = null;
        try {
            String folder = AndroidLocation.getFolder();
            File f = new File(folder, SETTINGS_FILENAME);
            path = f.getPath();

            fos = new FileOutputStream(f);

            mProperties.store( fos, "## Settings for Android Tool");  //$NON-NLS-1$

        } catch (Exception e) {
            ISdkLog log = mUpdaterData.getSdkLog();

            if (log != null) {
                log.error(e, "Failed to save settings at '%1$s'", path);
            }

            // This is important enough that we want to really nag the user about it
            String reason = null;

            if (e instanceof FileNotFoundException) {
                reason = "File not found";
            } else if (e instanceof AndroidLocationException) {
                reason = ".android folder not found, please define ANDROID_SDK_HOME";
            } else if (e.getMessage() != null) {
                reason = String.format("%1$s: %2$s", e.getClass().getSimpleName(), e.getMessage());
            } else {
                reason = e.getClass().getName();
            }

            MessageDialog.openInformation(mUpdaterData.getWindowShell(),
                    "SDK Manager Settings",
                    String.format(
                        "The Android SDK and AVD Manager failed to save its settings (%1$s) at %2$s",
                        reason, path));

        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                }
            }
        }
    }

    /**
     * When settings have changed: retrieve the new settings, apply them and save them.
     *
     * This updates Java system properties for the HTTP proxy.
     */
    private void onSettingsChanged() {
        if (mSettingsPage == null) {
            return;
        }

        String oldHttpsSetting = mProperties.getProperty(ISettingsPage.KEY_FORCE_HTTP,
                Boolean.FALSE.toString());

        mSettingsPage.retrieveSettings(mProperties);
        applySettings();
        saveSettings();

        String newHttpsSetting = mProperties.getProperty(ISettingsPage.KEY_FORCE_HTTP,
                Boolean.FALSE.toString());
        if (!newHttpsSetting.equals(oldHttpsSetting)) {
            // In case the HTTP/HTTPS setting changes, force sources to be reloaded
            // (this only refreshes sources that the user has already tried to open.)
            mUpdaterData.refreshSources(false /*forceFetching*/);
        }
    }

    /**
     * Applies the current settings.
     */
    public void applySettings() {
        Properties props = System.getProperties();

        // Get the configured HTTP proxy settings
        String proxyHost = mProperties.getProperty(ISettingsPage.KEY_HTTP_PROXY_HOST,
                ""); //$NON-NLS-1$
        String proxyPort = mProperties.getProperty(ISettingsPage.KEY_HTTP_PROXY_PORT,
                ""); //$NON-NLS-1$

        // Set both the HTTP and HTTPS proxy system properties.
        // The system property constants can be found in the Java SE documentation at
        // http://download.oracle.com/javase/6/docs/technotes/guides/net/proxies.html
        final String JAVA_PROP_HTTP_PROXY_HOST =  "http.proxyHost";      //$NON-NLS-1$
        final String JAVA_PROP_HTTP_PROXY_PORT =  "http.proxyPort";      //$NON-NLS-1$
        final String JAVA_PROP_HTTPS_PROXY_HOST = "https.proxyHost";     //$NON-NLS-1$
        final String JAVA_PROP_HTTPS_PROXY_PORT = "https.proxyPort";     //$NON-NLS-1$

        props.setProperty(JAVA_PROP_HTTP_PROXY_HOST,  proxyHost);
        props.setProperty(JAVA_PROP_HTTP_PROXY_PORT,  proxyPort);
        props.setProperty(JAVA_PROP_HTTPS_PROXY_HOST, proxyHost);
        props.setProperty(JAVA_PROP_HTTPS_PROXY_PORT, proxyPort);
     }

}
