package org.limewire.ui.swing.options;

import java.awt.Component;
import java.awt.Cursor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EventObject;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;

import net.miginfocom.swing.MigLayout;

import org.jdesktop.swingx.JXTable;
import org.limewire.core.api.spam.SpamManager;
import org.limewire.core.settings.FilterSettings;
import org.limewire.io.IP;
import org.limewire.ui.swing.components.MultiLineLabel;
import org.limewire.ui.swing.components.TextFieldClipboardControl;
import org.limewire.ui.swing.util.BackgroundExecutorService;
import org.limewire.ui.swing.util.FontUtils;
import org.limewire.ui.swing.util.I18n;

import com.google.inject.Inject;

/**
 * Filtering Option View.
 */
public class FilteringOptionPanel extends OptionPanel {
    
    private SpamManager spamManager;
    
    private BlockHostsPanel blockHostPanel;
    private AllowHostsPanel allowHostsPanel;
    
    @Inject
    public FilteringOptionPanel(SpamManager spamManager) {
        super();
        this.spamManager = spamManager;
        
        setLayout(new MigLayout("insets 15, fillx, wrap"));
        setOpaque(false);
        
        add(getBlockHostsPanel(), "pushx, growx");
        add(getAllowHostsPanel(), "pushx, growx");
    }
    
    private OptionPanel getBlockHostsPanel() {
        if(blockHostPanel == null) {
            blockHostPanel = new BlockHostsPanel();
            
        }
        
        return blockHostPanel;
    }
    
    private OptionPanel getAllowHostsPanel() {
        if(allowHostsPanel == null) {
            allowHostsPanel = new AllowHostsPanel();
            
        }
        
        return allowHostsPanel;
    }
    
    @Override
    void setOptionTabItem(OptionTabItem tab) {
        super.setOptionTabItem(tab);
        getAllowHostsPanel().setOptionTabItem(tab);
        getBlockHostsPanel().setOptionTabItem(tab);
    }
    
    @Override
    ApplyOptionResult applyOptions() {
        ApplyOptionResult result = null;
        
        result = getBlockHostsPanel().applyOptions();
        if (result.isSuccessful())
            result.applyResult(getAllowHostsPanel().applyOptions());
       
        return result;
    }
    
    @Override
    boolean hasChanged() {
        return getBlockHostsPanel().hasChanged() || getAllowHostsPanel().hasChanged();
    }
    
    @Override
    public void initOptions() {
        getBlockHostsPanel().initOptions();
        getAllowHostsPanel().initOptions();
    }
    
    private class BlockHostsPanel extends OptionPanel {
    
        private JTextField addressTextField;
        private JButton addButton;
        private FilteringTable filterTable;
        private JCheckBox backListCheckBox;
        private String description = I18n.tr("Use WireShare's blacklist to protect you from harmful people");
        
        public BlockHostsPanel() {
            super(I18n.tr("Block Hosts"));
            
            setLayout(new MigLayout("gapy 10", "[sg 1][sg 1][]", ""));
            
            addressTextField = new JTextField(26);
            TextFieldClipboardControl.install(addressTextField);
            addButton = new JButton(I18n.tr("Add Address"));
            filterTable = new FilteringTable();
            backListCheckBox = new JCheckBox();
            backListCheckBox.setOpaque(false);
            addButton.addActionListener(new AddAction(addressTextField, filterTable));
            
            add(new JLabel("<html>"+I18n.tr("Block contact with specific people by adding their IP address")+"</html>"), "span, growx, wrap");
            add(addressTextField, "gapright 10");
            add(addButton, "wrap");
            add(new JScrollPane(filterTable), "growx, span 2, wrap");
            add(backListCheckBox, "span, split");
            add(new MultiLineLabel(description, AdvancedOptionPanel.MULTI_LINE_LABEL_WIDTH), "span, growx");
        }
        
        @Override
        ApplyOptionResult applyOptions() {
            List<String> list = filterTable.getFilterModel().getModel();

            FilterSettings.USE_NETWORK_FILTER.setValue(backListCheckBox.isSelected());
            FilterSettings.BLACK_LISTED_IP_ADDRESSES.set(list.toArray(new String[list.size()]));
            BackgroundExecutorService.execute(new Runnable() {
                @Override
                public void run() {
                    spamManager.reloadIPFilter();
                }
            });
            return new ApplyOptionResult(false, true);
        }
    
        @Override
        boolean hasChanged() {
            List model = Arrays.asList(FilterSettings.BLACK_LISTED_IP_ADDRESSES.get());
            return backListCheckBox.isSelected() != FilterSettings.USE_NETWORK_FILTER.getValue()
                    || !model.equals(filterTable.getFilterModel().getModel());
        }
    
        @Override
        public void initOptions() {
            String[] bannedIps = FilterSettings.BLACK_LISTED_IP_ADDRESSES.get();           
            FilterModel model = new FilterModel(new ArrayList<String>(Arrays.asList(bannedIps)));
            filterTable.setModel(model);
            
            backListCheckBox.setSelected(FilterSettings.USE_NETWORK_FILTER.getValue());
        }
    }
    
    private class AllowHostsPanel extends OptionPanel {
    
        private JTextField addressTextField;
        private JButton addButton;
        private FilteringTable filterTable;
        
        private final String description = I18n.tr("Override the block list and allow specific people by adding their IP address");
        
        public AllowHostsPanel() {
            super(I18n.tr("Allow Hosts"));
            
            setLayout(new MigLayout("gapy 10", "[sg 1][sg 1][]", ""));
            
            addressTextField = new JTextField(26);
            TextFieldClipboardControl.install(addressTextField);
            addButton = new JButton(I18n.tr("Add Address"));
            filterTable = new FilteringTable();
            addButton.addActionListener(new AddAction(addressTextField, filterTable));
            
            add(new MultiLineLabel(description, AdvancedOptionPanel.MULTI_LINE_LABEL_WIDTH), "span, growx, wrap");
            add(addressTextField, "gapright 10");
            add(addButton, "wrap");
            add(new JScrollPane(filterTable), "growx, span 2");
        }
        
        @Override
        ApplyOptionResult applyOptions() {
            List<String> list = filterTable.getFilterModel().getModel();

            FilterSettings.WHITE_LISTED_IP_ADDRESSES.set(list.toArray(new String[list.size()]));
            BackgroundExecutorService.execute(new Runnable() {
                @Override
                public void run() {
                    spamManager.reloadIPFilter();
                }
            });
            return new ApplyOptionResult(false, true);
        }
    
        @Override
        boolean hasChanged() {
            List model = Arrays.asList(FilterSettings.WHITE_LISTED_IP_ADDRESSES.get());
            return !model.equals(filterTable.getFilterModel().getModel());
        }
    
        @Override
        public void initOptions() {
            String[] allowedIps = FilterSettings.WHITE_LISTED_IP_ADDRESSES.get();
            FilterModel model = new FilterModel(new ArrayList<String>(Arrays.asList(allowedIps)));
            filterTable.setModel(model);
        }
    }
    
    private class FilteringTable extends JXTable {
        
        private FilterModel model;
        
        public FilteringTable() {   
            setTableHeader(null);
            setShowGrid(false);
            setSelectionMode(0);
            setSelectionBackground(getBackground());
            setSelectionForeground(getForeground());
            
            addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {//adding this to editor messes up popups

                    int col = columnAtPoint(e.getPoint());
                    int row = rowAtPoint(e.getPoint());

                    if (row >= 0 && col >= 0) {
                        TableCellEditor editor = getCellEditor();
                        if (editor != null) {
                            // force update editor colors
                            prepareEditor(editor, row, col);
                            // editor.repaint() takes about a second to show sometimes
                            repaint();
                        }
                    }
                }
                
            });
        }
        
        public void setModel(FilterModel model) {
            super.setModel(model);
            this.model = model;
            getColumn(1).setCellRenderer(new RemoveButtonRenderer(this));    
            getColumn(1).setCellEditor(new RemoveButtonRenderer(this)); 
        }
        
        public FilterModel getFilterModel() {
            return model;
        }
        
        public void addIp(String ip) {
            if(model != null) {
                model.addIP(ip);
            }
        }
        
        @Override
        public boolean isCellEditable(int row, int col) {
            if (row >= getRowCount() || col >= getColumnCount() || row < 0 || col < 0) {
                return false;
            }
            return getColumnModel().getColumn(col).getCellEditor() != null;
        }
    }
    
    private class RemoveButtonRenderer extends JButton implements TableCellRenderer, TableCellEditor {
        private final List<CellEditorListener> listeners = new ArrayList<CellEditorListener>();
        
        public RemoveButtonRenderer(final FilteringTable table) {
            // lower case since hyperlink
            super(I18n.tr("remove"));
            
            setBorder(BorderFactory.createEmptyBorder());
            setContentAreaFilled(false);
            FontUtils.underline(this);
            setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));

            addActionListener(new ActionListener(){
                @Override
                public void actionPerformed(ActionEvent e) {
                    table.getFilterModel().removeIP(table.getSelectedRow());
                    cancelCellEditing();
                    table.repaint();
                }
            });
        }

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value,
                boolean isSelected, boolean hasFocus, int row, int column) {
            return this;
        }

        @Override
        public Component getTableCellEditorComponent(JTable table, Object value,
                boolean isSelected, int row, int column) {
            return this;
        }

        @Override
        public void cancelCellEditing() {
            synchronized (listeners) {
                for (int i=0, N=listeners.size(); i<N; i++) {
                    listeners.get(i).editingCanceled(new ChangeEvent(this));
                }
            }
        }

        @Override
        public Object getCellEditorValue() {
            return null;
        }

        @Override
        public boolean isCellEditable(EventObject anEvent) {
            return true;
        }

        @Override
        public void removeCellEditorListener(CellEditorListener lis) {
            synchronized (listeners) {
                if (listeners.contains(lis)) listeners.remove(lis);
            }
        }
        
        @Override
        public void addCellEditorListener(CellEditorListener lis) {
            synchronized (listeners) {
                if (!listeners.contains(lis)) listeners.add(lis);
            }
        }

        @Override
        public boolean shouldSelectCell(EventObject anEvent) {
            return true;
        }

        @Override
        public boolean stopCellEditing() {
            cancelCellEditing();
            return true;
        }
    }
    
    private class FilterModel extends AbstractTableModel {

        private List<String> filters;
        
        public FilterModel(List<String> filters) {
            this.filters = filters;
        }

        @Override
        public int getColumnCount() {
            return 2;
        }

        @Override
        public int getRowCount() {
            return filters.size();
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            return filters.get(rowIndex);
        }
        
        public List<String> getModel() {
            return filters;
        }

        public void addIP(String ip) {
            if(!filters.contains(ip))
                filters.add(ip);
            repaint();
        }
        
        public void removeIP(int index) {
            if(index < 0 || index >= filters.size())
                return;
            filters.remove(index);
        }
    }
    
    public static class AddAction implements ActionListener {

        private JTextField textField;
        private FilteringTable table;
        
        public AddAction(JTextField textField, FilteringTable table) {
            this.table = table;
            this.textField = textField;
        }
        
        @Override
        public void actionPerformed(ActionEvent e) {
            if(textField.getText() != null && textField.getText().length() > 0) {
                try {
                    new IP(textField.getText());
                    table.addIp(textField.getText());
                    table.invalidate();
                } catch(IllegalArgumentException iae) {
                }
                textField.setText("");
            }
        }
    }

}