package org.limewire.ui.swing.library;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.swing.SwingUtilities;

import org.limewire.core.api.Category;
import org.limewire.core.api.URN;
import org.limewire.core.api.download.DownloadItem;
import org.limewire.core.api.download.DownloadState;
import org.limewire.core.api.library.LibraryManager;
import org.limewire.core.api.library.LocalFileItem;
import org.limewire.core.api.library.SharedFileList;
import org.limewire.core.api.upload.UploadItem;
import org.limewire.inject.LazySingleton;
import org.limewire.ui.swing.library.navigator.LibraryNavItem;
import org.limewire.ui.swing.nav.NavCategory;
import org.limewire.ui.swing.nav.NavItem;
import org.limewire.ui.swing.nav.NavMediator;
import org.limewire.ui.swing.nav.Navigator;

import ca.odell.glazedlists.EventList;

import com.google.inject.Inject;
import com.google.inject.Provider;

@LazySingleton
public class LibraryMediator implements NavMediator<LibraryPanel> {

    public static final String NAME = "Library";
    
    private Provider<LibraryPanel> libraryProvider;
    private LibraryPanel libraryPanel;
    private final Provider<Navigator> navigatorProvider;
    private final Provider<LibraryManager> libraryManager;
    
    @Inject
    public LibraryMediator(Provider<LibraryPanel> libraryProvider, Provider<Navigator> navigatorProvider, 
            Provider<LibraryManager> libraryManager) {
        this.libraryProvider = libraryProvider;
        this.navigatorProvider = navigatorProvider;
        this.libraryManager = libraryManager;
    }
    
    @Override
    public LibraryPanel getComponent() {
        if(libraryPanel == null) {
            libraryPanel = libraryProvider.get();
        }
        
        return libraryPanel;
    }

    public void selectInLibrary(File file) {
        final File firstFile = findFile(file);
        showLibrary();
        clearFilters();
        getComponent().selectLocalFileList(libraryManager.get().getLibraryManagedList());
        //allow library to show before selecting the file, needed in case library
        //hasn't been loaded yet.
        SwingUtilities.invokeLater(new Runnable(){
            @Override
			public void run() {
                getComponent().selectAndScrollTo(firstFile);     
            }
        });       
    }
    
    /**
     * Returns the given file if it is a file, otherwise if it is a directory, 
     * it will find the first file in its subdirectories, if no file
     * can be found in the subdirectories, the original file is returned.
     */
    private File findFile(File file) {
        File firstFile = file;
        if(firstFile.isDirectory()) {
            List<File> accumulator = new ArrayList<File>(Arrays.asList(firstFile));
            while(accumulator.size() > 0) {
                File folderOrFile = accumulator.remove(0);
                if(folderOrFile.isDirectory()) {
                    File[] files = folderOrFile.listFiles();
                    if(files != null) {
                        //must null check files because it can return null if there was an error accessing 
                        //the files the interface is wrong about returning an empty list for all circumstances
                        accumulator.addAll(Arrays.asList(files));
                    }
                } else {
                    return folderOrFile;
                }
            }
        }
        return firstFile;
    }
    
    private void showLibrary(){
        NavItem item = navigatorProvider.get().getNavItem(NavCategory.LIBRARY, NAME);
        item.select();
    }
    
    /**
     * Selects the specified SharedFileList in the library nav and starts editing on its name.
     * @param sharedFileList can not be the public shared list
     */
    public void selectAndRenameSharedList(final SharedFileList sharedFileList) {
        assert(!sharedFileList.isPublic());
        showLibrary();
        //allow library to show before selecting and editing or the list won't display properly
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                getComponent().editSharedListName(sharedFileList);
            }
        });
    }

    public void selectInLibrary(final URN urn) {
        NavItem item = navigatorProvider.get().getNavItem(NavCategory.LIBRARY, NAME);
        item.select();
        getComponent().selectLocalFileList(libraryManager.get().getLibraryManagedList());
        //allow library to show before selecting the file, needed in case library
        //hasn't been loaded yet.
        SwingUtilities.invokeLater(new Runnable(){
            @Override
			public void run() {
                getComponent().selectAndScrollTo(urn);                
            }
        });
    }
    
    public void showSharedFileList(SharedFileList list) {
        showSharedFileList(list, false);
    }
    
    /**
     * Opens the My files view and selects the given share list.  
     * 
     * @param showEditingMode whether to open the panel up in friends editing mode.
     */
    public void showSharedFileList(final SharedFileList list, final boolean showEditingMode) {
        NavItem item = navigatorProvider.get().getNavItem(NavCategory.LIBRARY, NAME);
        item.select();
        getComponent();
        
        SwingUtilities.invokeLater(new Runnable(){
            @Override
            public void run() {
                getComponent().selectLocalFileList(list);
                
                // if sharedList can be edited, enable it
                if (showEditingMode) {
                    getComponent().showEditMode();
                }                
            }
        });
    }

    /**
     * Returns true if the library has been initialized. 
     */
    public boolean isInitialized() {
        return libraryPanel != null;
    }
    
    /**
     * Clears any active filters on the library.
     */
    public void clearFilters() {
        getComponent().clearFilters();
    }

    /**
     * Returns the LibraryNavItem that is currently selected.
     */
    public LibraryNavItem getSelectedNavItem() {
        return getComponent().getSelectedNavItem();
    }

    /**
     * Returns the category that is currently selected within the
     * currently selected LibraryNavItem.
     */
    public Category getSelectedCategory() {
        return getComponent().getSelectedCategory();
    }

    public EventList<LocalFileItem> getPlayableList() {
        return getComponent().getPlayableList();
    }

    /**
     * Returns the items that are currently selected.
     */
    public List<LocalFileItem> getSelectedItems() {
        return getComponent().getSelectedItems();
    }

    public void locateInLibrary(DownloadItem item) {
        File file;
        if(item.getState() == DownloadState.DONE) {
            file = item.getLaunchableFile();
        } else {
            file = item.getDownloadingFile();
        }
        URN urn = item.getUrn();
        
        if(file != null) {
            selectInLibrary(file);
        } else if (urn != null){
            selectInLibrary(urn);
        }
    }
    
    public void locateInLibrary(UploadItem item) {
        URN urn = item.getUrn();
        
        selectInLibrary(urn);
    }
}
