/*
 * GpioStateView.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 the state of the GPIO lines
 * and potentially allows the user to set them.
 * 
 * This view can be embedded in other panels as desired.
 */


public class GpioStateView extends ConfigViewImpl {
    /** A KvpNode that holds just the state fields used in this view */
    KvpNode viewStateTree;
    /** listens to the device for changes to state fields */
    DeviceChangeListener stateChangeListener;
    /** listens for changes in the UI controls */
    ControlChangeAdapter fieldListener;
    /** Panel that holds the view content */
    GridContentPanel content;

    // Data Fields currently used in this view
    KvpField pinKvpField[];

    // UI Controls for each field
    JRadioButton pinAsserted_UIC[];
    JRadioButton pinUnasserted_UIC[];
    
    JButton setGpioPinsBtn;

    ConfigAction setGpioPinsAction;

    /**
     * Basic constructor. Sets up the tree view and the edit panel.
     */
    public GpioStateView() throws Exception {
        super(false);
        setIncludeStateFields(true);
        setIncludeSettingsFields(false);

        // Create the panel that will hold the view controls
        content = new GridContentPanel(this.getName(),1);
        // create the KvpNode and the change listener
        viewStateTree = new KvpNode();
        stateChangeListener = new DeviceChangeListener() {
            public void deviceChanged(DeviceChangeEvent e) {
                //SystemLog.debug("GpioStateView was notified that device kvpGroup changed");
                refreshViewData();
                refreshViewControls();
            }
        };
        // create a listener that will detect when a user edits the field values
        fieldListener = new ControlChangeAdapter() {
            public void changePerformed(Object source, ActionEvent ae, FocusEvent fe) {
                //SystemLog.debug("GpioStateView was notified that field "+source.toString()+" changed.");
                for (int i=0;i<pinAsserted_UIC.length; i++) {
                    if (source==pinAsserted_UIC[i] || source== pinUnasserted_UIC[i]) {
                        pinKvpField[i].setStringValue(RciProtocol.assertedString(pinAsserted_UIC[i].isSelected()));
                    }
                }
                setGpioPinsBtn.setEnabled(isChanged());
            }
        };

        // Set button's apply action for gpio pins (just resuse the save action from our parent).
        setGpioPinsAction = new ConfigAction("SetGpioPins", this, "doSaveChangesAction");

        createViewContent();
    }

    /**
     * Builds the controls on the view. This method builds the view content based
     * upon the capabilities of device we are connected to. If we are not connected to a
     * device, then an empty page is created. This method should be called whenever
     * changing the device this view is connected to because the view content varies
     * per device.
     */
    private void createViewContent()  {
        content.removeAll();

        if (device==null) {
            return;
        }

        System.out.println("GpioStateView.createViewContent() called. gpio count = "+device.getDeviceModelInfo().getGpioCount());

        // Allocate dynamically sized setting and control arrays
        pinKvpField = new KvpNode[device.getDeviceModelInfo().getGpioCount()];
        pinAsserted_UIC = new JRadioButton[device.getDeviceModelInfo().getGpioCount()];
        pinUnasserted_UIC = new JRadioButton[device.getDeviceModelInfo().getGpioCount()];

        // Create the fields and put them on the view
        content.addTextLine("GpioStateViewDesc");

        GridContentPanel pinContent = new GridContentPanel(3);
        pinContent.addColumnHeading("", fieldListener, 0, false);
        pinContent.addColumnHeading("PinAssertedHeading", fieldListener, 1, false);
        pinContent.addColumnHeading("PinUnassertedHeading", fieldListener, 2, true);

        ButtonGroup pinGroup;
        for (int i=0; i<device.getDeviceModelInfo().getGpioCount(); i++) {
            pinGroup = new ButtonGroup();
            pinContent.addColumnHeading("GpioPin"+(i+1), fieldListener, 0, false);
            pinAsserted_UIC[i] = pinContent.addJRadioButton(null, fieldListener, pinGroup, 1, false);
            pinUnasserted_UIC[i] = pinContent.addJRadioButton(null, fieldListener, pinGroup, 2, true);
        }
        
        content.addSubPanel(pinContent);

        setGpioPinsBtn = new JButton(setGpioPinsAction);
        setGpioPinsBtn.setEnabled(false);
        content.addField(null, setGpioPinsBtn, null);

        content.addVGlue();
    }

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

    /** 
     * 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);
        createViewContent();
        refreshViewData();
        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() {
        viewStateTree.clear();
        if (device!=null) {
            for (int i=0;i<device.getDeviceModelInfo().getGpioCount(); i++) {
                pinKvpField[i] = viewStateTree.mergeFrom(device.getInternalStateTree(), "gpio", "pin"+(i+1)); 
            }
        }
    }

    /** 
     * Refreshes the state of the controls in the view to match the data in the views kvpGroup.
     */
    private void refreshViewControls() {
        if (device!=null) {
            for (int i=0;i<device.getDeviceModelInfo().getGpioCount(); i++) {
                setPinState(pinKvpField[i], pinAsserted_UIC[i], RciProtocol.ASSERTED);
                setPinState(pinKvpField[i], pinUnasserted_UIC[i], RciProtocol.UNASSERTED);
            }
        }
        setGpioPinsBtn.setEnabled(isChanged());
    }

    // Helper
    private void setPinState(KvpField field, JRadioButton pinButton, String state) {
        // If we don't have data for the field disable it.
        pinButton.setEnabled(field!=null);
        if (field!=null) {
            pinButton.setSelected(field.getStringValue().equals(state));
            // Only enable if configured for output
            pinButton.setEnabled(device.getInternalSettingTree().getField("gpio_mode", field.getName()).getStringValue().equals("out"));
        }
    }


    /**
     * 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());
        device.addStateChangeListener(stateChangeListener);
        device.addSettingChangeListener(stateChangeListener);
        doRefreshAction();
    }

    /**
     * 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());
        device.removeStateChangeListener(stateChangeListener);
        device.removeSettingChangeListener(stateChangeListener);
    }

    /**
     * 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 viewStateTree.hasChanged();
    }

    /**
     * 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
        stateCluster.merge(viewStateTree, 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
        viewStateTree.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();
    }


}

