package org.limewire.ui.swing.options;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;

import net.miginfocom.swing.MigLayout;

import org.jdesktop.swingx.JXTable;
import org.limewire.core.api.Category;
import org.limewire.core.api.file.CategoryManager;
import org.limewire.core.api.spam.SpamManager;
import org.limewire.core.settings.FilterSettings;
import org.limewire.core.settings.LibrarySettings;
import org.limewire.setting.Setting;
import org.limewire.ui.swing.action.AbstractAction;
import org.limewire.ui.swing.components.MultiLineLabel;
import org.limewire.ui.swing.components.TextFieldClipboardControl;
import org.limewire.ui.swing.options.actions.OKDialogAction;
import org.limewire.ui.swing.table.MouseableTable;
import org.limewire.ui.swing.util.BackgroundExecutorService;
import org.limewire.ui.swing.util.I18n;
import org.limewire.ui.swing.util.IconManager;
import org.limewire.util.OSUtils;

import ca.odell.glazedlists.BasicEventList;
import ca.odell.glazedlists.CompositeList;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.gui.TableFormat;
import ca.odell.glazedlists.swing.DefaultEventTableModel;

import com.google.inject.Inject;

/**
 * Creates a table to manage which file extensions will not show up in search results.
 */
public class FilterFileExtensionsOptionPanel extends AbstractFilterOptionPanel {
    
    private final SpamManager spamManager;
    private final CategoryManager categoryManager;
    private final UnsafeTypeOptionPanelStateManager settingStateManager;
    
    private JButton defaultButton;
    private JButton okButton;
    private JTextField keywordTextField;
    private JButton addKeywordButton;
    private JXTable filterTable;

    private final EventList<String> immutableProgramsList;
        
    @Inject
    public FilterFileExtensionsOptionPanel(IconManager iconManager, SpamManager spamManager,
            CategoryManager categoryManager,
            UnsafeTypeOptionPanelStateManager settingStateManager) {
        
        this.spamManager = spamManager;
        this.categoryManager = categoryManager;
        this.settingStateManager = settingStateManager;
        
        setLayout(new MigLayout("gapy 10, nogrid"));
        
        keywordTextField = new JTextField(30);
        addKeywordButton = new JButton(I18n.tr("Add Extension"));
        
        TextFieldClipboardControl.install(keywordTextField);
     
        CompositeList<String> allExtensions = new CompositeList<String>(eventList.getPublisher(),
                eventList.getReadWriteLock());
        allExtensions.addMemberList(eventList);

        immutableProgramsList = new BasicEventList<String>(eventList.getPublisher(),
                eventList.getReadWriteLock());
        allExtensions.addMemberList(immutableProgramsList);
        
                
        filterTable = new MouseableTable(new DefaultEventTableModel<String>(
                allExtensions, new FileFilterTableFormat(categoryManager, iconManager)));
        
        filterTable.setShowGrid(false, false);
        filterTable.setColumnSelectionAllowed(false);
        filterTable.setSelectionMode(0);
        TableColumn iconColumn = filterTable.getColumn(0); 
        iconColumn.setCellRenderer(new IconRenderer());
        iconColumn.setWidth(16);
        iconColumn.setMaxWidth(16);
        iconColumn.setMinWidth(16);
        
        TableColumn removeColumn = filterTable.getColumn(FileFilterTableFormat.columnNames.length-1);
        
        removeColumn.setCellRenderer(new RemoveButtonRenderer(filterTable));
        removeColumn.setCellEditor(new RemoveButtonRenderer(filterTable));
        
        okButton = new JButton(new OKDialogAction());
        addKeywordButton.addActionListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent e) {
                String text = keywordTextField.getText();
                
                if(text == null || text.trim().length() == 0) {
                    return;
                }
                
                text = text.trim();
                if(text.charAt(0) != '.') {
                    text = "." + text;
                }
                
                if(!eventList.contains(text) && !immutableProgramsList.contains(text)) {
                    eventList.add(text);
                }
                
                keywordTextField.setText("");
            }
        });
        
        defaultButton = new JButton(new DefaultAction());
        
        add(new MultiLineLabel(I18n.tr("WireShare will not show files with the following extensions in your search results"), 300), "span, wrap");
        add(keywordTextField, "gapright 10");
        add(addKeywordButton,"wrap");
        add(new JScrollPane(filterTable), "span 2, grow, wrap");
        
        add(defaultButton, "alignx left");
        add(okButton, "tag ok, alignx right");
    }
    
    @Override
    ApplyOptionResult applyOptions() {
        String[] values = eventList.toArray(new String[eventList.size()]);
        FilterSettings.BANNED_EXTENSIONS.set(values);
        BackgroundExecutorService.execute(new Runnable() {
            @Override
            public void run() {
                spamManager.adjustSpamFilters();
            }
        });
        return new ApplyOptionResult(false,true);
    }

    @Override
    boolean hasChanged() {
        List model = Arrays.asList(FilterSettings.BANNED_EXTENSIONS.get());
        String[] values = eventList.toArray(new String[eventList.size()]);
        
        return model.equals(new ArrayList<String>(Arrays.asList(values)));
    }

    private void updateItems() {
        eventList.clear();
        immutableProgramsList.clear();
    
        // Adds or removes the banned programs to list with non deletable elements to be appended at the end
        //  based on the allow programs setting.
        if (settingStateManager.getValue(LibrarySettings.ALLOW_PROGRAMS) == Boolean.FALSE) {
            for ( String extension : categoryManager.getExtensionsForCategory(Category.PROGRAM) ) {
                immutableProgramsList.add("." + extension);
            }
        }
        
        // Add all the banned extensions that are not already accounted for by 
        //  banned programs
        for ( String extension : FilterSettings.BANNED_EXTENSIONS.get() ) {
            if (!immutableProgramsList.contains(extension)) {
                eventList.add(extension);
            }
        }
    }
    
    @Override
    public void initOptions() {
        
        
        updateItems();
        settingStateManager.addSettingChangedListener(new OptionPanelStateManager.SettingChangedListener() {
            @Override
            public void settingChanged(Setting setting) {
                if (setting == LibrarySettings.ALLOW_PROGRAMS) {
                    updateItems();
                }
            }
        });
    }
    
    /**
     * Reverts the extensions not shown in search results to the default setting.
     */
    private class DefaultAction extends AbstractAction {
        public DefaultAction() {
            putValue(Action.NAME, I18n.tr("Use Default"));
            putValue(Action.SHORT_DESCRIPTION, I18n.tr("Revert to default settings"));
        }
        
        @Override
        public void actionPerformed(ActionEvent e) {
            FilterSettings.BANNED_EXTENSIONS.revertToDefault();
            updateItems();
        }
    }
    
    private static class FileFilterTableFormat implements TableFormat<String> {

        private final CategoryManager categoryManager;
        private final IconManager iconManager;
        
        final int ICON_COLUMN = 0;
        final int CATEGORY_COLUMN = 2;
        final int TYPE_COLUMN = 3;
        
        static final String[] columnNames = OSUtils.isWindows() ?
                new String[] {"", I18n.tr("Extension"), I18n.tr("Category"), I18n.tr("Type"), ""} 
              : new String[] {"", I18n.tr("Extension"), I18n.tr("Category"), ""};
        
        public FileFilterTableFormat(CategoryManager cagetoryManager, IconManager iconManager) {
            this.categoryManager = cagetoryManager;
            this.iconManager = iconManager;
        }
                
        @Override
        public Object getColumnValue(String baseObject, int column) {
            
            switch (column) {
                case ICON_COLUMN :
                    return iconManager.getIconForExtension(baseObject.substring(1));
            
                case CATEGORY_COLUMN :
                    return categoryManager.getCategoryForExtension(baseObject.substring(1));
            
                case TYPE_COLUMN :
                    if (OSUtils.isWindows()) {
                        return iconManager.getIconForExtension(baseObject.substring(1)).toString();
                    } // ELSE PASS-THROUGH
                    
                default:
                    return baseObject;
            }
        }

        @Override
        public int getColumnCount() {
            return columnNames.length;
        }

        @Override
        public String getColumnName(int column) {
            return columnNames[column];
        }
    }
    
    private static class IconRenderer implements TableCellRenderer {

        private final JLabel component = new JLabel(); 
        

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value,
                boolean isSelected, boolean hasFocus, int row, int column) {
            if (value == null) {
                return null;
            }
            component.setIcon((Icon) value);
            
            return component;
        }
    }
}
