/*
 * Copyright (c) 2000-2009 TeamDev Ltd. All rights reserved.
 * TeamDev PROPRIETARY and CONFIDENTIAL.
 * Use is subject to license terms.
 */
package com.jniwrapper.win32.samples.demo;

import com.jniwrapper.samples.shell.components.HTMLText;
import com.jniwrapper.samples.shell.components.LazyPanel;
import com.jniwrapper.util.Logger;
import com.jniwrapper.win32.io.FileSystemEvent;
import com.jniwrapper.win32.io.FileSystemEventListener;
import com.jniwrapper.win32.io.FileSystemException;
import com.jniwrapper.win32.io.FileSystemWatcher;
import com.jniwrapper.win32.ui.controls.AbstractChooserField;
import com.jniwrapper.win32.ui.controls.SelectFolderField;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 @author Serge Piletsky
 */
public class FileSystemWatcherSample extends ComfyJSample
        implements ActionListener, FileSystemEventListener, PropertyChangeListener
{
    private static final Logger LOG = Logger.getInstance(FileSystemWatcherSample.class);

    static final String[] ACTION_NAMES = new String[]{"added""removed""modified""renamed"};
    static final String MESSAGE_TEMPLATE = "File ''{0}'' was {1}";

    static final String SHORT_DATE_FORMAT = "HH:mm:ss";
    static final String LONG_DATE_FORMAT = "dd/MM/yyyy HH:mm:ss";

    private JLabel lblAdvisoryText;
    private JLabel lblSelectedFolderCaption;
    private AbstractChooserField _selectFolderField;
    private JCheckBox chkWatchSubtree;
    private JCheckBox chkChangeFileName;
    private JCheckBox chkChangeDirName;
    private JCheckBox chkChangeAttributes;
    private JCheckBox chkChangeSize;
    private JCheckBox chkChangeLastWrite;
    private JList lstEvents;
    private DefaultListModel _listModel;
    private JLabel lblEvents;
    private FileSystemWatcher _fileSystemWatcher;
    private JButton btnStart;
    private JButton btnStop;

    private boolean _comfyjAvailable;

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

    public void initialize() throws Exception
    {
        _comfyjAvailable = isComfyJAvailable();

        lblAdvisoryText = new HTMLText("This sample demonstrates FileSystemWatcher class features.");
        lblSelectedFolderCaption = new JLabel("Watching Folder:");
        chkWatchSubtree = new JCheckBox("Watch Subtree"true);
        chkChangeFileName = new JCheckBox("Watch File Name Change"true);
        chkChangeDirName = new JCheckBox("Watch Dir Name Change"true);
        chkChangeAttributes = new JCheckBox("Watch Attributes Change"true);
        chkChangeSize = new JCheckBox("Watch Size Change"true);
        chkChangeLastWrite = new JCheckBox("Watch Last Write Change"true);
        if (_comfyjAvailable)
        {
            _selectFolderField = new SelectFolderField();
        }
        else
        {
            _selectFolderField = new JFileChooserField();
        }

        _selectFolderField.addPropertyChangeListener(SelectFolderField.PROPERTY_FOLDER, this);

        lblEvents = new JLabel("File System Events:");
        _listModel = new DefaultListModel();
        lstEvents = new JList(_listModel);
        btnStart = new JButton("Start");
        btnStart.setEnabled(false);
        btnStart.addActionListener(this);
        btnStop = new JButton("Stop");
        btnStop.setEnabled(false);
        btnStop.addActionListener(this);

        setLayout(new GridBagLayout());

        add(lblAdvisoryText, new GridBagConstraints(00210.00.0
                , GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(10101010)00));

        add(lblSelectedFolderCaption, new GridBagConstraints(01110.00.0
                , GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(10101010)00));

        add(_selectFolderField, new GridBagConstraints(11110.00.0
                , GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(10101010)00));

        add(chkWatchSubtree, new GridBagConstraints(02110.00.0
                , GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(31000)00));

        add(chkChangeFileName, new GridBagConstraints(12110.00.0
                , GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(35010)00));

        add(chkChangeDirName, new GridBagConstraints(03110.00.0
                , GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(31000)00));

        add(chkChangeAttributes, new GridBagConstraints(13110.00.0
                , GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(35010)00));

        add(chkChangeSize, new GridBagConstraints(04110.00.0
                , GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(31000)00));

        add(chkChangeLastWrite, new GridBagConstraints(14110.00.0
                , GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(35010)00));

        add(lblEvents, new GridBagConstraints(05110.00.0
                , GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(1010010)00));

        JScrollPane eventPane = new JScrollPane(lstEvents);
        Dimension preferredSize = new Dimension(200100);
        eventPane.setPreferredSize(preferredSize);
        eventPane.setMinimumSize(preferredSize);

        add(eventPane, new GridBagConstraints(06211.01.0
                , GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(0101010)00));

        JPanel buttonsPanel = new JPanel();

        buttonsPanel.add(btnStart);
        buttonsPanel.add(btnStop);

        add(buttonsPanel, new GridBagConstraints(17110.00.0
                , GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(010010)00));

        add(new JPanel()new GridBagConstraints(08211.01.0
                , GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(0000)00));
        super.initialize();
    }

    public void actionPerformed(ActionEvent e)
    {
        if (e.getSource().equals(btnStart))
        {
            startWatching();
        }
        else if (e.getSource().equals(btnStop))
        {
            stopWatching();
        }
    }

    private void startWatching()
    {
        String folderName;
        if (_comfyjAvailable)
        {
            folderName = ((SelectFolderField_selectFolderField).getFolder();
        }
        else
        {
            folderName = ((JFileChooserField_selectFolderField).getFolder();
        }
        _listModel.clear();
        _fileSystemWatcher = new FileSystemWatcher(folderName, chkWatchSubtree.isSelected());
        _fileSystemWatcher.addFileSystemListener(this);
        FileSystemWatcher.WatcherOptions options = _fileSystemWatcher.getOptions();
        options.setNotifyChangeAttributes(chkChangeAttributes.isSelected());
        options.setNotifyChangeDirName(chkChangeDirName.isSelected());
        options.setNotifyChangeFileName(chkChangeFileName.isSelected());
        options.setNotifyChangeSize(chkChangeSize.isSelected());
        options.setNotifyLastModified(chkChangeLastWrite.isSelected());
        btnStart.setEnabled(false);
        btnStop.setEnabled(true);
        try
        {
            _fileSystemWatcher.start();
            _listModel.addElement("Started at " + getDateTime(true));
            lstEvents.setSelectedIndex(0);
        }
        catch (FileSystemException e1)
        {
            LOG.error("", e1);
            JOptionPane.showMessageDialog(this, "Unable to start watcher.\n" + e1, "File System Watcher", JOptionPane.WARNING_MESSAGE);
            btnStart.setEnabled(true);
            btnStop.setEnabled(false);
        }
    }

    private void stopWatching()
    {
        _listModel.addElement("Stopped at " + getDateTime(true));
        final int size = _listModel.getSize() 1;
        lstEvents.setSelectedIndex(size);
        lstEvents.ensureIndexIsVisible(size);
        btnStart.setEnabled(true);
        btnStop.setEnabled(false);
        try
        {
            _fileSystemWatcher.stop();
        }
        catch (FileSystemException e1)
        {
            LOG.error("", e1);
        }
    }

    public void deactivate()
    {
        if (_fileSystemWatcher != null && _fileSystemWatcher.isWatching())
        {
            stopWatching();
        }
    }

    public void handle(FileSystemEvent event)
    {
        String actionName = ACTION_NAMES[event.getAction() 1];
        String message = MessageFormat.format(MESSAGE_TEMPLATE, new Object[]{event.getFileInfo(), actionName});
        if (event.getAction() == FileSystemEvent.FILE_RENAMED)
            message += " from '" + event.getOldFileInfo() "'";
        message += " at " + getDateTime(false);

        _listModel.addElement(message);
        final int size = _listModel.getSize() 1;
        //lstEvents.setSelectedIndex(size);
        //lstEvents.ensureIndexIsVisible(size);
    }

    String getDateTime(boolean longFormat)
    {
        SimpleDateFormat dateFormat = new SimpleDateFormat(longFormat ? LONG_DATE_FORMAT : SHORT_DATE_FORMAT);
        return dateFormat.format(new Date());
    }

    public void propertyChange(PropertyChangeEvent evt)
    {
        btnStart.setEnabled(true);
    }

    public static class JFileChooserField extends AbstractChooserField
    {
        public static final String PROPERTY_FOLDER = "folder";

        public JFileChooserField()
        {
            super();
        }

        public void setFolder(String folder)
        {
            getTextField().setText(folder);
        }

        public String getFolder()
        {
            return getTextField().getText();
        }

        public void actionPerformed(ActionEvent e)
        {
            String oldFolder = getFolder();

            JFileChooser fileDialog = new JFileChooser(oldFolder);
            fileDialog.setDialogType(JFileChooser.OPEN_DIALOG);
            fileDialog.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
            if (fileDialog.showOpenDialog(SwingUtilities.getWindowAncestor(this)) == JFileChooser.APPROVE_OPTION)
            {
                String newFolder = fileDialog.getSelectedFile().getAbsolutePath();
                setFolder(newFolder);
                firePropertyChange(PROPERTY_FOLDER, oldFolder, newFolder);
            }
        }
    }
}