/*
 * SecurityView.java
 * 
 * Copyright (c) 2003-2004 Digi International
 * This program and the information contained in it is confidential and
 * proprietary to Digi International and may not be used, copied, or re-
 * produced without the prior written permission of Digi International.
 * 
 */

package com.digi.config.ui;

import com.digi.config.core.*;
import com.digi.config.util.*;

import javax.swing.*;
import javax.swing.text.*;
import java.text.*;
import javax.swing.event.*;
import javax.swing.tree.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.io.*;
import java.net.*;


/** 
 * This class is a view panel that shows serial realport
 * settings for the ConnectMe device
 * 
 * This view can be embedded in other panels as desired.
 */
public class SecurityView extends ConfigViewImpl {
    /** A KvpNode that holds just the settings used in this view */
    KvpNode viewSettingTree;
    /** listens to the device for changes to settings */
    DeviceChangeListener settingChangeListener;
    /** Panel that holds the view content */
    GridContentPanel content;
    GridContentPanel securityContent;

    // Data fields currently used in this view
    KvpField passwordModeKvpField;
    KvpField userNameKvpField;
    KvpNode passwordKvpField;

    // UI Controls for each filed
    JCheckBox  passwordMode_UIC;
    JTextField userName_UIC;
    JPasswordField password_UIC;
    JPasswordField verifyPassword_UIC;

    String newPassword = "";

    /**
     * Basic constructor. Sets up the tree view and the edit panel.
     */
    public SecurityView() throws Exception {
        super(true);
        // create the KvpNode and the change listener
        viewSettingTree = new KvpNode();
        settingChangeListener = new DeviceChangeListener() {
            public void deviceChanged(DeviceChangeEvent e) {
                refresh();
            }
        };
        // Create the panel that will hold the view controls
        content = new GridContentPanel(1);
        // create a listener that will detect when a user edits the field values
        ControlChangeAdapter fieldListener = new ControlChangeAdapter() {
            public void changePerformed(Object source, ActionEvent ae, FocusEvent fe) {
                if (source==passwordMode_UIC) {
                    boolean pwdEnabled = passwordMode_UIC.isSelected();
                    passwordModeKvpField.setStringValue(stringValuePasswordMode(pwdEnabled));
                    securityContent.setContainerEnabled(pwdEnabled);
                    // Keep addp in sync
                    if (!pwdEnabled) {
                        // Turned off password. Restore any old values and make sure we don't
                        // send those values to the device on a save.
                        refreshUserPassword();
                    }
                } else if (source==userName_UIC) {
                    userNameKvpField.setStringValue(userName_UIC.getText());
                } else if (source==password_UIC) {
                    passwordKvpField.setStringValue(String.valueOf(password_UIC.getPassword()));
                }
            }
        };

        //
        // Create the fields and put them on the view
        //

        passwordMode_UIC = content.addJCheckBox("SecurityPasswordMode", fieldListener);

        // Create the panel that will hold the security controls
        securityContent = new GridContentPanel(1);
        securityContent.addTextLine("SecurityEnabledDesc");

        userName_UIC = securityContent.addJTextField("SecurityUserNameLbl", null, 150, fieldListener);
        userName_UIC.setDocument(createGeneralDocument());
        password_UIC = securityContent.addJPasswordField("SecurityPasswordLbl", null, 150, fieldListener);
        password_UIC.setDocument(createGeneralDocument());
        verifyPassword_UIC = securityContent.addJPasswordField("SecurityVerifyPasswordLbl", null, 150, fieldListener);
        verifyPassword_UIC.setDocument(createGeneralDocument());

        content.addSubPanel(securityContent);

        content.addVGlue();
    }

    /**
     * Returns a one word name identifying this view. 
     */
    public String getName() {
        return "SecurityView";
    }

    /** 
     * Return the Component that displays the primary content for this view
     */
    public Component getViewContent() {
        return content;
    }



    /**
     * Informs view what device to work with. View should flush any
     * cached device data and mark current view content as invalid
     * so that it is refreshed appropriatly.
     */
    public void setDevice(Device device) {
        super.setDevice(device);
        refresh();
        device.addSettingChangeListener(settingChangeListener);
    }

    /** 
     * Refreshes the local copy of the kvpGroup displayed in the view. The updated kvpGroup come
     * from those currently cached in the Device
     */
    private void refresh() {
        viewSettingTree.clear();

        // Password mode
        passwordModeKvpField = viewSettingTree.mergeFrom(device.getInternalSettingTree(), "simple_password", "password_mode"); 
        passwordMode_UIC.setEnabled(passwordModeKvpField!=null);
        if (passwordModeKvpField!=null) {
            passwordMode_UIC.setSelected(booleanValuePasswordMode(passwordModeKvpField.getStringValue()));
        }

        refreshUserPassword();
    }

    /**
     * Refreshes only the user/password information.
     */
    private void refreshUserPassword() {
        userNameKvpField = viewSettingTree.mergeFrom(device.getInternalSettingTree(), "simple_password", "username");
        userName_UIC.setEnabled(userNameKvpField!=null);
        if (userNameKvpField!=null) {
            userName_UIC.setText(userNameKvpField.getStringValue());
        }

        // Password field isn't actually sent back from the device.
        KvpNode group = viewSettingTree.getGroup("simple_password");
        if (group != null) {
            // Always clear out the password on refresh.
            passwordKvpField = new KvpNode("password");
            group.addChild(passwordKvpField);
        }
        password_UIC.setEnabled(passwordKvpField!=null);
        verifyPassword_UIC.setEnabled(passwordKvpField!=null);
        if (passwordKvpField!=null) {
            password_UIC.setText("");
            verifyPassword_UIC.setText("");
        }

        securityContent.setContainerEnabled(passwordMode_UIC.isSelected());

        // If no data disable entire panel
        if (passwordModeKvpField==null) {
            content.setContainerEnabled(false);
        }
    }

    /**
     * Helper to convert RCI password mode strings to boolean.
     * @return true if password mode enabled (test_password or always_reject), otherwise false (always_accept).
     */
    boolean booleanValuePasswordMode(String passwordMode) {
        return !passwordMode.equalsIgnoreCase("always_accept");
    }

    /**
     * Helper to convert boolean value of the password mode to RCI strings.
     * @return "test_password" if passwordModeEnabled is true or "always_accept" if passwordModeEnabled is false.
     */
    String stringValuePasswordMode(boolean passwordModeEnabled) {
        if (passwordModeEnabled) {
            return "test_password";
        } else {
            return "always_accept";
        }
    }

    /**
     * This method is called to instruct the view that it is about to be made
     * active. When a view is active it needs to make sure its content is correct.
     * An example of why a panel may be inactive is if it were on a tabbed pane 
     * and was not currently visible or if the user selected some other view.
     */
    public void activate() {
    }

    /**
     * This method is called to instruct the view that it is about to be made
     * inactive. When a view is inactive, it does not need to worry
     * about maintaining its content in a correct state. An example of why a panel
     * may be inactive is if it were on a tabbed pane and was not currently visible
     * or if the user selected some other view.
     */
    public void deactivate() {
    }

    /**
     * Indicates if any changes have been made by the user that have not yet been
     * saved to the device(ie committed).
     */
    public boolean isChanged() {
        return viewSettingTree.hasChanged();
    }

    /**
     * This method instructs the view to validate any of the changes the user has made
     * within the view. Any errors are to be added to the provided errorList. 
     * 
     * @param errorList is a collection of ViewValidationError objects
     */
    public void validateChanges(Collection errorList) {
        // Nothing to Validate if Not Selected.
        if (!passwordMode_UIC.isSelected()) return;

        JTextComponent focusComp = null; // Set if error to set focus on a control.

        String password = String.valueOf(password_UIC.getPassword());
        String verifyPassword = String.valueOf(verifyPassword_UIC.getPassword());
        if (password.length() == 0 || password.length() > 15) {
            errorList.add(new ValidationError("SecurityPasswordLengthError"));
            focusComp = password_UIC;
        } else if (!password.equals(verifyPassword)) {
            errorList.add(new ValidationError("SecurityPasswordMismatchError"));
            focusComp = password_UIC;
        }

        // Trim leading/trailing spaces on user name
        String user = userName_UIC.getText();
        user = user.trim();
        userNameKvpField.setStringValue(user); // push back into field data
        if (user.length() == 0 || user.length() > 15) {
            errorList.add(new ValidationError("SecurityUserLengthError"));
            focusComp = userName_UIC;
        }

        if (focusComp != null) {
            focusComp.requestFocus();
            focusComp.selectAll();
        }

        // Save the new password for later in the commit. Unfortunately
        // the device object refreshes the cluster after the save, causing
        // our password to get reset.
        newPassword = password;
    }

    /**
     * This method instructs the view to place any changes the user has made within 
     * the view into the provided clusters. Once the changes have been saved to
     * to the device, a subsequent call to commitChanges() will be made.
     */
    public void getChanges(KvpNode settingCluster, KvpNode stateCluster) {
        // Merge the view changes into the provided cluster
        settingCluster.merge(viewSettingTree, false);
    }

    /**
     * Instructs the view to consider any user changes within the view as 
     * saved to the device (ie committed).
     */
    public void commitChanges() {
        boolean pwdEnabled = booleanValuePasswordMode(passwordModeKvpField.getStringValue());
        if (pwdEnabled) {
            // Set the new credentials into the authenticator to avoid popping up
            // a login prompt after changing the user/password.
            RciAuthenticator auth = RciAuthenticator.getInstance();
            auth.setNewCredentials(userNameKvpField.getStringValue(),
                                   newPassword);
        }

        // reset flags indicating user changes in the contols
        viewSettingTree.resetChanged();
    }

    /** 
     * Instructs the view to discard any changes the user has made within the
     * view and revert those fields to the original state. The view is also 
     * free to refresh all its fields to the present cached state of the device at 
     * this time.
     */
    public void cancelChanges() {
        refresh();
    }


}

