package com.muwire.gui

import com.google.common.collect.Sets
import com.muwire.core.Persona
import griffon.core.GriffonApplication

import javax.inject.Inject
import javax.swing.AbstractAction
import javax.swing.Action
import javax.swing.JComponent
import javax.swing.JSplitPane
import javax.swing.KeyStroke
import javax.swing.tree.DefaultMutableTreeNode
import java.awt.event.ActionEvent
import java.awt.event.KeyEvent

import static com.muwire.gui.Translator.trans
import griffon.core.artifact.GriffonView
import griffon.inject.MVCMember
import griffon.metadata.ArtifactProviderFor

import javax.swing.JCheckBox
import javax.swing.JLabel
import javax.swing.JMenuItem
import javax.swing.JPopupMenu
import javax.swing.JTable
import javax.swing.JTextArea
import javax.swing.JTree
import javax.swing.ListSelectionModel
import javax.swing.SwingConstants
import javax.swing.table.DefaultTableCellRenderer
import javax.swing.tree.TreePath

import com.muwire.core.collections.FileCollection
import com.muwire.core.collections.FileCollectionItem

import java.awt.BorderLayout
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent

import javax.annotation.Nonnull

@ArtifactProviderFor(GriffonView)
class CollectionTabView {
    @MVCMember @Nonnull
    FactoryBuilderSupport builder
    @MVCMember @Nonnull
    CollectionTabModel model
    @MVCMember @Nonnull
    CollectionTabController controller
    @Inject
    GriffonApplication application

    def parent
    JComponent p
    
    JTable collectionsTable
    def lastCollectionsTableSortEvent
    def itemsPanel
    JTable itemsTable
    def lastItemsTableSortEvent
    JTree itemsTree
    JCheckBox downloadSequentiallyCollectionCheckbox
    JCheckBox downloadSequentiallyItemCheckbox
    
    void initUI() {
        int rowHeight = application.context.get("row-height")
        int treeRowHeight = application.context.get("tree-row-height")
        p = builder.panel {
            gridLayout(rows : 1, cols: 1)
            splitPane(orientation: JSplitPane.VERTICAL_SPLIT, continuousLayout : true, dividerLocation: 300 ) {
                panel {
                    borderLayout()
                    panel(constraints: BorderLayout.NORTH) {
                        borderLayout()
                        panel(constraints: BorderLayout.CENTER) {
                            label(text: trans("STATUS") + ":")
                            label(text: bind { trans(model.status.name()) })
                        }
                        panel(constraints: BorderLayout.EAST) {
                            button(text: trans("COPY_FULL_ID"), toolTipText: trans("TOOLTIP_COPY_SENDER_FULL_ID"), copyIdAction)
                        }
                    }
                    scrollPane(constraints: BorderLayout.CENTER, border: etchedBorder()) {
                        collectionsTable = table(id: "collections-table", autoCreateRowSorter: true, rowHeight: rowHeight) {
                            tableModel(list: model.collections) {
                                closureColumn(header: trans("NAME"), type: String, read: { HTMLSanitizer.sanitize(it.name) })
                                closureColumn(header: trans("AUTHOR"), type: Persona, read: { it.author })
                                closureColumn(header: trans("COLLECTION_TOTAL_FILES"), type: Integer, read: { it.numFiles() })
                                closureColumn(header: trans("COLLECTION_TOTAL_SIZE"), type: Long, read: { it.totalSize() })
                                closureColumn(header: trans("COMMENT"), type: Boolean, read: { it.comment != "" })
                                closureColumn(header: trans("CREATED"), type: Long, read: { it.timestamp })
                            }
                        }
                    }
                    panel(constraints: BorderLayout.SOUTH) {
                        gridLayout(rows: 1, cols: 3)
                        panel {}
                        panel {
                            button(text: trans("COLLECTION_DOWNLOAD"), toolTipText: trans("TOOLTIP_DOWNLOAD_FULL_COLLECTION"),
                                    enabled: bind { model.downloadCollectionButtonEnabled }, downloadCollectionAction)
                            button(text: trans("VIEW_COMMENT"), toolTipText: trans("TOOLTIP_VIEW_COLLECTION_COMMENT"),
                                    enabled: bind {model.viewCollectionCommentButtonEnabled}, viewCollectionCommentAction)
                        }
                        panel {
                            label(text: trans("DOWNLOAD_SEQUENTIALLY"), toolTipText: trans("TOOLTIP_DOWNLOAD_SEQUENTIALLY"))
                            downloadSequentiallyCollectionCheckbox = checkBox(selected: bind { model.downloadSequentiallyCollection },
                                    enabled : bind {model.downloadCollectionButtonEnabled })
                        }
                    }
                }
                panel {
                    borderLayout()
                    panel(constraints: BorderLayout.NORTH) {
                        label(text: trans("FILES"))
                    }
                    itemsPanel = panel(constraints: BorderLayout.CENTER) {
                        cardLayout()
                        panel(constraints: "table") {
                            borderLayout()
                            scrollPane(constraints: BorderLayout.CENTER, border: etchedBorder()) {
                                itemsTable = table(id: "items-table", autoCreateRowSorter: true, rowHeight: rowHeight) {
                                    tableModel(list: model.items) {
                                        closureColumn(header: trans("NAME"), type: String, read: {
                                            HTMLSanitizer.sanitize(String.join(File.separator, it.pathElements))
                                        })
                                        closureColumn(header: trans("SIZE"), type: Long, read: { it.length })
                                        closureColumn(header: trans("COMMENT"), type: Boolean, read: { it.comment != "" })
                                    }
                                }
                            }
                        }
                        panel(constraints: "tree") {
                            borderLayout()
                            scrollPane(constraints: BorderLayout.CENTER, border: etchedBorder()) {
                                itemsTree = new JTree(model.fileTreeModel)
                                itemsTree.setCellRenderer(new PathTreeRenderer())
                                tree(id: "items-tree", rowHeight: treeRowHeight, rootVisible: false, expandsSelectedPaths: true, itemsTree)
                            }
                        }
                    }
                    panel(constraints: BorderLayout.SOUTH) {
                        gridLayout(rows: 1, cols: 3)
                        panel {
                            buttonGroup(id: "viewType")
                            radioButton(text: trans("TREE"), toolTipText: trans("TOOLTIP_RESULT_VIEW_TREE"),
                                    selected: true, buttonGroup: viewType, actionPerformed: showTree)
                            radioButton(text: trans("TABLE"), toolTipText: trans("TOOLTIP_RESULT_VIEW_TABLE"),
                                    selected: false, buttonGroup: viewType, actionPerformed: showTable)
                        }
                        panel {
                            button(text: trans("DOWNLOAD"), toolTipText: trans("TOOLTIP_DOWNLOAD_FILE"),
                                    enabled: bind { model.downloadItemButtonEnabled }, downloadAction)
                            button(text: trans("VIEW_COMMENT"), toolTipText: trans("TOOLTIP_VIEW_RESULT_COMMENT"),
                                    enabled: bind { model.viewCommentButtonEnabled }, viewCommentAction)
                        }
                        panel {
                            label(text: trans("DOWNLOAD_SEQUENTIALLY"), toolTipText: trans("TOOLTIP_DOWNLOAD_SEQUENTIALLY"))
                            downloadSequentiallyItemCheckbox = checkBox(selected: bind { model.downloadSequentiallyItem },
                                    enabled: bind { model.downloadItemButtonEnabled })
                        }
                    }
                }
            }
        }
        
        p.registerKeyboardAction(closeTab,
                KeyStroke.getKeyStroke(KeyEvent.VK_W, KeyEvent.CTRL_DOWN_MASK),
                JComponent.WHEN_IN_FOCUSED_WINDOW)

        Action downloadAction = new AbstractAction() {
            @Override
            void actionPerformed(ActionEvent e) {
                controller.download()
            }
        }
        ["items-tree", "items-table"].each {
            JComponent resultsComponent = builder.getVariable(it)
            resultsComponent.registerKeyboardAction(downloadAction,
                    KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),
                    JComponent.WHEN_FOCUSED)
        }
        
        Action downloadCollectionAction = new AbstractAction() {
            @Override
            void actionPerformed(ActionEvent e) {
                controller.downloadCollection()
            }
        }
        collectionsTable.registerKeyboardAction(downloadCollectionAction,
                KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),
                JComponent.WHEN_FOCUSED)
    }
    
    boolean isSequentialCollection() {
        downloadSequentiallyCollectionCheckbox.model.isSelected()
    }
    
    boolean isSequentialItem() {
        downloadSequentiallyItemCheckbox.model.isSelected()
    }
    
    void mvcGroupInit(Map<String, String> args) {
        def mainFrameGroup = application.mvcGroupManager.findGroup("MainFrame")
        mainFrameGroup.model.collections.add(model.uuid.toString())
        parent = mainFrameGroup.view.builder.getVariable("result-tabs")
        parent.addTab(model.uuid.toString(), p)
        
        int index = parent.indexOfComponent(p)
        parent.setSelectedIndex(index)
     
        def tabPanel
        builder.with {
            tabPanel = panel {
                borderLayout()
                panel {
                    label(text : model.fileName, constraints : BorderLayout.CENTER)
                }
                button(icon : imageIcon("/close_tab.png"), preferredSize : [20,20], constraints : BorderLayout.EAST, // TODO: in osx is probably WEST
                    actionPerformed : closeTab )
            }
        }

        parent.setTabComponentAt(index, tabPanel)
        mainFrameGroup.view.showSearchWindow.call()
        
        // collections table
        TableUtil.packColumns(collectionsTable, Sets.newHashSet(0,1,5))
        TableUtil.nicknameColumn(collectionsTable, 1)
        TableUtil.dateColumn(collectionsTable, 5)
        def centerRenderer = new DefaultTableCellRenderer()
        centerRenderer.setHorizontalAlignment(JLabel.CENTER)
        collectionsTable.setDefaultRenderer(Integer.class, centerRenderer)
        collectionsTable.setDefaultRenderer(Persona.class, new PersonaCellRenderer(application.context.get("ui-settings")))
        
        collectionsTable.columnModel.getColumn(3).setCellRenderer(new SizeRenderer())
        collectionsTable.columnModel.getColumn(5).setCellRenderer(new DateRenderer())
        
        collectionsTable.rowSorter.setComparator(1, new PersonaComparator())
        collectionsTable.rowSorter.addRowSorterListener({evt -> lastCollectionsTableSortEvent = evt})
        collectionsTable.rowSorter.setSortsOnUpdates(true)
        
        def selectionModel = collectionsTable.getSelectionModel()
        selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
        selectionModel.addListSelectionListener({
            int row = selectedCollection()
            if (row < 0)
                return
            model.downloadCollectionButtonEnabled = true
            FileCollection selected = model.collections.get(row)
            model.viewCollectionCommentButtonEnabled = selected.getComment() != ""
            
            model.items.clear()
            model.items.addAll(selected.files)
            itemsTable.model.fireTableDataChanged()
            
            model.root.removeAllChildren()
            def newNode = new DefaultMutableTreeNode()
            TreeUtil.copy(newNode, selected.tree.root)
            model.root.add newNode
            itemsTree.model.nodeStructureChanged(model.root)
            TreeUtil.expand(itemsTree)
        })
        
        collectionsTable.addMouseListener(new MouseAdapter() {
            public void mouseReleased(MouseEvent e) {
                if (e.isPopupTrigger())
                    showCollectionMenu(e)
            }
            public void mousePressed(MouseEvent e) {
                if (e.isPopupTrigger())
                    showCollectionMenu(e)
            }
        })
        
        
        // items table
        TableUtil.packColumns(itemsTable, Sets.newHashSet(0, 1))
        TableUtil.sizeColumn(itemsTable, 1)
        itemsTable.setDefaultRenderer(Long.class, new SizeRenderer())
        itemsTable.rowSorter.addRowSorterListener({evt -> lastItemsTableSortEvent = evt})
        itemsTable.rowSorter.setSortsOnUpdates(true)
        selectionModel = itemsTable.getSelectionModel()
        selectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
        selectionModel.addListSelectionListener({
            List<FileCollectionItem> items = selectedItems()
            if (items.isEmpty()) {
                model.viewCommentButtonEnabled = false
                model.downloadItemButtonEnabled = false
                return
            }
            model.downloadItemButtonEnabled = true
            model.viewCommentButtonEnabled = items.size()== 1 && items.get(0).comment != ""
        })
        
        // items tree
        itemsTree.addTreeSelectionListener({
            List<FileCollectionItem> items = selectedItems()
            if (items.isEmpty()) {
                model.viewCommentButtonEnabled = false
                model.downloadItemButtonEnabled = false
                return
            }
            model.downloadItemButtonEnabled = true
            model.viewCommentButtonEnabled = items.size()== 1 && items.get(0).comment != ""
        })
        
        // popup
        def itemsMouseListener = new MouseAdapter() {
            public void mouseReleased(MouseEvent e) {
                if (e.isPopupTrigger())
                    showItemsMenu(e)
            }
            public void mousePressed(MouseEvent e) {
                if (e.isPopupTrigger())
                    showItemsMenu(e)
            }
        }
        itemsTable.addMouseListener(itemsMouseListener)
        itemsTree.addMouseListener(itemsMouseListener)
        
        
        showTree.call()
    }
    
    int selectedCollection() {
        int row = collectionsTable.getSelectedRow()
        if (row < 0)
            return row
        if (lastCollectionsTableSortEvent != null) 
            row = collectionsTable.rowSorter.convertRowIndexToModel(row)
        
        return row
    }
    
    List<FileCollectionItem> selectedItems() {
        if (!model.treeVisible) {
            int [] rows = itemsTable.getSelectedRows()
            if (rows.length == 0)
                return Collections.emptyList()
            if (lastItemsTableSortEvent != null) {
                for (int i = 0; i < rows.length; i++)
                    rows[i] = itemsTable.rowSorter.convertRowIndexToModel(rows[i])
            }
            List<FileCollectionItem> rv = new ArrayList<>()
            for(int i = 0; i < rows.length; i++)
                rv.add(model.items.get(rows[i]))
            return rv
        } else {
            List<FileCollectionItem> rv = new ArrayList<>()
            for (TreePath path : itemsTree.getSelectionPaths())
                TreeUtil.getLeafs(path.getLastPathComponent(), rv)
            return rv
        }
    }
    
    def showTree = {
        model.treeVisible = true
        itemsPanel.getLayout().show(itemsPanel, "tree")
    }
    
    def showTable = {
        model.treeVisible = false
        itemsPanel.getLayout().show(itemsPanel, "table")
    }
    
    def closeTab = {
        def mainFrameGroup = application.mvcGroupManager.findGroup("MainFrame")
        mainFrameGroup.model.collections.remove(model.uuid.toString())
        
        int index = parent.indexOfTab(model.uuid.toString())
        parent.removeTabAt(index)
        mvcGroup.destroy()
    }
    
    private void showCollectionMenu(MouseEvent e) {
        if (!RightClickSupport.processRightClick(e))
            return
        int row = selectedCollection()
        if (row < 0)
            return
        JPopupMenu menu = new JPopupMenu()
        JMenuItem downloadCollection = new JMenuItem(trans("COLLECTION_DOWNLOAD"))
        downloadCollection.addActionListener({controller.downloadCollection()})
        menu.add(downloadCollection)
        
        if (model.viewCollectionCommentButtonEnabled) {
            JMenuItem viewCollectionComment = new JMenuItem(trans("VIEW_COMMENT"))
            viewCollectionComment.addActionListener({controller.viewCollectionComment()})
            menu.add(viewCollectionComment)
        }
        showPopupMenu(menu, e)
    }
    
    private void showItemsMenu(MouseEvent e) {
        if (!RightClickSupport.processRightClick(e))
            return
        List<FileCollectionItem> selected = selectedItems()
        if (selected.isEmpty())
            return
        
        JPopupMenu menu = new JPopupMenu()
        JMenuItem download = new JMenuItem(trans("DOWNLOAD"))
        download.addActionListener({controller.download()})
        menu.add(download)
        
        if (model.viewCommentButtonEnabled) {
            JMenuItem viewComment = new JMenuItem(trans("VIEW_COMMENT"))
            viewComment.addActionListener({controller.viewComment()})
            menu.add(viewComment)
        }
        
        showPopupMenu(menu, e)
    }
    
    private static void showPopupMenu(JPopupMenu menu, MouseEvent event) {
        menu.show(event.getComponent(), event.getX(), event.getY())
    }
}