/***************************************************************************
 *   Copyright (C) 2005 by Reel Multimedia                                 *
 *                                                                         *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <vector>
#include <iostream>
#include <sstream>

#include <vdr/osd.h>

#include "filebrowser.h"
#include "stringtools.h"
#include "stillpictureplayer.h"
#include "menufileinfo.h"
#include "convert.h"
#include "mountmanager.h"
#include "def.h"
#include "threadprovider.h"
#include "menuplaylist.h"
#include "userIf.h"
#include "filetools.h"
#include "menuBrowser.h"

#include "i18n.h"

//---------listings of files and directories to be handled individually---------

// "directory", "old name", "new name", expert mode
const uint adjustableDirEntriesSize_expert = 2;
const char *const adjustableDirEntries_expert[][3] =
{
    {BASE_DIR, "hd", "Hard Drive"},
    {BASE_DIR, "dvd", "DVD"},
};

// "directory", "old name", "new name", restricted/autostart mode
const uint adjustableDirEntriesSize_restricted = 7;
const char *const adjustableDirEntries_restricted[][3] =
{
    {BASE_DIR, "hd",       trNOOP("Hard Drive")},
    {BASE_DIR, "dvd",      trNOOP("DVD")},
    {MEDIA_DIR, "music",      trNOOP("My Music")},
    {MEDIA_DIR, "pictures",   trNOOP("My Pictures")},
    {MEDIA_DIR, "video",      trNOOP("My Videos")},
    {MEDIA_DIR, "recordings", trNOOP("My Recordings")},
    {VIDEO_DIR, "dvd",     trNOOP("My DVDs")}
};

// "base directory", "directory name", "pretitle for entries in this directory", restricted/autostart mode
const uint preTitleEntriesSize = 5;
const char *const preTitleEntries[][3] =
{
    {MEDIA_DIR, "music",      trNOOP("My Music")},
    {MEDIA_DIR, "pictures",   trNOOP("My Pictures")},
    {VIDEO_DIR, "dvd",     trNOOP("My DVDs")},
    {MEDIA_DIR, "video",      trNOOP("My Videos")},
    {MEDIA_DIR, "recordings", trNOOP("My Recordings")}
};

// "base directory", "directory name", "entry", alternative pretitle, restricted/autostart mode
const uint preTitleExceptionsSize = 1;
const char *const preTitleExceptions[][4] =
{
    {MEDIA_DIR, "video",  "dvd",  ""}
};

// "directory" - dirs in which hidden entries are not displayed
const uint hideHiddenEntryDirsSize= 1;
const char *const hideHiddenEntryDirs[] =
{
    BASE_DIR
};

// "directory", "name" - empty "directory" means in all directories
const uint forbiddenDirEntriesSize_expert = 2;
const char *const forbiddenDirEntries_expert[][2] =
{
    {"", "."},
    {"", "protection.fsk"},
};

// "directory", "name" - empty "directory" means in all directories
const uint forbiddenDirEntriesSize_restricted = 7;
const char *const forbiddenDirEntries_restricted[][2] =
{
    {"", "."},
    {"", "protection.fsk"},
    {MEDIA_DIR, "Channels.conf"},
    {MEDIA_DIR, "Channels.conf.tmp"},
    {MEDIA_DIR, "epg.data"},
    {MEDIA_DIR, "sys.log"},
    {MEDIA_DIR, "home"}
};

// "directory", "name", restricted/autostart mode - only these entries will appear in the specified dir
const uint allowedDirEntriesSize = 5;
const char *const allowedDirEntries[][2] =
{
    {MEDIA_DIR, ".."},
    {MEDIA_DIR, "music"},
    {MEDIA_DIR, "pictures"},
    {MEDIA_DIR, "video"},
    {MEDIA_DIR, "recordings"}
};

// "directory", "name" - entries will appear in same order
const uint positionedDirEntriesSize = 2;
const char *const positionedDirEntries[][2] =
{
    {BASE_DIR, "hd"},
    {BASE_DIR, "dvd"}
};

//---------Utilities-------------------------------

bool sortDirEntry(const cMenuDirEntryItem *item1, const cMenuDirEntryItem *item2)
{
    return GetLast(item1->GetPath()) <  GetLast(item2->GetPath());
}

//----------cMenuFileBrowser-----------------------

cThreadProvider cMenuFileBrowser::provider_;
cPlayList cMenuFileBrowser::actualPlaylist;

cMenuFileBrowser::cMenuFileBrowser(const std::string title, cFileBrowserStatus *newstatus, cFileCache *cache, cImageConvert *convert, cThumbConvert *thumbConvert)
: cMenuFileBrowserBase(title), currentdir_(newstatus->GetStartDir()), cache_(cache), convert_(convert), thumbConvert_(thumbConvert), playlist_(newstatus->GetPlayList())
{
    printf("#####cMenuFileBrowser: currentdir_ = %s mode = %d\n", currentdir_.c_str(), newstatus->GetMode());
    SetCols(2, 2, 2);

    status = newstatus;
    status->Startup();

    cUserIfBase::SetHistory(status->GetIfHistory());

    if(status->GetMode() == browseplaylist)
    {
        //printf("#####cMenuFileBrowser: browseplaylist\n"); 
        userIf_ = cMenuBrowserBrowsePlayListIf::Instance();
        userIf_->EnterState(this);
    }
    else if(status->GetMode() == autostart)
    {
        //printf("#####cMenuFileBrowser: autostart\n"); 
        userIf_ = cMenuBrowserAutoStartIf::Instance();
        userIf_->EnterState(this);
    }
    else if(status->GetMode() == expert)
    {
        //printf("#####cMenuFileBrowser: expert\n");
        userIf_ = cMenuBrowserStandardIf::Instance();
        userIf_->EnterState(this);
    }
    else if(status->GetMode() == restricted)
    {  
        //printf("#####cMenuFileBrowser: restricted\n");
        userIf_ = cMenuBrowserRestrictedIf::Instance();
        userIf_->EnterState(this);
    }

    if(!Refresh(status->GetStartFile(), true))
    {
        //start dir cannot be opened -> use BASE_DIR instead
        currentdir_ = BASE_DIR; 
        Refresh(itemId(), true);
    }       

    //save or discard playlist?
    if(status->ClosePlayList())
    {
        //printf("#####cMenuFileBrowser: ClosePlayList\n");
        cMenuBrowserBaseIf *lastIf = userIf_;
        userIf_ = cMenuBrowserPlaylistRequestIf::Instance();
        userIf_->EnterState(this, lastIf, osContinue);
    }

    if(status->EnterPlayList()) //???
    {
        //if(status->GetPlayList() != playlist_.GetName())
        {
            //LoadPlayList(cPlayListItem(status->GetPlayList()));
        }
        if(!playlist_->CurrentIsSet() || playlist_->GetCurrent().GetPath() != status->GetPlayListEntry())
        {
            playlist_->SetCurrent(status->GetPlayListEntry(), true);
        }
        //cUserIfBase::RemoveLastIf();
        EnterPlayList();
    }
}
cMenuFileBrowser::~cMenuFileBrowser()
{
    Shutdown();
}

void cMenuFileBrowser::Shutdown()
{ 
    //printf("-------------cMenuFileBrowser::Shutdown-----------------\n");
    //make sure, conversion has finished
    if(convert_ && !status->StillPictureActive())
    {
        RemoveItemsFromConversionList(convert_);
    }   
    if(thumbConvert_)
    {
        RemoveItemsFromConversionList(thumbConvert_);
    }

    //shutdown submenus of browser type in reverse order
    ShutdownBrowserSubMenu();

    //clear playlist if not active
    if(!status->PlayListIsActive()) //???
    {
        //ClearPlayList();
    }

    ShowThumbnail(cPlayListItem(), true, false);
    Skins.Flush(); /** TB: hack to avoid the "clear thumbnail"-problem */

    status->Shutdown(currentdir_, GetCurrentItem() ? GetCurrentItem()->GetPath() : "",
                   /* playlist_->GetName(),*/ playlist_->CurrentIsSet() ? playlist_->GetCurrent().GetPath() : "");
}

eOSState cMenuFileBrowser::ProcessKey(eKeys Key)
{
    eOSState state = osUnknown;
    if(Key != kInfo)
    {
        state = userIf_->AnyKey(this, Key);
    }
    RefreshIfDirty();

    if(state == osUnknown && !GetBrowserSubMenu())
    {
        switch (Key)
        {
            case kOk:
                state = userIf_->OkKey(this);
                break;
            case kBack:
                state = userIf_->BackKey(this);
                break;
            case kRed:
                state = userIf_->RedKey(this);
                break;
            case kGreen:
                state = userIf_->GreenKey(this);
                break;
            case kYellow:
                state = userIf_->YellowKey(this);
                break;
            case kBlue:
                state = userIf_->BlueKey(this);
                break;
            case kInfo:
                state = userIf_->InfoKey(this);
                break;
            default:
                state = osContinue;
                break;
        }
    }
    return state;
}

eOSState cMenuFileBrowser::ShowFileWithImageViewer(cPlayListItem &item, bool asPlaylist)
{
    //printf("---cMenuFileBrowser::ShowFileWithImageViewer item = %s asPlaylist = %d---\n", item.GetPath().c_str(), asPlaylist);
    ClearMarks();
    if(asPlaylist)
    {
        actualPlaylist.Clear();
        playlist_->GetPartialListOfSimilarItems(item, actualPlaylist);
    }
    else
    {
        actualPlaylist.Clear();
        BuildActualPlayList(item,  actualPlaylist);
        actualPlaylist.SetCurrent(item, true);
    }

    status->SetStillPictureActive(true);
    cStillPictureControl *control = new cStillPictureControl(&actualPlaylist, cache_, convert_, status->GetInstance());
    cControl::Launch(control);
    return osEnd;
    //return AddFileBrowserSubMenu(new cMenuShowFile(DESCRIPTION, &actualPlaylist_, cache_, convert_));
}

eOSState cMenuFileBrowser::ShowPlayListWithXine()
{
     //printf("---------cMenuFileBrowser::ShowPlayListWithXine--------\n");
     if(playlist_->CurrentIsSet())
     {
        return ShowFileWithXine(playlist_->GetCurrent(), true);
     }
     return osContinue;
}

eOSState cMenuFileBrowser::ShowFileWithXine(const cPlayListItem &item, bool asPlaylistItem)
{
    //printf("----cMenuFileBrowser::ShowFileWithXine, asPlaylist = %d----\n", (int)asPlaylistItem);
    std::vector<std::string> playlistEntries; //empty list
    if(asPlaylistItem)
    {
        cPlayList partialList;
        playlist_->GetPartialListOfSimilarItems(item, partialList);
        playlistEntries = partialList.GetPathList();
    }
    std::string tmp = item.GetPath().c_str();
    if(strstr("://", tmp.c_str()))
    {
	tmp = std::string("file://") + item.GetPath().c_str();
    }

    struct
    {
        char const *mrl;
        int instance;
        bool playlist;
        std::vector<std::string> playlistEntries;
    } xinePlayData =
    {
        tmp.c_str(),
        status->GetInstance(),
        asPlaylistItem,
        playlistEntries
    };
    
    cPluginManager::CallAllServices("Xine Play mrl", &xinePlayData);
    XineStarted(asPlaylistItem);
    return osEnd;
}

eOSState cMenuFileBrowser::ShowDvdWithXine(const cPlayListItem &item, bool asPlaylistItem)
{
    //printf("---------cMenuFileBrowser::ShowDvdFileWithXine, asPlaylist = %d--------\n", (int)asPlaylist);
    std::vector<std::string> playlistEntries; //empty list
    if(asPlaylistItem)
    {
        cPlayList partialList;
        playlist_->GetPartialListOfSimilarItems(item, partialList);
        playlistEntries = partialList.GetPathList();
    }
    std::string tmp = item.GetPath().c_str();
    if(!strstr("dvd://", tmp.c_str()))
    {
	tmp = std::string("dvd://") + item.GetPath().c_str();
    }

    struct
    {
        char const *mrl;
        int instance;
        bool playlist;
        std::vector<std::string> playlistEntries;
    } xinePlayData =
    {
        tmp.c_str(), 
        status->GetInstance(),
        asPlaylistItem,
        playlistEntries
    };

    cPluginManager::CallAllServices("Xine Play mrl", &xinePlayData);
    XineStarted(asPlaylistItem);
    return osEnd;
}

struct CurThumb
{
  int x;
  int y;
  char path[255];
  bool blend;
  int slot;
  int w;
  int h;
};

eOSState cMenuFileBrowser::ShowThumbnail(cPlayListItem item, bool hide, bool currentChanched)
{
    //printf("ShowThumbnail, currentChanched = %d\n", currentChanched);
    static bool dirty = true;

    if(hide)
    {
        //printf("HideThumbnail\n");
        dirty = false;
        cPluginManager::CallAllServices("setThumb", NULL);
    }
    else
    {
        //printf("ShowThumbnail, currentChanched = %d\n", currentChanched);
        std::string thumb;
        if(currentChanched)
        {
            dirty = true;
        }
        //TODO: make sure, cache exists 
        if(dirty && cache_->FetchFromCache(item.GetPath(), thumb, true))
        {
            dirty = false;
            //tell skinreelng that a new thumb is available 
            struct CurThumb curThumb;
            curThumb.x = curThumb.y = 0;
            curThumb.h = curThumb.w = 0;
            curThumb.blend = false;
            curThumb.slot = 0;
            strncpy(curThumb.path, thumb.c_str(), 255);
            cPluginManager::CallAllServices("setThumb", (void*)&curThumb);
        }
        if(dirty && currentChanched)
        { 
            //TODO: hourglass
            thumbConvert_->InsertInConversionList(item, true);
            thumbConvert_->StartConversion();
            cPluginManager::CallAllServices("setThumb", NULL);
        }
    }
    return osContinue;
}

eOSState cMenuFileBrowser::OpenFileInfo(const cMenuFileItem &file)
{
    return AddFileBrowserSubMenu(new cMenuFileInfo(DESCRIPTION, file));
}

eOSState cMenuFileBrowser::EnterSubDir(std::string pathname)
{
    std::string lastdir = currentdir_;
    if(GetLast(pathname) == "..")
    {
        ExitDir(true);
    }
    else if (!ChangeDir(pathname))
    {
        //go back to last directory in case of error
        ChangeDir(lastdir);
    }
    return osContinue;
}

eOSState cMenuFileBrowser::LoadAndEnterPlayList(cPlayListItem item)
{
    LoadPlayList(item);
    return EnterPlayList();
}

//rename some entries in root dir, inefficient for larger numbers of entries!!
std::string cMenuFileBrowser::GetAdjustedEntryName(std::string path) const
{
    std::string dir = RemoveName(path);
    std::string name = GetLast(path);

    if(status->GetMode() == restricted || status->GetMode() == autostart || status->GetMode() == browseplaylist)
    {
        for (uint i = 0; i < adjustableDirEntriesSize_restricted; i++)
        {
            if(adjustableDirEntries_restricted[i][0] == dir
            && adjustableDirEntries_restricted[i][1] == name)
            {
                return tr(adjustableDirEntries_restricted[i][2]);
            }
        }
    }
    else if(status->GetMode() == expert || status->GetMode() == browseplaylist)
    {
        for (uint i = 0; i < adjustableDirEntriesSize_expert; i++)
        {
            if(adjustableDirEntries_expert[i][0] == dir
            && adjustableDirEntries_expert[i][1] == name)
            {
                return tr(adjustableDirEntries_expert[i][2]);
            }
        }
    }
    return name;
}

eOSState cMenuFileBrowser::EnterPlayList(bool play)
{
    std::string title = "Playlist: " + RemoveEnding(GetLast(playlist_->GetName()));
    return AddFileBrowserSubMenu(new cMenuPlayList(title.c_str(), playlist_, cache_, convert_, play));
}

eOSState cMenuFileBrowser::OpenMarked(bool &playlistEntered)
{
    if(!markedEntries_.empty())
    {
        int oldsize = playlist_->Size();
        playlist_->Insert(markedEntries_);
        if(playlist_->Size() > oldsize)
        {
            cPlayListItem currentItem = playlist_->GetNthItem(oldsize);
            playlist_->SetCurrent(currentItem);
            playlistEntered = true;
            eOSState state;
            state = currentItem.Open(this, true);
            return state;
        }
    }
    return osContinue;
}

eOSState cMenuFileBrowser::ExitDir(bool fromItem)
{    
    /*if(status->GetMode() == browseplaylist)
    {
        if(currentdir_ == BASE_DIR)
        {
            return osContinue; //do not exit in browseplaylist mode
        }
    }*/
    //printf("------cMenuFileBrowser::ExitDir, status->GetExitDir() = %s-----------\n", status->GetExitDir().c_str());
    if((currentdir_ == status->GetExitDir() && !fromItem) || currentdir_ == BASE_DIR) //never go beyond BASE_DIR!
    {
        status->NormalExit();
        return osBack;
    }
 
    if(mountMgr_.IsMounted(currentdir_))
    {
        if(convert_)
        {
            RemoveItemsFromConversionList(convert_);
            convert_->WaitUntilCopied();
        }   
        if(thumbConvert_)
        {
            RemoveItemsFromConversionList(thumbConvert_);
            thumbConvert_->WaitUntilCopied();
        }
        mountMgr_.Umount();
    }
    ChangeDir(RemoveName(currentdir_), currentdir_);
    return osContinue;
}

eOSState cMenuFileBrowser::StandardKeyHandling(eKeys Key)
{
    if((Key == kBack || Key == kInfo) && !HasSubMenu()) //these keys are handled separately
    {
        return osUnknown;
    }
    else
    {
        return cOsdMenu::ProcessKey(Key);
    }
}

void cMenuFileBrowser::GetFinishedTasks()
{
    cTask *task;
    if(provider_.GetNextFinishedTask(&task))
    {
        delete task;
        Refresh();
    }
}

void cMenuFileBrowser::DeleteMarkedEntries()
{
    std::vector<std::string> entriesToDelete;
    for(std::set<itemId>::iterator i = markedEntries_.begin(); i != markedEntries_.end(); i++)
    {
        if(convert_ && GetFileItem(*i))
        {
            convert_->RemoveFromConversionList((*i).path_);
            if (convert_->IsInConversion((*i).path_))
                convert_->WaitUntilCopied();
        }
        entriesToDelete.push_back((*i).path_);
    }
    provider_.InsertNewTask(new cDeleteTask(entriesToDelete, currentdir_));
}

void cMenuFileBrowser::DeleteMarkedOrCurrent()
{
    if(markedEntries_.empty())
    {
        cMenuDirEntryItem *item = GetCurrentDirEntryItem();
        if(item && GetLast(item->GetPath()) != "..")
        {
            if(convert_ && item->IsFile())
            {
                if (convert_->IsInConversion(item->GetPath()))
                    convert_->WaitUntilCopied();
            }
            std::vector<std::string> entriesToDelete;
            entriesToDelete.push_back(item->GetPath());
            provider_.InsertNewTask(new cDeleteTask(entriesToDelete, currentdir_));
        }
    }
    else
    {
        DeleteMarkedEntries();
    }
}

void cMenuFileBrowser::InsertCopiedEntries()
{
    Paste(false);
}

void cMenuFileBrowser::InsertMovedEntries()
{
    Paste(true);
}

void cMenuFileBrowser::CopyMarkedEntries()
{
    for(std::set<itemId>::iterator i = markedEntries_.begin(); i != markedEntries_.end(); i++)
    {
        copiedEntries_.push_back((*i).path_);
    }
}

void cMenuFileBrowser::CopyMarkedOrCurrent()
{
    if(markedEntries_.empty())
    {
        cMenuDirEntryItem *item = GetCurrentDirEntryItem();
        if(item && GetLast(item->GetPath()) != "..")
        {
            copiedEntries_.push_back(item->GetPath());
        }
    }
    else
    {
        CopyMarkedEntries();
    }
}

bool cMenuFileBrowser::RenameEntry(std::string oldname, std::string newname)
{
    std::string cmd = std::string("mv ") + "\"" + currentdir_  + "/" + oldname + "\" " +  "\"" + currentdir_ + "/" + newname + "\"";
    //printf("----%s-----\n", cmd.c_str());
    return !::system(cmd.c_str());
}

bool cMenuFileBrowser::CreateDirectory(std::string name)
{
    std::string cmd = std::string("mkdir ") + "\"" + currentdir_ + "/" + name + "\"";
    //printf("----%s----\n", cmd.c_str());
    return !::system(cmd.c_str());
}

bool cMenuFileBrowser::ConvertToUTF8(std::string name)
{
    std::string cmd = std::string("convert_to_utf8.sh -f -d ") + "\"" + name + "\"";
    //printf("----%s----\n", cmd.c_str());
    return !::system(cmd.c_str());
}

bool cMenuFileBrowser::InsertCurrentInPlayList()
{
    cMenuDirEntryItem *item = GetCurrentDirEntryItem();
    if(item && GetLast(item->GetPath()) != "..")
    {
        return playlist_->Insert(item);
    }
    return playlist_->Insert(markedEntries_);
}

bool cMenuFileBrowser::InsertMarkedEntriesInPlayList()
{
    return playlist_->Insert(markedEntries_);
}

bool cMenuFileBrowser::InsertMarkedOrCurrentInPlayList()
{
    if(markedEntries_.empty())
    {
        return InsertCurrentInPlayList();
    }
    else
    {
        return InsertMarkedEntriesInPlayList();
    }
    return true;
}

bool cMenuFileBrowser::HasUnfinishedTasks()
{
    return provider_.HasTasks();
}

bool cMenuFileBrowser::Refresh(itemId newItemId, bool initial)
{ 
    std::string newCurrentPath = newItemId.path_;
    std:: string currentPath = GetCurrentItemPath();

    if(initial && status->StillPictureActive())
    {
        //printf("--cMenuFileBrowser::Refresh, actualPlaylist.CurrentIsSet() = %d--\n", actualPlaylist.CurrentIsSet());
        currentPath = actualPlaylist.CurrentIsSet() ? actualPlaylist.GetCurrent().GetPath() : "";
        status->SetStillPictureActive(false);
    }
    else if(newCurrentPath != "")
    {
        currentPath = newCurrentPath;
    }

    SetTitle(GetTitleString().c_str());
    ClearMarks();
    Clear();

    //create menu entries
    bool success = OpenDir();

    //set current entry
    if(success && currentPath != "")
    {
        for (int i= 0; i < Count(); i++)
        {
            if(GetItem(i)->GetPath() == currentPath)
            {
                SetCurrent(Get(i));
                UpdateCurrent(true);
                break;
            }
        }
    }

    if(success)
    {
        //start conversion for convertable items
        if(convert_)
        {
            InsertItemsInConversionList(convert_);
            convert_->StartConversion();
        }
        if(thumbConvert_)
        {
            InsertItemsInConversionList(thumbConvert_);
            thumbConvert_->StartConversion();
        }
        FlagItems();
    }

    //refresh user if 
    userIf_->SetMode(this, status->GetMode(), initial);
    userIf_->SetHelp(this);
    Display();

    return success;
}

void cMenuFileBrowser::Hide(bool hide)
{
    static int cur = 0;
    if(!hide)
    {
        hidden = false;
        Clear();
        OpenDir();
        ReMarkItems();
        FlagItems();
        SetCurrent(Get(cur));
        UpdateCurrent(true);
        userIf_->SetHelp(this); 
    }
    else
    {
        hidden = true;
        cur = Current();
        Clear();
	    ShowThumbnail(cPlayListItem(),true,false);
	    Skins.Flush();
    }
    Display();
}

bool cMenuFileBrowser::OpenDir()
{
    if (!mountMgr_.Mount(currentdir_))
    {
        Skins.Message(mtError, tr("Cannot mount media"));
        return false;
    }

    //use BASEDIR if currentdir_ is empty
    std::string dir = currentdir_;
    if (dir == "")
    {
        dir = BASE_DIR;
    }

    std::vector<Filetools::dirEntryInfo>  dirs;
    std::vector<Filetools::dirEntryInfo>  files;
    std::vector<Filetools::dirEntryInfo>  dirLinks;
    std::vector<Filetools::dirEntryInfo>  fileLinks;

    //search for entries in directory
    if(!Filetools::GetDirEntries(dir, dirs, files, dirLinks, fileLinks))
    {
        printf("couldn't open %s\n", dir.c_str());
        return false;
    }

    std::vector<cMenuDirItem*> dirEntries;
    std::vector<cMenuFileItem*> fileEntries;

    //create the corresponding menu entries
    for(uint i = 0; i < dirs.size(); i++)
    {
        dirEntries.push_back(new cMenuDirItem(dirs[i].name_, this));
    }
    for(uint i = 0; i < dirLinks.size(); i++)
    {
        dirEntries.push_back(new cMenuDirLinkItem(dirLinks[i].name_, this));
    }
    for(uint i = 0; i < files.size(); i++)
    {
        fileEntries.push_back(new cMenuFileItem(files[i].name_, this));
    }
    for(uint i = 0; i < fileLinks.size(); i++)
    {
        fileEntries.push_back(new cMenuFileLinkItem(fileLinks[i].name_, this));
    }

    //Sort menu entries alphabetically
    std::stable_sort(dirEntries.begin(), dirEntries.end(), sortDirEntry);
    std::stable_sort(fileEntries.begin(), fileEntries.end(), sortDirEntry);

    //Remove "forbidden" entries, resort
    AdjustEntries(dirEntries, fileEntries);

    //Add menu entries
    uint i = 0;
    if(/*currentdir_ != status->GetExitDir() && */currentdir_ != BASE_DIR)
    {
        for (i = 0; i < dirEntries.size(); ++i)
        {
            if(GetLast(dirEntries[i]->GetPath()) == "..")
            {
                Add(dirEntries[i]);
                dirEntries[i]->Set();
                break;
            }
        }
    }
    for (i  = 0; i < dirEntries.size(); ++i)
    {
        if(GetLast(dirEntries[i]->GetPath()) != "..")
        {
            Add(dirEntries[i]);
            dirEntries[i]->Set();
        }
    }
    for (i = 0; i < fileEntries.size(); ++i)
    {
        Add(fileEntries[i]);
        fileEntries[i]->Set();
    }

    return true;
}

bool cMenuFileBrowser::ChangeDir(std::string pathname, std::string currentItem)
{
    currentdir_ = pathname;
    if(convert_)
    {
        RemoveItemsFromConversionList(convert_);
    }   
    if(thumbConvert_)
    {
        RemoveItemsFromConversionList(thumbConvert_);
    }
    return Refresh(currentItem);
}

void cMenuFileBrowser::ClearPlayList()
{
    //printf("----------cMenuFileBrowser::ClearPlaylist--------\n");
    playlist_->Clear(status->GetExitDir());
}

void cMenuFileBrowser::SavePlayList()
{
    //printf("----------cMenuFileBrowser::SavePlaylist--------\n");
    playlist_->SaveToFile();
    ClearPlayList();
}

void cMenuFileBrowser::CopyPlayListToUsb()
{
    //printf("----------cMenuFileBrowser::CopyPlaylistToUsb--------\n");
    //TODO: determine actual mount point for usb device!!!
    std::string usbdir = "/media/USB_DISK_2.0";
    std::string listdir = usbdir + "/" + RemoveEnding(GetLast(playlist_->GetName()));
    std::string cmd = std::string("mkdir ") + listdir;
    ::system(cmd.c_str());
    provider_.InsertNewTask(new cCopyTask(playlist_->GetPathList(), listdir));
}

void cMenuFileBrowser::CopyPlayListToDvd()
{
    playlist_->SaveToFile(); //?????????????
    std::string cmd =  std::string("sudo mp3_2_media.sh ") + playlist_->GetName();
    printf("-----CopyPlaylistToDvd %s--------\n", cmd.c_str());
    ::system(cmd.c_str());
}

bool cMenuFileBrowser::PlayListIsEmpty() const
{
    return playlist_->IsEmpty();
}

void cMenuFileBrowser::Paste(bool remove)
{
    if(!remove)
    {
         provider_.InsertNewTask(new cCopyTask(copiedEntries_, currentdir_));
    }
    else
    {
        for (uint i= 0; i< copiedEntries_.size(); i++)
        {
            if(convert_)
            {
                convert_->RemoveFromConversionList(copiedEntries_[i]);
                if(convert_->IsInConversion(copiedEntries_[i]))
                    convert_->WaitUntilCopied();
            }
        }
        provider_.InsertNewTask(new cMoveTask(copiedEntries_, currentdir_, RemoveName(copiedEntries_[0])));
    }
    copiedEntries_.clear();
}

void cMenuFileBrowser::InsertItemsInConversionList(cImageConvert *convert) const
{
    cMenuFileItem *fileitem;
    std::map<std::string, cPlayListItem> convItems;
    for (int i = 0; i < Count(); i++)
    {
        fileitem = GetFileItem(i);
        if(fileitem && fileitem->GetFileType().IsOfSimilarType(tp_jpg))
        {
            if (!cache_->InCache(fileitem->GetPath()))
            {
                convItems[fileitem->GetPath()] = fileitem->GetPlayListItem();
            }
        }
    }
    convert->InsertInConversionList(convItems);
}

void cMenuFileBrowser::RemoveItemsFromConversionList(cImageConvert *convert) const
{
    cMenuFileItem *fileitem;
    for (int i= 0; i < Count(); i++)
    {
        fileitem = GetFileItem(i);
        if(fileitem && fileitem->GetFileType().IsOfSimilarType(tp_jpg))
        {
            convert->RemoveFromConversionList(fileitem->GetPath());
        }
    }
}

std::string cMenuFileBrowser::GetTitleString() const
{
    std::string titlestring;
    if(status->GetMode() == restricted /*|| status->GetMode() == browseplaylist*/)
    {
        titlestring = GetPreTitle(currentdir_);
        titlestring += GetLastN(GetAdjustedEntryName(currentdir_), 20);
    }
    else if(status->GetMode() == expert || status->GetMode() == autostart)
    {
        titlestring = tr(DESCRIPTION) + std::string(": ");
        titlestring += GetLastN(currentdir_, 20);
    }
    else if(status->GetMode() == browseplaylist)
    {
        titlestring = GetPreTitle(currentdir_);
        titlestring += GetLastN(GetAdjustedEntryName(currentdir_), 20);
        titlestring += std::string(": ") + tr("Insert in playlist");
    }
    return titlestring;
}

void cMenuFileBrowser::LoadPlayList(cPlayListItem item)
{
    playlist_->LoadFromFile(item);
}

void cMenuFileBrowser::FlagItems()
{
    cMenuFileItem *fileitem;
    for (int i = 0; i < Count(); i++)
    {
        fileitem = GetFileItem(i);
        if(fileitem)
        {
            if(status->GetMode() == browseplaylist && playlist_->IsInList(fileitem->GetPlayListItem()))
            {
                fileitem->Flag();
            }
            else
            {
                fileitem->UnFlag();
            }
        }
    }
}

/*std::string cMenuFileBrowser::GetPreTitle(std::string entry) const
{
    std::string pretitle = "";
    for (uint i = 0; i < preTitleEntriesSize; i++)
    {
         if (StartsWith2(entry, std::string(preTitleEntries[i][0]) + "/" + preTitleEntries[i][1]))
         {
              pretitle = std::string(tr(preTitleEntries[i][2])) + ": ";
              break;
         }
    }
    for (uint i = 0; i < preTitleExceptionsSize; i++)
    {
         if (entry == std::string(preTitleExceptions[i][0]) + "/" + preTitleExceptions[i][1] + "/" + preTitleExceptions[i][2])
         {
              pretitle = preTitleExceptions[i][3]; 
              break;
         }
    }
    return pretitle;
}*/

std::string cMenuFileBrowser::GetPreTitle(std::string entry) const
{
    std::string pretitle = "";
    entry = cFileBrowserStatus::GetDefStartDir(status->GetFilter());

    for (uint i = 0; i < preTitleEntriesSize; i++)
    {
         if (entry == std::string(preTitleEntries[i][0]) + "/" + preTitleEntries[i][1])
         {
              pretitle = std::string(tr(preTitleEntries[i][2])) + ": ";
              break;
         }
    }
    for (uint i = 0; i < preTitleExceptionsSize; i++)
    {
         if (entry == std::string(preTitleExceptions[i][0]) + "/" + preTitleExceptions[i][1] + "/" + preTitleExceptions[i][2])
         {
              pretitle = preTitleExceptions[i][3]; 
              break;
         }
    }
    return pretitle;
}

bool cMenuFileBrowser::IsForbiddenDirEntry(std::string entry) const
{
    std::string dir = RemoveName(entry);
    std::string name = GetLast(entry);

    if(status->GetMode() == expert || status->GetMode() == browseplaylist)
    {
        for (uint i = 0; i < forbiddenDirEntriesSize_expert; i++)
        {
            if (std::string(forbiddenDirEntries_expert[i][1]) == name && (std::string(forbiddenDirEntries_expert[i][0]) == dir || std::string(forbiddenDirEntries_expert[i][0]) == std::string("")))
            {
                return true;
            }
        }
        return false;
    }
    else
    {
        for (uint i = 0; i < forbiddenDirEntriesSize_restricted; i++)
        {
            if (std::string(forbiddenDirEntries_restricted[i][1]) == name && (std::string(forbiddenDirEntries_restricted[i][0]) == dir || std::string(forbiddenDirEntries_restricted[i][0]) == std::string("")))
            {
                return true;
            }
        }
        return false;
    }
}

bool cMenuFileBrowser::IsAllowedDirEntry(std::string entry) const
{
    if(status->GetMode() == expert || status->GetMode() == browseplaylist)
    {
        return true;
    }
    std::string dir = RemoveName(entry);
    std::string name = GetLast(entry);
    bool foundDir = false;
    for (uint i = 0; i < allowedDirEntriesSize; i++)
    {
       if(std::string(allowedDirEntries[i][0]) == dir || std::string(allowedDirEntries[i][0]) == std::string(""))
       {
           foundDir = true;
       }
    }
    if(!foundDir)
    {
        return true;
    }
    
    for (uint i = 0; i < allowedDirEntriesSize; i++)
    {
         if (std::string(allowedDirEntries[i][1]) == name && (std::string(allowedDirEntries[i][0]) == dir || std::string(allowedDirEntries[i][0]) == std::string("")))
         {
              return true;
         }
    }
    return false;
}

bool cMenuFileBrowser::IsHiddenDirEntry(std::string entry) const
{
    std::string dir = RemoveName(entry);
    std::string name = GetLast(entry);
    for (uint i = 0; i < hideHiddenEntryDirsSize; i++)
    {
        if(dir == hideHiddenEntryDirs[i])
        {
            if(StartsWith(name, ".") && name != "..")
            {
                return true;
            }
        }
    }
    //TODO: limit to resume files, not all hidden entries
    if(StartsWith(name, ".") && name != "..") // remove .files
    {
	    return true;
    }
    return false;
}

bool cMenuFileBrowser::IsInsideFilter(cMenuFileItem &item) const
{
    uint filter = status->GetFilter();
    if(filter & cl_unknown)
    {
        return true;
    }
    if(filter & item.GetFileType().GetClass())
    {
        return true;
    }
    return false;
}

//reposition entries in browser, inefficient!!!
void cMenuFileBrowser::RepositionDirEntries(std::vector<cMenuDirItem*> &direntries)
{
    std::vector<cMenuDirItem*> temp;

    for (uint i = 0; i < positionedDirEntriesSize; ++i)
    {
        for (std::vector<cMenuDirItem*>::iterator itd = direntries.begin(); itd < direntries.end(); )
        {
            std::string dir = RemoveName((*itd)->GetPath());
            std::string name = GetLast((*itd)->GetPath());
            if(positionedDirEntries[i][0] == dir && positionedDirEntries[i][1] == name)
            {
                temp.push_back(*itd);
                itd = direntries.erase(itd);
            }
            else
            {
                ++itd;
            }
        }
    }

    for (std::vector<cMenuDirItem*>::iterator itd = direntries.begin(); itd < direntries.end(); ++itd)
    {
        temp.push_back(*itd);
    }

    direntries = temp;
}

//adjust entry display, inefficient!!!
void cMenuFileBrowser::AdjustEntries(std::vector<cMenuDirItem*> &direntries, std::vector<cMenuFileItem*> &fileentries)
{
    //Big TODO: make it halfway efficient!
    //remove forbidden entries and hidden entries
    for (std::vector<cMenuDirItem*>::iterator itd = direntries.begin(); itd < direntries.end(); )
    {
        if (IsForbiddenDirEntry((*itd)->GetPath())
         || IsHiddenDirEntry((*itd)->GetPath())
         ||!IsAllowedDirEntry((*itd)->GetPath()))
        {
            itd = direntries.erase(itd);
        }
        else
        {
            ++itd;
        }
    }

    for (std::vector<cMenuFileItem*>::iterator itf = fileentries.begin(); itf < fileentries.end(); )
    {
        if (IsForbiddenDirEntry((*itf)->GetPath())
         || IsHiddenDirEntry((*itf)->GetPath())
         ||!IsAllowedDirEntry((*itf)->GetPath())
         ||!IsInsideFilter((**itf)))
        {
            itf = fileentries.erase(itf);
        }
        else
        {
             ++itf;
        }
    }
    //reposition entries
    RepositionDirEntries(direntries);
}
