/*
 * Copyright (c) 2002-2007 TeamDev Ltd. All rights reserved.
 *
 * Use is subject to license terms.
 *
 * The complete licence text can be found at
 * http://www.teamdev.com/winpack/license.jsf
 */
package com.jniwrapper.win32.samples.demo;

import com.jniwrapper.samples.shell.components.HTMLText;
import com.jniwrapper.samples.shell.components.LazyPanel;
import com.jniwrapper.samples.shell.components.LineBevel;
import com.jniwrapper.win32.dde.*;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Date;

/**
 @author Vladimir Kondrashchenko
 */
public class DdeSample extends LazyPanel
{
    private JLabel _lblAdvisoryText;
    private JLabel _lblService;
    private JLabel _lblClient;
    private JLabel _lblMessageLog;
    private JTextArea _serviceLog;

    private MyClient _myClient;
    private MyService _myService;

    public DdeSample(Window parent)
    {
        super(parent);
    }

    public void initialize() throws Exception
    {
        _lblAdvisoryText = new HTMLText("This page demonstrates a simple DDE client-server conversation.");
        _lblClient = new JLabel("DDE Client");
        _lblService = new JLabel("DDE Service");

        _serviceLog = new JTextArea();
        _serviceLog.setFont(new Font("Courier New", Font.PLAIN, 12));
        _serviceLog.setEditable(false);

        _myService = new MyService();
        _myClient = new MyClient();

        _lblMessageLog = new JLabel("Message Log:");

        JPanel bevel1 = new LineBevel();

        JPanel bevel2 = new LineBevel();

        setLayout(new BorderLayout());

        JPanel clientHeader = new JPanel(new GridBagLayout());
        clientHeader.add(_lblAdvisoryText, new GridBagConstraints(00210.00.0,
                GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(10101010)00));

        clientHeader.add(_lblClient, new GridBagConstraints(01110.00.0,
                GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(01000)00));
        clientHeader.add(bevel1, new GridBagConstraints(11111.00.0,
                GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets(01000)00));

        _myClient.setBorder(BorderFactory.createEmptyBorder(10101010));

        JSplitPane clientPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, clientHeader, _myClient);
        clientPane.setBorder(BorderFactory.createEmptyBorder());
        clientPane.setDividerLocation(60);
        clientPane.setDividerSize(0);

        JPanel serviceHeader = new JPanel(new GridBagLayout());
        serviceHeader.add(_lblService, new GridBagConstraints(00110.00.0,
                GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(01000)00));
        serviceHeader.add(bevel2, new GridBagConstraints(10111.00.0,
                GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets(01000)00));

        JPanel servicePanel = new JPanel(new GridBagLayout());
        servicePanel.add(_lblMessageLog, new GridBagConstraints(00110.00.0,
                GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(51000)00));
        servicePanel.add(new JScrollPane(_serviceLog)new GridBagConstraints(01111.01.0,
                GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(0101010)00));

        JSplitPane servicePane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, serviceHeader, servicePanel);
        servicePane.setBorder(BorderFactory.createEmptyBorder());
        servicePane.setDividerSize(0);

        final JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, clientPane, servicePane);
        splitPane.setBorder(BorderFactory.createEmptyBorder());
        splitPane.setDividerLocation(0.6);
        splitPane.setDividerSize(0);
        splitPane.addComponentListener(new ComponentAdapter()
        {
            public void componentResized(ComponentEvent e)
            {
                splitPane.setDividerLocation(0.6);
            }
        });

        add(splitPane, BorderLayout.CENTER);

        _myService.runService();

        getParentWindow().addWindowListener(new WindowAdapter()
        {
            public void windowClosing(WindowEvent e)
            {
                try
                {
                    _myService.shutdownService();
                }
                catch (DdeException ex)
                {
                }
            }
        });

        super.initialize();
    }

    JTextArea getServiceLog()
    {
        return _serviceLog;
    }

    /**
     * MyService is a simple DDE service, that contains two topics: System and Data.
     *
     * The System topic supports the following item names: Topics (a list of supported topics),
     * Items (a list of non-System item names), Formats (a list of supported clipboard formats)
     * and SysItems (a list of System item names).
     *
     * The Data topic is designed for data management. It supports the following item names:
     * CurrentDate ([out] sends a client a string representation of the current date),
     * Spool ([in, out] sends or recieves text data),
     * Timer ([out] the item value changes every second and equals the time in seconds since the
     * service has been started).
     * The Data topic also supports the "reverse" command that replaces the Spool text by the reverse of the text.
     *
     @author Vladimir Kondrashchenko
     */
    public class MyService
    {
        private String _currentDate;
        private StringBuffer _spool = new StringBuffer();
        private int _timer = 0;
        private DdeService _service;

        private class EventHandler implements DdeServiceEventHandler
        {
            public EventHandler()
            {
                Date date = new Date();
                _currentDate = date.toString();
                new Timer(1000new ActionListener()
                {
                    public void actionPerformed(ActionEvent e)
                    {
                        _timer++;
                        if (_service != null && _service.isRegistered())
                        {
                            try
                            {
                                _service.postAdvise("Data""Timer");
                            }
                            catch (DdeException ex)
                            {
                                ex.printStackTrace();
                            }
                        }
                    }
                }).start();
            }

            public byte[] adviseRequest(String topic, DdeItem item)
            {
                if (!topic.equalsIgnoreCase("Data"| !item.getName().equalsIgnoreCase("Timer"|
                        (item.getFormat() != DdeItem.CF_TEXT))
                {
                    return null;
                }
                String result = new Integer(_timer).toString();

                MyService.this.log("Advise request : " + result);

                return result.getBytes();
            }

            public boolean adviseStart(String topic, DdeItem item)
            {
                if (!topic.equalsIgnoreCase("Data"| !item.getName().equalsIgnoreCase("Timer"|
                        (item.getFormat() != DdeItem.CF_TEXT))
                {
                    MyService.this.log("Advise start : refused");
                    return false;
                }
                else
                {
                    MyService.this.log("Advise start : confirmed");
                    return true;
                }
            }

            public void adviseStop(String topic, DdeItem item)
            {
                MyService.this.log("Advise stoped");
            }

            public DdeResponse execute(String topic, String command)
            {
                if (!topic.equalsIgnoreCase("Data"| !command.equalsIgnoreCase("reverse"))
                {
                    MyService.this.log("Execute [" + topic + ", " + command + "] : Not processed");
                    return DdeResponse.NOTPROCESSED;
                }
                _spool = _spool.reverse();
                MyService.this.log("Execute [" + topic + ", " + command + "] : Processed");
                return DdeResponse.PROCESSED;
            }

            public boolean beforeConnect(String topic, boolean sameApplication)
            {
                if (!topic.equalsIgnoreCase("System"&& !topic.equalsIgnoreCase("Data"))
                {
                    if (sameApplication)
                    {
                        MyService.this.log("Connection to " + topic + " topic has been refused (the same application)");
                    }
                    else
                    {
                        MyService.this.log("Connection to " + topic + " topic has been refused");
                    }
                    return false;
                }
                else
                {
                    if (sameApplication)
                    {
                        MyService.this.log("Connection to " + topic + " topic has been " +
                                "established (the same application)");

                    }
                    else
                    {
                        MyService.this.log("Connection to " + topic + " has been established");
                    }
                    return true;
                }
            }

            public DdeResponse pokeData(String topic, DdeItem item, byte[] data)
            {
                if (!topic.equalsIgnoreCase("Data"| !item.getName().equalsIgnoreCase("Spool"|
                        (item.getFormat() != DdeItem.CF_TEXT))
                {
                    MyService.this.log("Poke data [" new String(data"] : Not processed");
                    return DdeResponse.NOTPROCESSED;
                }
                _spool = new StringBuffer(new String(data));
                MyService.this.log("Poke data [" new String(data"] : Processed");
                return DdeResponse.PROCESSED;
            }

            public byte[] requestData(String topic, DdeItem item)
            {
                if (topic.equalsIgnoreCase("System"))
                {
                    if (item.getName().equalsIgnoreCase("Topics"))
                    {
                        String result = "System\tData";
                        MyService.this.log("Request data [" + topic + ", " + item.getName() "] : " + result);
                        return result.getBytes();
                    }
                    else if (item.getName().equalsIgnoreCase("Items"))
                    {
                        String result = "CurrentDate\tSpool\tTimer";
                        MyService.this.log("Request data [" + topic + ", " + item.getName() "] : " + result);
                        return result.getBytes();
                    }
                    else if (item.getName().equalsIgnoreCase("Formats"))
                    {
                        String result = "CF_TEXT";
                        MyService.this.log("Request data [" + topic + ", " + item.getName() "] : " + result);
                        return result.getBytes();
                    }
                    else if (item.getName().equalsIgnoreCase("SysItems"))
                    {
                        String result = "Topics\tItems\tFormats\tSysItems";
                        MyService.this.log("Request data [" + topic + ", " + item.getName() "] : " + result);
                        return result.getBytes();
                    }
                    else
                    {
                        MyService.this.log("Request data : item \"" + item.getName() "\" is not supported by System topic");
                        return null;
                    }
                }
                else if (topic.equalsIgnoreCase("Data"))
                {
                    if (item.getName().equalsIgnoreCase("CurrentDate"))
                    {
                        MyService.this.log("Request data [" + topic + ", " + item.getName() "] : " + _currentDate);
                        return _currentDate.getBytes();
                    }
                    else if (item.getName().equalsIgnoreCase("Spool"))
                    {
                        MyService.this.log("Request data [" + topic + ", " + item.getName() "] : " + _spool.toString());
                        return _spool.toString().getBytes();
                    }
                    else if (item.getName().equalsIgnoreCase("Timer"))
                    {
                        String result = new Integer(_timer).toString();
                        MyService.this.log("Request data [" + topic + ", " + item.getName() "] : " + result);
                        return result.getBytes();
                    }
                    else
                    {
                        MyService.this.log("Request data : item \"" + item.getName() "\" is not supported by Data topic");
                        return null;
                    }
                }
                else
                {
                    MyService.this.log("Request data : topic \"" + topic + "\" is not supported");
                    return null;
                }
            }

            public void disconnect(boolean sameApplication)
            {
                if (sameApplication)
                {
                    MyService.this.log("Disconnected (the same application)");
                }
                else
                {
                    MyService.this.log("Disconnected");
                }
            }

            public void serviceRegister(String service, String instanceName)
            {
                MyService.this.log("A service registered : " + instanceName);
            }

            public void serviceUnregister(String service, String instanceName)
            {
                MyService.this.log("A service unregistered : " + instanceName);
            }
        }

        private void log(String s)
        {
            DdeSample.this._serviceLog.append(s + "\n");
        }

        public void runService() throws DdeException
        {
            _service = new DdeService("MyService");
            EventHandler eventHandler = new EventHandler();
            _service.setEventHandler(eventHandler);
            _service.register();
            log("Service \"MyService\" is running...");
        }

        public void shutdownService() throws DdeException
        {
            _service.unregister();
            log("Service \"MyService\" is stopped.");
        }
    }

    /**
     * This is a simple DDE client of MyService.
     */
    public class MyClient extends JPanel
    {
        private DdeClient _systemClient;
        private DdeClient _dataClient;

        private JTextArea _logArea;
        private JToggleButton _btnConnect;
        private JToggleButton _btnAdviseLoop;
        private JTextField _textField;
        private JButton _btnGetSpool;
        private JButton _btnSetSpool;
        private JButton _btnReverseSpool;
        private JLabel _lblSpoolData;
        private JLabel _lblMessageLog;

        private long _timeout = 1000;
        private long _transactionID = 0;

        public MyClient()
        {
            _systemClient = new DdeClient("MyService""System");

            _dataClient = new DdeClient("MyService""Data");
            _dataClient.setEventHandler(new EventHandler());

            _logArea = new JTextArea();
            _logArea.setFont(new Font("Courier New", Font.PLAIN, 12));
            _logArea.setEditable(false);

            _btnConnect = new JToggleButton(new AbstractAction("Connect to MyService")
            {
                public void actionPerformed(ActionEvent e)
                {
                    if (_btnConnect.isSelected())
                    {
                        try
                        {
                            _systemClient.connect();
                            _dataClient.connect();
                            _logArea.append("The connection to MyService is established successfully\n");
                            _btnConnect.setText("Disconnect from MyService");
                        }
                        catch (DdeException ex)
                        {
                            _logArea.append("Unable to connect to MyService\n");
                            _btnConnect.setSelected(false);
                        }
                    }
                    else
                    {
                        try
                        {
                            if (_btnAdviseLoop.isSelected())
                            {
                                _btnAdviseLoop.doClick();
                            }
                            _systemClient.disconnect();
                            _dataClient.disconnect();
                            _logArea.append("The client is disconnected from MyService\n");
                            _btnConnect.setText("Connect to MyService");
                        }
                        catch (DdeException ex)
                        {
                            _logArea.append("Unable to disconnect from MyService\n");
                        }
                    }
                }
            });

            _btnAdviseLoop = new JToggleButton(new AbstractAction("Start Advise Loop")
            {
                public void actionPerformed(ActionEvent e)
                {
                    if (_btnAdviseLoop.isSelected())
                    {
                        try
                        {
                            _dataClient.startAdviseLoop(new DdeItem("Timer", DdeItem.CF_TEXT), true, true, _timeout);
                            _logArea.append("An advise loop is started\n");
                            _btnAdviseLoop.setText("Stop Advise Loop");
                        }
                        catch (DdeException ex)
                        {
                            _btnAdviseLoop.setSelected(false);
                            _logArea.append("Unable to start an advise loop\n");
                        }
                    }
                    else
                    {
                        try
                        {
                            _dataClient.stopAdviseLoop(new DdeItem("Timer"), _timeout);
                            _logArea.append("An advise loop is stopped\n");
                            _btnAdviseLoop.setText("Start Advise Loop");
                        }
                        catch (DdeException ex)
                        {
                            _logArea.append("Unable to stop the advise loop\n");
                        }
                    }
                }
            });

            _textField = new JTextField(19);

            _btnGetSpool = new JButton(new AbstractAction("Receive")
            {
                public void actionPerformed(ActionEvent e)
                {
                    try
                    {
                        byte[] data = _dataClient.get(new DdeItem("Spool"), _timeout);
                        _logArea.append("The spool data : " new String(data"\n");
                    }
                    catch (DdeException ex)
                    {
                        _logArea.append("Unable to receive a spool data\n");
                    }
                }
            });

            _btnSetSpool = new JButton(new AbstractAction("Send")
            {
                public void actionPerformed(ActionEvent e)
                {
                    try
                    {
                        _dataClient.send(new DdeItem("Spool"), _textField.getText().getBytes(), _timeout);
                        _logArea.append("\"" + _textField.getText() "\" was sent to Spool\n");
                    }
                    catch (DdeException ex)
                    {
                        _logArea.append("Unable to send data to Spool\n");
                    }
                }
            });

            _btnReverseSpool = new JButton(new AbstractAction("Reverse")
            {
                public void actionPerformed(ActionEvent e)
                {
                    try
                    {
                        _dataClient.execute("Reverse", _timeout);
                        _logArea.append("The command was successfully executed\n");
                    }
                    catch (DdeException ex)
                    {
                        _logArea.append("Unable to execute the command\n");
                    }
                }
            });

            Dimension buttonSize = new JButton("Disconnect from MyService").getPreferredSize();
            _btnAdviseLoop.setPreferredSize(buttonSize);
            _btnConnect.setPreferredSize(buttonSize);

            buttonSize = _btnReverseSpool.getPreferredSize();
            _btnSetSpool.setPreferredSize(buttonSize);
            _btnGetSpool.setPreferredSize(buttonSize);

            _lblSpoolData = new JLabel("The Spool Data: ");
            _lblSpoolData.setBorder(BorderFactory.createEmptyBorder(0500));

            JPanel navPanel = new JPanel();
            navPanel.setLayout(new GridBagLayout());
            navPanel.add(_btnConnect, new GridBagConstraints(00110.00.0,
                    GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(00025)00));
            navPanel.add(_btnAdviseLoop, new GridBagConstraints(01110.00.0,
                    GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(00025)00));
            navPanel.add(_lblSpoolData, new GridBagConstraints(10110.00.0,
                    GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0000)00));
            navPanel.add(_textField, new GridBagConstraints(20110.00.0,
                    GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0000)00));

            JPanel spoolNav = new JPanel(new FlowLayout());
            spoolNav.add(_btnGetSpool);
            spoolNav.add(_btnSetSpool);
            spoolNav.add(_btnReverseSpool);
            spoolNav.setBorder(BorderFactory.createEmptyBorder());
            navPanel.add(spoolNav, new GridBagConstraints(11210.00.0,
                    GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0000)00));

            _lblMessageLog = new JLabel("Message Log:");

            setLayout(new BorderLayout());

            JPanel logPanel = new JPanel(new GridBagLayout());
            logPanel.add(_lblMessageLog, new GridBagConstraints(00110.00.0,
                    GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5000)00));
            logPanel.add(new JScrollPane(_logArea)new GridBagConstraints(01111.01.0,
                    GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(0000)00));

            JPanel navWrapper = new JPanel(new BorderLayout());
            navWrapper.add(navPanel, BorderLayout.WEST);

            JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, navWrapper, logPanel);
            splitPane.setBorder(BorderFactory.createEmptyBorder());
            splitPane.setDividerSize(0);

            add(splitPane, BorderLayout.CENTER);
        }

        private class EventHandler implements DdeClientEventHandler
        {
            public void error(int errorCode)
            {
                _logArea.append("Error message : error code " new Integer(errorCode"\n");
            }

            public DdeResponse itemChanged(DdeItem item, byte[] data)
            {
                _logArea.append("Advise loop message : \nMyService was started " new String(data+
                        " seconds ago [item=" + item.getName() ";format=" + item.getFormat() +"]\n");
                return DdeResponse.PROCESSED;
            }

            public void asyncActionComplete(DdeItem item, byte[] data, long transactionID)
            {
                if (transactionID == _transactionID)
                {
                    _logArea.append("\nCurrent date [asynchronous action complete] : \n\t" new String(data"\n");
                }
            }

            public void disconnect(boolean sameApplication)
            {
                _logArea.append("MyService is disconnected\n");
            }

            public void serviceRegister(String service, String instanceName)
            {
            }

            public void serviceUnregister(String service, String instanceName)
            {
            }
        }
    }
}