/*
 * Copyright (c) 2003, Vanderbilt University
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice, the following
 * two paragraphs and the author appear in all copies of this software.
 * 
 * IN NO EVENT SHALL THE VANDERBILT UNIVERSITY BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE VANDERBILT
 * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE VANDERBILT UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE VANDERBILT UNIVERSITY HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 */

package net.tinyos.mcenter;

import net.tinyos.packet.PacketListenerIF;

/**
 *
 * @author  nadand
 */
public class RegisterManager extends /*javax.swing.JInternalFrame*/MessageCenterInternalFrame implements PacketListenerIF{
    
    
    private final int registerCount = 16;
    private byte[] registerSpace;
    private int[] registerAddress= {0xd300,0xd310,0xd320,0xd330};
    private int moteAddress = 0xffff;
    private boolean broadcastEnabled = false;
    
    private byte[] sendPacket;
    
    private java.util.HashSet changes = new java.util.HashSet();
    
    private String[] headTitles;
    private javax.swing.JTable generalTable;
    private javax.swing.JTable channelATable;
    private javax.swing.JTable channelBTable;
    private javax.swing.JTable channelCTable;
    
    private RegisterReaderThread sendThread;
    private int radioDelay = 500;
    
    private static final int READ_REPLY = 0x10;
    private static final int READ_REQ = 0x11;
    private static final int WRITE_REQ = 0x12;
    private static final int SHOT_DETECTED = 0x13;
    private static final int MAX_SEQ = 0x600;
    
    /** Creates new form RegisterManager */
    public RegisterManager() {
        super("Register Manager");
        registerSpace = new byte[registerCount*4];
        generateTitle();
        initPacket();
        initComponents();
        generalTable = new javax.swing.JTable(new RegisterTableModel(0));
        channelATable = new javax.swing.JTable(new RegisterTableModel(registerCount));
        channelBTable = new javax.swing.JTable(new RegisterTableModel(2*registerCount));
        channelCTable = new javax.swing.JTable(new RegisterTableModel(3*registerCount));
        generalScrollPane.setViewportView(generalTable);
        channelAScrollPane.setViewportView(channelATable);
        channelBScrollPane.setViewportView(channelBTable);
        channelCScrollPane.setViewportView(channelCTable);
        
        SerialConnector.instance().registerPacketListener(this,READ_REPLY);
        
    }
    
    private void generateTitle(){
        headTitles = new String[registerCount];
        for(int i =0; i < registerCount; i++){
            headTitles[i] = "0x"+Integer.toString(i,16).toUpperCase();
            
        }
    }
    private void initPacket(){
        sendPacket = new byte[SerialConnector.instance().getMsgLength()];
        sendPacket[0] = (byte)0xff;     // Destination address low byte
        sendPacket[1] = (byte)0xff;     // Destination address high byte
        sendPacket[2] = (byte)READ_REQ; // Type = AM_READ_REQ
        sendPacket[3] = (byte)0x7d;     // Group ID
        sendPacket[4] = (byte)4;        // Length of payload
    }
    
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    private void initComponents() {//GEN-BEGIN:initComponents
        java.awt.GridBagConstraints gridBagConstraints;

        jPanel1 = new javax.swing.JPanel();
        jLabel1 = new javax.swing.JLabel();
        generalScrollPane = new javax.swing.JScrollPane();
        jLabel2 = new javax.swing.JLabel();
        channelAScrollPane = new javax.swing.JScrollPane();
        jLabel3 = new javax.swing.JLabel();
        channelBScrollPane = new javax.swing.JScrollPane();
        jLabel4 = new javax.swing.JLabel();
        channelCScrollPane = new javax.swing.JScrollPane();
        jPanel2 = new javax.swing.JPanel();
        readButton = new javax.swing.JButton();
        bcastCheckBox = new javax.swing.JCheckBox();
        writeButton = new javax.swing.JButton();
        jPanel3 = new javax.swing.JPanel();
        moteLabel = new javax.swing.JLabel();
        moteTextField = new javax.swing.JTextField();
        radioDelayCheckBox = new javax.swing.JCheckBox();

        getContentPane().setLayout(new java.awt.GridBagLayout());

        jPanel1.setLayout(new java.awt.GridBagLayout());

        jLabel1.setText("Genaral Registers");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
        gridBagConstraints.ipady = 8;
        gridBagConstraints.weightx = 1.0;
        jPanel1.add(jLabel1, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        jPanel1.add(generalScrollPane, gridBagConstraints);

        jLabel2.setText("Channel A Registers");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
        gridBagConstraints.ipady = 8;
        gridBagConstraints.weightx = 1.0;
        jPanel1.add(jLabel2, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        jPanel1.add(channelAScrollPane, gridBagConstraints);

        jLabel3.setText("Channel B Registers");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
        gridBagConstraints.ipady = 8;
        gridBagConstraints.weightx = 1.0;
        jPanel1.add(jLabel3, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        jPanel1.add(channelBScrollPane, gridBagConstraints);

        jLabel4.setText("Channel C Registers");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
        gridBagConstraints.ipady = 8;
        jPanel1.add(jLabel4, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        jPanel1.add(channelCScrollPane, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        getContentPane().add(jPanel1, gridBagConstraints);

        jPanel2.setLayout(new java.awt.GridBagLayout());

        readButton.setText("Read Registers");
        readButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                readButtonActionPerformed(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.weightx = 1.0;
        jPanel2.add(readButton, gridBagConstraints);

        bcastCheckBox.setText("to All");
        bcastCheckBox.addItemListener(new java.awt.event.ItemListener() {
            public void itemStateChanged(java.awt.event.ItemEvent evt) {
                bcastCheckBoxItemStateChanged(evt);
            }
        });

        jPanel2.add(bcastCheckBox, new java.awt.GridBagConstraints());

        writeButton.setText("Write Changes");
        writeButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                writeButtonActionPerformed(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.weightx = 1.0;
        jPanel2.add(writeButton, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weighty = 0.2;
        getContentPane().add(jPanel2, gridBagConstraints);

        jPanel3.setLayout(new java.awt.GridBagLayout());

        jPanel3.setBorder(new javax.swing.border.LineBorder(new java.awt.Color(0, 0, 0)));
        moteLabel.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING);
        moteLabel.setText("Mote ID");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        gridBagConstraints.weightx = 1.0;
        jPanel3.add(moteLabel, gridBagConstraints);

        moteTextField.setText("0xFFFF");
        moteTextField.setMinimumSize(new java.awt.Dimension(80, 20));
        moteTextField.setPreferredSize(new java.awt.Dimension(80, 20));
        moteTextField.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                moteTextFieldActionPerformed(evt);
            }
        });

        moteTextField.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusLost(java.awt.event.FocusEvent evt) {
                moteTextFieldFocusLost(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        jPanel3.add(moteTextField, gridBagConstraints);

        radioDelayCheckBox.setSelected(true);
        radioDelayCheckBox.setText("Radio delay");
        radioDelayCheckBox.addItemListener(new java.awt.event.ItemListener() {
            public void itemStateChanged(java.awt.event.ItemEvent evt) {
                radioDelayCheckBoxItemStateChanged(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.weightx = 3.0;
        jPanel3.add(radioDelayCheckBox, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weighty = 0.1;
        getContentPane().add(jPanel3, gridBagConstraints);

        pack();
        java.awt.Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
        setSize(new java.awt.Dimension(657, 357));
        setLocation((screenSize.width-657)/2,(screenSize.height-357)/2);
    }//GEN-END:initComponents

    private void bcastCheckBoxItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_bcastCheckBoxItemStateChanged
        if (evt.getStateChange() == java.awt.event.ItemEvent.SELECTED) {
            broadcastEnabled = true;
        } else if(evt.getStateChange() == java.awt.event.ItemEvent.DESELECTED) {
            broadcastEnabled = false;
        }
    }//GEN-LAST:event_bcastCheckBoxItemStateChanged
    
    private void moteTextFieldFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_moteTextFieldFocusLost
        updateMoteID();
    }//GEN-LAST:event_moteTextFieldFocusLost
    
    private void moteTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_moteTextFieldActionPerformed
        updateMoteID();
    }//GEN-LAST:event_moteTextFieldActionPerformed
    
    private void radioDelayCheckBoxItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_radioDelayCheckBoxItemStateChanged
        if (evt.getStateChange() == java.awt.event.ItemEvent.SELECTED) {
            radioDelay = 250;
        } else if(evt.getStateChange() == java.awt.event.ItemEvent.DESELECTED) {
            radioDelay = 10;
        }
    }//GEN-LAST:event_radioDelayCheckBoxItemStateChanged
    
    private void writeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_writeButtonActionPerformed
        if(!changes.isEmpty()){
            int address, data;
            int destinationAddress = moteAddress;
            java.util.Iterator changesIterartor= changes.iterator();
            while(changesIterartor.hasNext()){
                
                int changedAddress = ((Integer)changesIterartor.next()).intValue();
                changesIterartor.remove();
                if(0 <= changedAddress && changedAddress < registerCount){
                    address = registerAddress[0] + changedAddress;
                    
                }else if(registerCount <= changedAddress && changedAddress < (2*registerCount)){
                    address = registerAddress[1] + changedAddress-registerCount;
                    
                }else if((2*registerCount) <= changedAddress && changedAddress < (3*registerCount)){
                    address = registerAddress[2] + changedAddress-(2*registerCount);
                    
                }else if((3*registerCount) <= changedAddress && changedAddress < (4*registerCount)){
                    address = registerAddress[3] + changedAddress-(3*registerCount);
                }else{
                    return;
                }
                
                data = registerSpace[changedAddress];
                
                sendPacket[2] = (byte)(WRITE_REQ & 0xff);             	// Type = AM_WRITE_REQ
                
                sendPacket[6] = (byte)((destinationAddress >> 8) & 0xff);      	// Mote Address high byte
                sendPacket[5] = (byte)(destinationAddress & 0xff);              	// Mote Address low byte
                
                
                sendPacket[8] = (byte)((address >> 8) & 0xff);      	// Address high byte
                sendPacket[7] = (byte)(address & 0xff);              	// Address low byte
                sendPacket[9] = (byte)(0x01 & 0xff);             	// length byte
                sendPacket[10] = (byte)(data & 0xff);             	// Data byte
                
                byte[] writeData = new byte[6];
                writeData[0] = sendPacket[5];
                writeData[1] = sendPacket[6];
                writeData[2] = sendPacket[7];
                writeData[3] = sendPacket[8];
                writeData[4] = sendPacket[9];
                writeData[5] = sendPacket[10];
                
                if(broadcastEnabled)
                    destinationAddress = 0xFFFF;
                
                SerialConnector.instance().sendMessage(destinationAddress, (short)sendPacket[2], (short)sendPacket[3], writeData);
            }
        }
    }//GEN-LAST:event_writeButtonActionPerformed
    
    private void readButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_readButtonActionPerformed
        System.out.println("Send Read");
        sendThread = new RegisterReaderThread();
        sendThread.start();
    }//GEN-LAST:event_readButtonActionPerformed
    
    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JLabel jLabel4;
    private javax.swing.JScrollPane channelBScrollPane;
    private javax.swing.JButton readButton;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JScrollPane channelCScrollPane;
    private javax.swing.JPanel jPanel3;
    private javax.swing.JButton writeButton;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JCheckBox radioDelayCheckBox;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JScrollPane channelAScrollPane;
    private javax.swing.JLabel moteLabel;
    private javax.swing.JCheckBox bcastCheckBox;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JScrollPane generalScrollPane;
    private javax.swing.JTextField moteTextField;
    // End of variables declaration//GEN-END:variables
    
    private void updateMoteID(){
        String value = moteTextField.getText();
        if(value.trim().toUpperCase().startsWith("0X"))
            moteAddress=Short.parseShort(value.trim().substring(2),16);
        else
            moteAddress=Short.parseShort(value.trim());
    }
    
    
    /** packetReceived() is called by the SerialPortStub object. After
     *  the message processing we force the update of the screen.
     *
     *  TODO: CRC checking
     */
    public void packetReceived(byte[] packet) {
        
        int msgtype;
        int address;
        int baseAddress;
        int moteId;
        int data;
        int relativeAddress;
        int length;
        String msgstr;
        
        moteId = (packet[6] & 0xff) << 8;
        moteId |= packet[5] & 0xff;
        
        
        if(moteId != moteAddress)
            return;
        
        msgtype = packet[2] & 0xff;
        
        baseAddress = (packet[8] & 0xff) << 8;
        baseAddress |= packet[7] & 0xff;
        
        length = packet[9] & 0xff;
        
        
        if (msgtype == READ_REPLY) {
            
            for(int i = 0; i < length; i++){
                data= packet[10+i] & 0xff;
                address = baseAddress+i;
                if( registerAddress[0] <= address && address < registerAddress[1] ){
                    relativeAddress = address-registerAddress[0];
                    if(relativeAddress < registerCount){
                        
                        registerSpace[relativeAddress]=(byte)data;
                        ((javax.swing.table.DefaultTableModel) generalTable.getModel()).fireTableCellUpdated(0, relativeAddress) ;
                        //((javax.swing.table.DefaultTableModel) generalTable.getModel()).fireTableDataChanged();
                    }
                    
                }else if( registerAddress[1] <= address && address < registerAddress[2] ){
                    relativeAddress = address-registerAddress[1];
                    if(relativeAddress < registerCount){
                        
                        registerSpace[registerCount + relativeAddress]=(byte)data;
                        ((javax.swing.table.DefaultTableModel) channelATable.getModel()).fireTableCellUpdated(0, relativeAddress) ;
                        //((javax.swing.table.DefaultTableModel)channelATable.getModel()).fireTableDataChanged();
                    }
                    
                }else if( registerAddress[2] <= address && address < registerAddress[3] ){
                    relativeAddress = address-registerAddress[2];
                    if(relativeAddress < registerCount){
                        registerSpace[2*registerCount + relativeAddress]=(byte)data;
                        ((javax.swing.table.DefaultTableModel) channelBTable.getModel()).fireTableCellUpdated(0, relativeAddress) ;
                        //((javax.swing.table.DefaultTableModel)channelBTable.getModel()).fireTableDataChanged();
                    }
                    
                }else if( registerAddress[3] <= address && address < (registerAddress[3] + registerCount) ){
                    relativeAddress = address-registerAddress[3];
                    if(relativeAddress < registerCount){
                        registerSpace[3*registerCount + relativeAddress]=(byte)data;
                        ((javax.swing.table.DefaultTableModel) channelCTable.getModel()).fireTableCellUpdated(0, relativeAddress) ;
                    }
                }
                
            }
            
            
        }
        
    }
    
    
    
    /************************ Inner Classes ***********************************/
    
    private class RegisterTableModel extends javax.swing.table.DefaultTableModel{
        
        
        private int spaceStart;
        
        public RegisterTableModel(int spaceStart){
            super(headTitles,1);
            
            this.spaceStart = spaceStart;
            
        }
        
        public Object getValueAt(int row, int column) {
            if(column < registerCount && row == 0){
                return Integer.toHexString(registerSpace[spaceStart + column] & 0xFF);
            }
            return Integer.toString(-1);
        }
        
        public void setValueAt(Object aValue, int row, int column){
            if(column < registerCount && row == 0){
                registerSpace[spaceStart + column] = (byte)(Integer.parseInt((String)aValue,16) & 0xFF);
                changes.add(new Integer(spaceStart + column));
            }
        }
        
    }
    
    private class RegisterReaderThread extends Thread{
        
        public RegisterReaderThread(){}
        
        public void run(){
            
            int address;
            int destinationAddress = moteAddress;
            for(int channel = 0; channel < 4; channel++){
                //System.out.println("Channel"+Integer.toString(channel));
                
                //address = Integer.parseInt(addressField.getText(), 16);
                
                address = registerAddress[channel];
                
                sendPacket[2] = (byte)(READ_REQ & 0xff);             	// Type = AM_READ_REQ
                
                sendPacket[6] = (byte)((destinationAddress >> 8) & 0xff);      	// Mote Address high byte
                sendPacket[5] = (byte)(destinationAddress & 0xff);              	// Mote Address low byte
                
                
                sendPacket[8] = (byte)((address >> 8) & 0xff);      	// Address high byte
                sendPacket[7] = (byte)(address & 0xff);              	// Address low byte
                sendPacket[9] = (byte)(16 & 0xff);             	// length byte
                sendPacket[10] = (byte)(0x00 & 0xff);             	// Data byte
                byte[] readData = new byte[6];
                readData[0] = sendPacket[5];
                readData[1] = sendPacket[6];
                readData[2] = sendPacket[7];
                readData[3] = sendPacket[8];
                readData[4] = sendPacket[9];
                readData[5] = sendPacket[10];

                if(broadcastEnabled)
                    destinationAddress = 0xFFFF;
                SerialConnector.instance().sendMessage(destinationAddress, (short)sendPacket[2], (short)sendPacket[3], readData);
                try{
                    this.sleep(radioDelay);
                }catch(java.lang.InterruptedException ie){}
                
                
            }
        }
        
    }
    
}
