/*
 * SerialAdvancedView.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.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 advanced 
 * serial configuration settings for the Connect 
 * device.
 * 
 * This view can be embedded in other panels as desired.
 */


public class SerialAdvancedView extends ConfigViewImpl {
    /** A KvpNode that holds just the fields 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 tcpSocketIdContent;
    GridContentPanel tcpIdleTimeoutContent;
    GridContentPanel tcpConnectContent;

    GridContentPanel udpSocketIdContent;


    // Data Fields currently used in this view
    KvpField tcpSocketIdEnabledKvpField;
    KvpField tcpSocketIdKvpField;
    KvpField tcpIdleTimeoutKvpField;
    KvpField tcpDropDCDEnabledKvpField;
    KvpField tcpDropDSREnabledKvpField;

    KvpField udpSocketIdEnabledKvpField;
    KvpField udpSocketIdKvpField;

    // UI Controls for each field
    JCheckBox            tcpSocketIdEnabled_UIC;
    JTextField           tcpSocketId_UIC;
    JCheckBox            tcpIdleTimeoutEnabled_UIC;
    JTextField           tcpIdleTimeout_UIC;
    JCheckBox            tcpDropDCDEnabled_UIC;
    JCheckBox            tcpDropDSREnabled_UIC;

    JCheckBox    udpSocketIdEnabled_UIC;
    JTextField   udpSocketId_UIC;

    /**
     * Constructor. Sets up the view controls
     */
    public SerialAdvancedView() throws Exception {
        // create the KvpNode and the change listener
        viewSettingTree = new KvpNode();
        settingChangeListener = new DeviceChangeListener() {
            public void deviceChanged(DeviceChangeEvent e) {
                //SystemLog.debug("SerialAdvancedView was notified that device kvpGroup changed");
                refreshViewData();
                refreshViewControls();
            }
        };
        // Create the panel that will hold the view controls
        content = new GridContentPanel(this.getName(),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) {
                //SystemLog.debug("SerialAdvancedView was notified that field "+source.toString()+" changed.");
                if (source==tcpSocketIdEnabled_UIC) {
                    tcpSocketIdEnabledKvpField.setStringValue(RciProtocol.toString(tcpSocketIdEnabled_UIC.isSelected()));
                    tcpSocketIdContent.setContainerEnabled(tcpSocketIdEnabled_UIC.isSelected());
                } else if (source==tcpSocketId_UIC) {
                    tcpSocketIdKvpField.setStringValue(tcpSocketId_UIC.getText());
                } else if (source==tcpIdleTimeoutEnabled_UIC) {
                    tcpIdleTimeoutKvpField.setStringValue(tcpIdleTimeoutEnabled_UIC.isSelected() ? tcpIdleTimeout_UIC.getText() : "0");
                    tcpIdleTimeoutContent.setContainerEnabled(tcpIdleTimeoutEnabled_UIC.isSelected());
                } else if (source==tcpIdleTimeout_UIC) {
                    tcpIdleTimeoutKvpField.setStringValue(tcpIdleTimeout_UIC.getText());
                } else if (source==tcpDropDCDEnabled_UIC) {
                    tcpDropDCDEnabledKvpField.setStringValue(RciProtocol.toString(tcpDropDCDEnabled_UIC.isSelected()));
                } else if (source==tcpDropDSREnabled_UIC) {
                    tcpDropDSREnabledKvpField.setStringValue(RciProtocol.toString(tcpDropDSREnabled_UIC.isSelected()));
                } else if (source==udpSocketIdEnabled_UIC) {
                    udpSocketIdEnabledKvpField.setStringValue(RciProtocol.toString(udpSocketIdEnabled_UIC.isSelected()));
                    udpSocketIdContent.setContainerEnabled(udpSocketIdEnabled_UIC.isSelected());
                } else if (source==udpSocketId_UIC) {
                    udpSocketIdKvpField.setStringValue(udpSocketId_UIC.getText());
                }
            }
        };

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

        
        //-----------------------------------------------
        // TCP Settings
        //-----------------------------------------------
        content.addHeadingBar("SerialAdvancedTcpHeading");
        //content.addTextLine("SerialAdvancedDesc");

        tcpSocketIdEnabled_UIC = content.addJCheckBox("TcpSocketIdEnabled", fieldListener);
        tcpSocketIdContent = new GridContentPanel(1);
        tcpSocketId_UIC = tcpSocketIdContent.addJTextField("TcpSocketId", "TcpSocketIdDesc", 100, fieldListener);
        content.addSubPanel(tcpSocketIdContent);

        tcpIdleTimeoutEnabled_UIC = content.addJCheckBox("TcpIdleTimeoutEnabled", fieldListener);
        tcpIdleTimeoutContent = new GridContentPanel(1);
        tcpIdleTimeout_UIC = tcpIdleTimeoutContent.addJTextField("TcpIdleTimeout", "TcpIdleTimeoutDesc", 100, fieldListener);
        tcpIdleTimeout_UIC.setDocument(createIntegerDocument());
        content.addSubPanel(tcpIdleTimeoutContent);

        tcpDropDCDEnabled_UIC = content.addJCheckBox("TcpDropOnDCDEnabled", fieldListener);
        tcpDropDSREnabled_UIC = content.addJCheckBox("TcpDropOnDSREnabled", fieldListener);

        //-----------------------------------------------
        // UDP Settings
        //-----------------------------------------------
        content.addHeadingBar("SerialAdvancedUdpHeading");
        udpSocketIdEnabled_UIC = content.addJCheckBox("UdpSocketIdEnabled", fieldListener);
        udpSocketIdContent = new GridContentPanel(1);
        udpSocketId_UIC = udpSocketIdContent.addJTextField("UdpSocketId", "UdpSocketIdDesc", 100, fieldListener);
        content.addSubPanel(udpSocketIdContent);


        content.addVGlue();

    }

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

    /** 
     * 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);
        refreshViewData();
        refreshViewControls();
        device.addSettingChangeListener(settingChangeListener);
    }

    /**
     * Informs view to use the specified primary index in selecting the
     * data to display in the view. Not all views will support indexed
     * viewing and it is up to each view to define what the index is based 
     * upon. 
     * <p>
     * An example of an indexed view would be the SerialBasicView. This view
     * is indexed off the port # whose data is to be displayed in the view. An
     * external control in the SerialView lets the user select the port to view
     * and then that view instructs the SerialBasicView to use the selected 
     * port number as it's ViewIndex. Other views could use the index to base
     * their view off other integer keys such as Alarm#, Destination#,etc...
     * <p>
     * Views that do not support indexing can simply ignore this call.
     */
    public void setViewIndex(int primaryIndex) {
        super.setViewIndex(primaryIndex);
        // Rebind kvp fields for this port and then refresh controls
        bindFieldsForIndex(primaryIndex);
        refreshViewControls();
    }


    /** 
     * Refreshes the local copy of the kvpGroup displayed in the view. The updated kvpGroup come
     * from those currently cached in the Device
     */
    private void refreshViewData() {
        viewSettingTree.clear();
        int portCount=0;
        if (device!=null) {
            portCount = device.getInternalSettingTree().getChildCount("serial");
        }
        // refresh data for each port
        for (int port=1; port<=portCount; port++) {
            refreshViewDataForIndex(port);
        }
        bindFieldsForIndex(this.viewIndex);
    }

    /** 
     * Refreshes the local copy of the kvpGroup displayed in the view. The updated kvpGroup come
     * from those currently cached in the Device
     */
    private void refreshViewDataForIndex(int port) {
        viewSettingTree.mergeChild(device.getInternalSettingTree().getChild("tcp_serial", port), true);        
        viewSettingTree.mergeChild(device.getInternalSettingTree().getChild("udp_serial", port), true);        
    }


    /** 
     * Refreshes the local copy of the kvpGroup displayed in the view. The updated kvpGroup come
     * from those currently cached in the Device
     */
    private void bindFieldsForIndex(int port) {
        KvpNode tcpSerialSettings = viewSettingTree.getChild("tcp_serial", port);        
        tcpSocketIdEnabledKvpField = (tcpSerialSettings==null) ? null : tcpSerialSettings.getChild("socketid_state");        
        tcpSocketIdKvpField = (tcpSerialSettings==null) ? null : tcpSerialSettings.getChild("socketid_string");
        tcpIdleTimeoutKvpField = (tcpSerialSettings==null) ? null : tcpSerialSettings.getChild("idle_timeout");
        tcpDropDCDEnabledKvpField = (tcpSerialSettings==null) ? null : tcpSerialSettings.getChild("hangup_dcd");
        tcpDropDSREnabledKvpField = (tcpSerialSettings==null) ? null : tcpSerialSettings.getChild("hangup_dsr");

        KvpNode udpSerialSettings = viewSettingTree.getChild("udp_serial", port);        
        udpSocketIdEnabledKvpField = (udpSerialSettings==null) ? null : udpSerialSettings.getChild("socketid_state");
        udpSocketIdKvpField = (udpSerialSettings==null) ? null : udpSerialSettings.getChild("socketid_string");

    }



    /** 
     * Refreshes the state of the controls in the view to match the data in the views kvpGroup.
     */
    private void refreshViewControls() {
        // Set TCP controls
        tcpSocketId_UIC.setEnabled(tcpSocketIdKvpField!=null);
        if (tcpSocketIdKvpField!=null) {
            tcpSocketId_UIC.setText(tcpSocketIdKvpField.getStringValue());
        }
        tcpSocketIdEnabled_UIC.setEnabled(tcpSocketIdEnabledKvpField!=null);
        if (tcpSocketIdEnabledKvpField!=null) {
            tcpSocketIdEnabled_UIC.setSelected(RciProtocol.booleanValue(tcpSocketIdEnabledKvpField.getStringValue()));
            tcpSocketIdContent.setContainerEnabled(tcpSocketIdEnabled_UIC.isSelected());
        }

        tcpIdleTimeout_UIC.setEnabled(tcpIdleTimeoutKvpField!=null);
        tcpIdleTimeoutEnabled_UIC.setEnabled(tcpIdleTimeoutKvpField!=null);
        if (tcpIdleTimeoutKvpField!=null) {
            if (Integer.parseInt(tcpIdleTimeoutKvpField.getStringValue()) <= 0) {
                tcpIdleTimeoutEnabled_UIC.setSelected(false);
                tcpIdleTimeout_UIC.setText("");
            }
            else {
                tcpIdleTimeoutEnabled_UIC.setSelected(true);
                tcpIdleTimeout_UIC.setText(tcpIdleTimeoutKvpField.getStringValue());
            }

            tcpIdleTimeoutContent.setContainerEnabled(tcpIdleTimeoutEnabled_UIC.isSelected());
        }
        
        tcpDropDCDEnabled_UIC.setEnabled(tcpDropDCDEnabledKvpField!=null);
        if (tcpDropDCDEnabledKvpField!=null) {
            tcpDropDCDEnabled_UIC.setSelected(RciProtocol.booleanValue(tcpDropDCDEnabledKvpField.getStringValue()));
        }
        tcpDropDSREnabled_UIC.setEnabled(tcpDropDSREnabledKvpField!=null);
        if (tcpDropDSREnabledKvpField!=null) {
            tcpDropDSREnabled_UIC.setSelected(RciProtocol.booleanValue(tcpDropDSREnabledKvpField.getStringValue()));
        }

        // Set UDP controls
        udpSocketId_UIC.setEnabled(udpSocketIdKvpField!=null);
        if (udpSocketIdKvpField != null) {
            udpSocketId_UIC.setText(udpSocketIdKvpField.getStringValue());
        }
        udpSocketIdEnabled_UIC.setEnabled(udpSocketIdEnabledKvpField!=null);
        if (udpSocketIdEnabledKvpField != null) {
            udpSocketIdEnabled_UIC.setSelected(RciProtocol.booleanValue(udpSocketIdEnabledKvpField.getStringValue()));
            udpSocketIdContent.setContainerEnabled(udpSocketIdEnabled_UIC.isSelected());
        }


        // If no device data is not available, disable the entire panel
        if (tcpSocketIdKvpField==null) {
            content.setContainerEnabled(false);
        }

    }


    /**
     * 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() {
        SystemLog.debug("Activating "+getName());
    }

    /**
     * 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() {
        SystemLog.debug("Deactivating "+getName());
    }

    /**
     * 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) {
        try {
    
            // Validate TCP Settings
            if (tcpSocketIdEnabled_UIC.isSelected()) {
                if (tcpSocketId_UIC.getText().length() <= 0 ||
                    tcpSocketId_UIC.getText().length() > 40)
                    errorList.add(new ValidationError("SerialTcpSocketIdError"));
            }
            if (tcpIdleTimeoutEnabled_UIC.isSelected()) {
                if (!ValidationHelper.isValidUnsigned16(tcpIdleTimeout_UIC.getText()) ||
                    (Integer.parseInt(tcpIdleTimeout_UIC.getText()) <= 0))
                    errorList.add(new ValidationError("SerialTcpIdleTimeoutError"));
            }

            // Validate UDP Settings
            if (udpSocketIdEnabled_UIC.isSelected()) {
                if (udpSocketId_UIC.getText().length() <= 0 ||
                    udpSocketId_UIC.getText().length() > 40)
                    errorList.add(new ValidationError("SerialUdpSocketIdError"));
            }

        } catch (Exception e) {
            System.out.println("Caught Exception in SerialAdvancedView::validationChanges()");
            e.printStackTrace();
        }
    }

    /**
     * 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() {
        // simply 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() {
        refreshViewData();
        refreshViewControls();
    }


}

