package org.limewire.ui.swing.library.navigator;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import org.limewire.core.api.FilePropertyKey;
import org.limewire.core.api.library.LocalFileItem;
import org.limewire.core.api.library.LocalFileList;
import org.limewire.io.IOUtils;
import org.limewire.ui.swing.util.BackgroundExecutorService;
import org.limewire.util.FileUtils;

class M3UList {

    private static final String M3U_HEADER = "#EXTM3U";
    private static final String SONG_DELIM = "#EXTINF";
    private static final String SEC_DELIM  = ":";
    
    private final File file;
    private final LocalFileList localFileList;
    
    public M3UList(File file, LocalFileList localFileList) {
        this.file = file;
        this.localFileList = localFileList;
    }
    
    public void load() {
        BackgroundExecutorService.execute(new Runnable(){
            @Override
			public void run() {
                try {
                    List<File> m3uFileList = loadInternalList();
                    for(File file : m3uFileList)
                        localFileList.addFile(file);
                } catch (Exception e) {
                    //TODO: give user feedback
                }
            }
        });
    }

    public void save() {
        final List<LocalFileItem> writeableList;
        
        localFileList.getModel().getReadWriteLock().readLock().lock();
        try {
            writeableList = new ArrayList<LocalFileItem>(localFileList.getModel().subList(0, localFileList.getModel().size()));
        } finally {
            localFileList.getModel().getReadWriteLock().readLock().unlock();
        }
        
        BackgroundExecutorService.execute(new Runnable(){
            @Override
			public void run() {
                try {
                    saveInternalList(writeableList);
                } catch (Exception e) {
                    //TODO: notify user
                }
            }
        });
    }
    
    /**
     * Attempts to write the list of files to an m3u list. 
     * Follows the standard m3u protocol. 
     */
    private void saveInternalList(List<LocalFileItem> writeableList) throws Exception {
        PrintWriter m3uFile = null;
        try {
            m3uFile = new PrintWriter(new FileWriter(file.getCanonicalPath(), false));

            m3uFile.write(M3U_HEADER);
            m3uFile.println();

            for(LocalFileItem currFile : writeableList) {
                // only save files that are local to the file system
                if(currFile.getFile().isFile() && !currFile.isIncomplete()){
                    File locFile = currFile.getFile();

                    // first line of song description...
                    m3uFile.write(SONG_DELIM);
                    m3uFile.write(SEC_DELIM);
                    // try to write out seconds info....
                    if(currFile.getPropertyString(FilePropertyKey.LENGTH) != null) {
                        m3uFile.write("" + currFile.getPropertyString(FilePropertyKey.LENGTH) + ",");
                    } else {
                        m3uFile.write("-1,");
                    }

                    m3uFile.write(currFile.getName());
                    m3uFile.println();
                    // canonical path follows...
                    m3uFile.write(locFile.getCanonicalPath());
                    m3uFile.println();
                }
            }
        } finally {
            IOUtils.close(m3uFile);
        }
    }
    
    /**
     * Attempts to open the current m3u list and locate and load
     * all the files described within that list. Returns the list
     * of Files that were succesfully found.
     */
    private List<File> loadInternalList() throws Exception {
        List<File> m3uFileList = new ArrayList<File>();
        BufferedReader m3uFile = null;
        try {
            m3uFile = new BufferedReader(new FileReader(file));
            String currLine = null;
            currLine = m3uFile.readLine();
            if (currLine == null || !(currLine.startsWith(M3U_HEADER) || currLine.startsWith(SONG_DELIM)))
                throw new IOException();
            if(currLine.startsWith(M3U_HEADER))
                currLine = m3uFile.readLine();
            
            for(; currLine != null; currLine = m3uFile.readLine()) {
                if (currLine.startsWith(SONG_DELIM)) {
                    currLine = m3uFile.readLine();
                    if(currLine == null)
                        break;
                    File currentFile = new File(currLine);
                    if (currentFile.exists() && !currentFile.isDirectory())
                        m3uFileList.add(currentFile);
                    else {
                        // try relative path to the playlist
                        currentFile = new File(file.getParentFile().getAbsolutePath(), currentFile.getPath());
                        if (currentFile.exists() && !currentFile.isDirectory() && 
                                FileUtils.isReallyInParentPath(file.getParentFile(), currentFile))
                            m3uFileList.add(currentFile);
                    }
                }
            }
        } finally {
            IOUtils.close(m3uFile);
        }
        return m3uFileList;
    }
}
