
package usda.weru.util.wepsFileChooser2;

import de.schlichtherle.truezip.file.TFile;
import java.awt.Container;
import java.awt.Cursor;
import static java.awt.EventQueue.invokeLater;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import javax.swing.ImageIcon;
import javax.swing.JTable;
import javax.swing.RowSorter;
import javax.swing.SwingWorker;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import usda.weru.remoteDataAccess.remoteFiles.RemoteFile;
import usda.weru.remoteDataAccess.remoteFiles.WepsBaseFile;
import usda.weru.util.wepsFileChooser2.WepsFileChooser2.SelectionType;

/**
 *
 * @author mhaas
 */
public class WfcFileListTable extends JTable implements ListSelectionListener, MouseListener, PropertyChangeListener {
    
    public enum showFileNameType {
        Init,
        Mouse
    };
    
    static final long serialVersionUID = 1L;
    
    public static String propChangeCmdStrFileSected = "WFC_FileListTable_file_selected";
    public static String propChangeCmdSetFilename = "WFC_FileListTable_set_filename";
    public static String singleClickSelect = "SingleClickSelect";
    public static String doubleClickSelect = "DoubleClickSelect";
    
    static int colIcon = 0;
    static int colName = 1;
    static int colType = 2;
    static int colDate = 3;
    static int colSize = 4;
    
    protected final String colIconStr = "icon";
    protected final String colNameStr = "name";
    protected final String colTypeStr = "type";
    protected final String colDateStr = "date";
    protected final String colSizeStr = "size (bytes)";

    File displayingDir;
    boolean isDoubleClick;

    boolean isRemote = false;
    
    boolean showDetails;
    TableColumn sizeCol;
    TableColumn dateCol;
    
    protected java.io.FileFilter fileFilter;
    protected SelectionType selectionMode;
    
    public WfcFileListTable () {
        super();
        
        getColumnModel().getSelectionModel().addListSelectionListener(this);
        displayingDir = null;
        
        isDoubleClick = false;
        addMouseListener(this);
        
        fileFilter = null;
    }

    public void setup () {
        getColumn(colIconStr).setHeaderValue("");
        dateCol = getColumn(colDateStr);
        sizeCol = getColumn(colSizeStr);
        showDetails(false);
    }
    protected void setupRowSorting() {
        RowSorter<? extends TableModel> sort;
        TableRowSorter<? extends TableModel> tsort;
                
        setAutoCreateRowSorter(true);
        
        if (showDetails) {
            // Need to add a sorter for the size col (only when showDetails is true)
            // because size is a long and no default long comparator
            sort = getRowSorter();
            if (sort instanceof TableRowSorter) {
                tsort = (TableRowSorter<? extends TableModel>)sort;

                Comparator<Long> comparatorLong = new Comparator<Long>() {
                    public int compare(Long i1, Long i2) {
                        return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
                    }
                };
                tsort.setComparator(colSize, comparatorLong);
            }
        }
        
        getRowSorter().toggleSortOrder(1);
    }
    
    public void displayDir (File dirIn) {
        DefaultTableModel tableModel = (DefaultTableModel)this.getModel();
        displayingDir = dirIn;
        
        tableModel.setRowCount(0);
        if (dirIn != null) {           
            File[] files = dirIn.listFiles(fileFilter);
            if (files != null) {
                for (File f : files) {
                    if(directoryFilter(fileFilter)&& f.isDirectory() || !directoryFilter(fileFilter)){
                    ArrayList<Object> rowData = new ArrayList<>();
                    // rowData.add(colIcon,"");
                    // This adds the icons into the icon row.
                    URL uf = this.getClass().getResource("/usda/weru/resources/new.gif");
                    ImageIcon fi = new ImageIcon (uf);
                    URL ud = this.getClass().getResource("/usda/weru/resources/folder.gif");
                    ImageIcon di = new ImageIcon (ud);
                    rowData.add(colIcon,f.isDirectory() ? di : fi);
                    
                    
                    rowData.add(colName,f.getName());
                    rowData.add(colType,f.isDirectory() ? "dir" : "file");
                    if (f.getName().endsWith(".zip")) {
                        rowData.add(colType,"zip");
                    }
                    // if (showDetails) { // old code for adding data + size.
                    Date d = new Date (f.lastModified());
                    SimpleDateFormat df = new SimpleDateFormat ("MM/dd/yyyy HH:mm:ss a");
                    String s = df.format(d);
                    rowData.add(colDate,s);

                    // Need to set to null for comparator to work.
                    // java doesn't get real size of directories, so the sorting
                    // looks completely wrong, better to sort by file size only.
                    if (!f.isDirectory()) { 
                        rowData.add(colSize,f.length());
                    } else { 
                        rowData.add(colSize, null );
                    } 
                    if(allowedRow(f)){
                        tableModel.addRow(rowData.toArray());
                    }
                    }
                }
        
                setupRowSorting();
            }
            fireShowFilename(showFileNameType.Init);
        }
    }
    
    private boolean directoryFilter(java.io.FileFilter filt){
        if(filt == null){
            return false;
        }
        if(filt.equals(WepsFileTypes2.RunDirNoFiles.getFileFilter())){
            return true;
        }
        return false;
    }
    
    protected void setSelectType(SelectionType selType){
        selectionMode = selType;
    }
    
    private boolean allowedRow(File f){
        if(selectionMode == SelectionType.FILES_AND_DIRECTORIES){
            return true;
        }
        if(selectionMode == SelectionType.DIRECTORIES_ONLY && f.isDirectory()){
            return true;
        }
        if(selectionMode == SelectionType.FILES_ONLY && f.isFile()){
            return true;
        }
        return false;
    }
    
    public void showDetails (boolean show) {
        showDetails = show;
        if (show) {
           addColumn(dateCol);
           addColumn(sizeCol);
           // After adding new cols, set "name" col "very" big, to force the new cols to their mins.
           // This makes the original display at their mins, but leaves them sizeable if the user chooses
           getColumn(colNameStr).setPreferredWidth(600);
        } else {
           removeColumn(dateCol);
           removeColumn(sizeCol);
        }
        if (displayingDir != null) {
            displayDir (displayingDir);
        }
    }
    
    public void doSelect () {
        int row = getSelectedRow();
        clearSelection();
        setRowSelectionInterval(row, row);
    }
    
    public void doRefreshAndSelectDir (File newDir) {
        fireClearFilename(showFileNameType.Mouse);
        displayDir(newDir);
        int row = findRowVal(newDir);
        if (row >= 0) {
            setRowSelectionInterval(row, row);
            fireAddDirToCombo (newDir);
        }
    }
    
    
    public void doSelectFile (File newFile) {
        displayDir(newFile.getParentFile());
        int row = findRowVal(newFile);
        if (row >= 0) {
            setRowSelectionInterval(row, row);
        }
    }
    
    protected int findRowVal (File searchFile) {
        int retVal = -1;
        DefaultTableModel tableModel = ((DefaultTableModel)this.getModel());
        
        String name = searchFile.getName();
        for (int i = 0; i < tableModel.getRowCount(); i++ ) {
            if (getValueTranslated(i).contentEquals(name)) {
                retVal = i;
                break;
            }
        }
        return retVal;
    }
    
    public File getCurrentSelection () {
        File ret = null;
        int row = getSelectedRow();
        
        if (row == -1 && getRowCount() > 0) {
                setRowSelectionInterval(0, 0);
                row = getSelectedRow();
        }
        if (row >= 0) {
            ret = getFileAtRow(row);
        }
        return ret;
    }
    
    public File getDisplayingDir () {
        return displayingDir;
    }
    
    public File[] getCurrentSelections () {
        int [] rows = getSelectedRows();
        ArrayList<File> files = new ArrayList<>();
        for (int i : rows) {
            files.add( WepsBaseFile.newOfParentType (displayingDir,getValueTranslated(i) ) );
        }
        
        return files.toArray(new File[0]);
    }
    
    protected File getFileAtRow (int row) {
        if (row >= 0) {
            return WepsBaseFile.newOfParentType (displayingDir,getValueTranslated(row) );
        }
        return new WepsBaseFile ("");
    }
    
    protected String getValueTranslated (int row) {
        // If the table has been sorted, need to translate row nums to get actual data...
        return (String)((DefaultTableModel)this.getModel()).getValueAt(convertRowIndexToModel(row), colName);
    }
    
    protected void fireAddDirToCombo (File dir) {
        fireChange (WfcPathComboBox.propChangeCmdStrUpdatePath, dir);
    }
    
    protected void fireSelectFile (File file) {
        fireChange (WfcFileListTable.propChangeCmdStrFileSected,  file);
    }
    
    protected void fireShowFilename (showFileNameType type) {
        fireChange (WfcFileListTable.propChangeCmdSetFilename, type, getCurrentSelection());
    }
    
    protected void fireClearFilename (showFileNameType type) {
        fireChange (WfcFileListTable.propChangeCmdSetFilename, type, new File("") ); 
    }
    
    /**
     * This fires on single clicking on in item in the pane. It is used in
     * the main file chooser class to set the text string displayed.
     * @param type 
     */
    protected void fireSelectSingleClick(showFileNameType type) {
        fireChange (WfcFileListTable.singleClickSelect, type, getCurrentSelection());
    }
    
    protected void fireSelectDoubleClick(showFileNameType type) {
        fireChange (WfcFileListTable.doubleClickSelect, type, getCurrentSelection());
    }
    
    protected void fireChange (String prop, Object newItem) {
        fireChange (prop, "", newItem);
    }
    protected void fireChange (String prop, Object oldItem, Object newItem) {
        invokeLater(
            new Runnable () {
                @Override
                public void run() {
                    firePropertyChange(prop, oldItem, newItem);
                }
            }
        );
    }
    
    
    @Override
    public void valueChanged(ListSelectionEvent e) {
        super.valueChanged(e);
        if (isDoubleClick) {
            int j = e.getFirstIndex();
            
            File file = WepsBaseFile.newOfParentType (displayingDir,getValueTranslated(j) );
            
            if (file.isDirectory()) {
                fireAddDirToCombo (file);
            } else {
                fireSelectFile(file);
            }
            isDoubleClick = false;
        }
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        if (e.getClickCount() == 2) {
            isDoubleClick = true;
            doSelect();
            fireSelectDoubleClick(showFileNameType.Mouse);
            fireClearFilename(showFileNameType.Mouse);
        } else {
            //isDoubleClick = false;
            fireShowFilename(showFileNameType.Mouse);
            fireSelectSingleClick(showFileNameType.Mouse);
        }
    }

    @Override
    public void mousePressed(MouseEvent e) {
    }

    @Override
    public void mouseReleased(MouseEvent e) {
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseExited(MouseEvent e) {
    }

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        if (evt.getPropertyName().equals(WfcPathComboBox.propChangeCmdStrPathChanged)) {
            File newFile = (File)evt.getNewValue(); //(no fixPath)
            //File newFile = Util.fixPath((File)evt.getNewValue()); //(fixPath) DEPRECATED
            if (newFile instanceof RemoteFile) {
                isRemote = true;
                //System.out.println("A remote file.");
                //System.out.println("Remote file path: " + newFile.getAbsolutePath());
                Container thisContainer = this.getParent().getParent().getParent(); // This table is buried, we want the Dialog
                thisContainer.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
                
                (new SwingWorker <Void, Void> () {
                    @Override
                    protected Void doInBackground() throws Exception {
                        // call listFiles here in background, if RemoteFile, 
                        // since this intializes all of the data in the internal structures.
                        // It will be called again in displayDir, but then all data
                        // will be pre-loaded from this call.
                        newFile.listFiles(fileFilter);
                        RemoteFile remote = (RemoteFile)newFile;
                        if(remote.isFile()) {
                            //System.out.println("File.");
                            File downloadedFile = remote.downloadFile();
                        } else {
                            //System.out.println("Directory.");
                        }
                        return null;
                    }        

                    @Override
                    protected void done() {
                       try {
                            displayDir(newFile);

                            thisContainer.setCursor(Cursor.getDefaultCursor());
                            if (getRowCount() > 0) {
                                setRowSelectionInterval(0, 0);
                            }
                            requestFocusInWindow();
                       } catch (Exception ignore) {
                       }
                   }
                }).execute();
            } else {
                isRemote = false;
                //System.out.println("Not a remote file.");
                //System.out.println("Non-remote file path: " + newFile.getAbsolutePath());
                String path = newFile.getAbsolutePath();
                if(path.contains(":")) {
                    path = path.substring(path.lastIndexOf(":") - 1); //remove duplicated path discrepancy
                }
                System.out.println("Non-remote file path: " + path);
                TFile newTFile = new TFile(path);
                displayDir(newTFile);

                if (getRowCount() > 0) {
                    setRowSelectionInterval(0, 0);
                }
                requestFocusInWindow();
            }

        } else if (evt.getPropertyName().equals(WfcFileTypeComboBox.propChangeCmdStrFilterChanged)) {
            fileFilter = (java.io.FileFilter) evt.getNewValue();
            refreshFilterSelected(fileFilter);
            
            if (displayingDir != null) {
                displayDir (displayingDir);
            }
        }
    }
    
    private void refreshFilterSelected(java.io.FileFilter filefilt){
        if(new WepsFileFilter().toString().equals(fileFilter.toString())){ //All files filter
                setSelectType(SelectionType.FILES_AND_DIRECTORIES);
        }
        if(fileFilter.toString().contains("*.wpj")){
                setSelectType(SelectionType.DIRECTORIES_ONLY);
        }
        if(fileFilter.toString().contains("*.wjr")){
                setSelectType(SelectionType.DIRECTORIES_ONLY);
        }
    }
}
