package ru.kkey.ui;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.KeyStroke;
import javax.swing.SwingWorker;
import ru.kkey.core.FSSource;
import ru.kkey.core.FTPSource;
import ru.kkey.core.FileItem;
import ru.kkey.core.Source;
import ru.kkey.core.Utils.Pair;
import ru.kkey.core.ZipSource;
import ru.kkey.ui.menu.SelectMenuResult;
import ru.kkey.ui.preview.Preview;
import ru.kkey.ui.preview.PreviewRegistry;
/**
* Controller for the application window
*
* @author anstarovoyt
* @see FilesView
*/
public class FilesController
{
private static final String ENTER = "enter";
private static final String BACK_STRING = "/...";
private static final Logger logger = Logger.getAnonymousLogger();
private volatile Source fileSource = new FSSource("");
private final ArrayList<Pair<Source, FileItem>> prevSources = new ArrayList<>();
private final FilesView view;
public FilesController(FilesView view)
{
this.view = view;
}
public void bind()
{
bindDoubleClick();
bindEnterKey();
bindMenuActions();
}
public void updateFilesInView(List<FileItem> fileItems, FileItem selectedItem)
{
List<Object> items = new ArrayList<>();
items.add(BACK_STRING);
items.addAll(fileItems);
view.setFilesAndUpdateView(items, selectedItem);
view.resetStateMessage();
}
public void updateFilesInViewAsync()
{
updateFilesInViewAsync(null);
}
private void addMenuAction(final String code, final String stateLoadingMessage,
final Source.SourceFactory sourceFactory)
{
view.addActionForMenu(code, new SelectMenuResult()
{
@Override
public void process(final String result)
{
view.setStateMessage(stateLoadingMessage + result);
new SwingWorker<Source, Void>()
{
@Override
protected Source doInBackground() throws Exception
{
return sourceFactory.create(result);
}
@Override
protected void done()
{
try
{
Source newSource = get();
replaceSourceAndUpdateView(newSource, null);
view.resetStateMessage();
}
catch (Exception e)
{
setError("Error open path: ", e);
}
}
}.execute();
}
});
}
private void bindDoubleClick()
{
view.addMouseListener(new MouseAdapter()
{
@Override
public void mouseClicked(MouseEvent e)
{
if (e.getClickCount() == 2)
{
onEnter();
}
}
});
}
private void bindEnterKey()
{
KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
view.addKeySelectionListener(enter, ENTER, new AbstractAction()
{
private static final long serialVersionUID = 1L;
@Override
public void actionPerformed(ActionEvent e)
{
onEnter();
}
});
}
private void bindMenuActions()
{
addMenuAction(FilesView.MENU_ITEM_LOCATION, "Open location: ", FSSource.FACTORY);
addMenuAction(FilesView.MENU_ITEM_ZIP, "Open zip file: ", ZipSource.FACTORY);
addMenuAction(FilesView.MENU_ITEM_FTP, "Connect to ftp server: ", FTPSource.FACTORY);
}
private Throwable getCause(Exception e)
{
Throwable cause = e;
if (e.getCause() != null)
{
cause = e.getCause();
}
return cause;
}
private void goBack()
{
new SwingWorker<FileItem, Void>()
{
@Override
protected FileItem doInBackground() throws Exception
{
return fileSource.goBack();
}
@Override
protected void done()
{
try
{
FileItem fileItem = get();
if (null != fileItem)
{
updateFilesInViewAsync(fileItem);
return;
}
if (!prevSources.isEmpty())
{
Pair<Source, FileItem> prevSource = prevSources.remove(prevSources.size() - 1);
replaceSourceAndUpdateView(prevSource.key, prevSource.value);
}
view.resetStateMessage();
}
catch (Exception e)
{
setError("Cannot go to parent: ", e);
}
}
}.execute();
}
private void goInto(final FileItem item)
{
new SwingWorker<Void, Void>()
{
@Override
protected Void doInBackground() throws Exception
{
fileSource.goInto(item);
return null;
}
@Override
protected void done()
{
try
{
get();
view.setStateMessage("Load files ...");
updateFilesInViewAsync();
}
catch (Exception e)
{
setError("Cannot go to the directory: ", e);
}
}
}.execute();
}
private void onEnter()
{
if (view.getSelectedValue() != null)
{
view.setStateMessage("Open ...");
processEnterKey();
}
}
private void processEnterKey()
{
if (view.isSelectedBackLink())
{
goBack();
return;
}
final FileItem item = view.getSelectedValue();
if (item.isDirectory())
{
goInto(item);
return;
}
if (tryShowPreview(item))
{
return;
}
tryGetChildSource(item);
}
private void replaceDataSource(Source newSource)
{
fileSource.destroy();
fileSource = newSource;
}
private void replaceSourceAndUpdateView(Source newSource, FileItem selectedItem)
{
replaceDataSource(newSource);
updateFilesInViewAsync(selectedItem);
}
private void setError(String message, Exception e)
{
Throwable cause = getCause(e);
logger.log(Level.WARNING, cause.getMessage(), cause);
view.setStateMessage(message + cause.getMessage());
}
private void setNotFoundPreviewMessageFor(FileItem item)
{
if (!"".equals(item.getFileExtension()))
{
view.setStateMessage("Cannot find preview for the file extension '" + item.getFileExtension() + "'");
}
else
{
view.setStateMessage("Cannot find preview to the file '" + item.getName() + "'");
}
}
private void showPreviewAsync(final FileItem item, Preview preview)
{
final Preview previewForProcess = preview;
view.setStateMessage("Loading preview for file " + item.getName());
new SwingWorker<byte[], Void>()
{
@Override
protected byte[] doInBackground() throws Exception
{
return fileSource.getFile(item);
}
@Override
protected void done()
{
try
{
byte[] file = get();
view.showDialog(previewForProcess, file);
view.resetStateMessage();
}
catch (ExecutionException e)
{
setError("Cannot load file: ", e);
}
catch (Exception e)
{
setError("Error open preview: ", e);
}
}
}.execute();
}
private void tryGetChildSource(final FileItem item)
{
new SwingWorker<Source, Void>()
{
@Override
protected Source doInBackground() throws Exception
{
return fileSource.getSourceFor(item);
}
@Override
protected void done()
{
try
{
Source newSource = get();
if (null != newSource)
{
prevSources.add(new Pair<>(fileSource, item));
fileSource = newSource;
updateFilesInViewAsync();
}
else
{
setNotFoundPreviewMessageFor(item);
}
}
catch (Exception e)
{
logger.log(Level.WARNING, e.getMessage(), e);
}
}
}.execute();
}
private boolean tryShowPreview(final FileItem item)
{
for (Preview preview : PreviewRegistry.get().getPreviews())
{
if (preview.getExtensions().contains(item.getFileExtension()))
{
showPreviewAsync(item, preview);
return true;
}
}
return false;
}
private void updateFilesInViewAsync(final FileItem selectedValue)
{
new SwingWorker<List<FileItem>, Void>()
{
@Override
protected List<FileItem> doInBackground() throws Exception
{
return fileSource.listFiles();
}
@Override
protected void done()
{
try
{
updateFilesInView(get(), selectedValue);
}
catch (Exception e)
{
updateFilesInView(new ArrayList<FileItem>(), selectedValue);
setError("Error file list loading: ", e);
}
}
}.execute();
}
}