/*
 * vdr-podcatcher - A plugin for the Linux Video Disk Recorder
 * Copyright (c) 2006 Tobias Grimm <vdr@e-tobi.net>
 *
 * 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
 *
 * $Id: servicelocatorimpl.cc 3282 2006-04-23 23:44:30Z svntobi $
 *
 */

#include "servicelocatorimpl.h"
#include "feedsconfigfile.h"
#include "configuration.h"
#include "rssfeedparser.h"
#include "vdrthread.h"
#include "vdrsleeper.h"
#include "vdrcriticalsection.h"
#include "downloadqueue.h"
#include "threadsafedownloadpool.h"
#include "syncheddownloadpool.h"
#include "localfilecache.h"
#include "curldownloader.h"
#include "feedupdaterimpl.h"
#include "downloadaction.h"
#include "osdlistmenu.h"
#include "feedmenupresenter.h"
#include "itemmenupresenter.h"
#include "osditemview.h"
#include "itemviewpresenter.h"
#include "circularpespacketbuffer.h"
#include "synchedpespacketbuffer.h"
#include "playbackcontrol.h"
#include "mp3decoder.h"
#include "decodeaction.h"
#include "playbackaction.h"
#include "syslogerrorlogger.h"
#include "itembuttonsupdater.h"
#include "osdsetupmenu.h"
#include "playbackcontroller.h"
#include "vdrpesplayer.h"

using namespace std;

ServiceLocatorImpl::ServiceLocatorImpl(IConfiguration& configuration)
:configuration(configuration)
{
}

IFeedSources* ServiceLocatorImpl::GetFeedSources()
{
    if (!feedSources.get())
    {
        feedSources = ref_ptr<IFeedSources>(
          new FeedsConfigFile(configuration.GetSourcesFileName()));
    }

    return feedSources.get();
}

IFeedParser* ServiceLocatorImpl::GetFeedParser()
{
    if (!feedParser.get())
    {
        feedParser = ref_ptr<IFeedParser>(
          new RssFeedParser(*GetDownloadCache()));
    }

    return feedParser.get();
}

IDownloadPool* ServiceLocatorImpl::GetDownloadPool()
{
    if (!downloadPool.get())
    {
        ref_ptr<ISleeper> downloadSleeper(new VdrSleeper());
        ref_ptr<ICriticalSection> criticalSection(new VdrCriticalSection());

        // Download pool decorated with thread safety and thread synchronization
        ref_ptr<IDownloadPool> pool(new DownloadQueue());
        ref_ptr<IDownloadPool> threadSafePool(new ThreadSafeDownloadPool(pool,
          criticalSection));
        ref_ptr<IDownloadPool> synchedPool(new SynchedDownloadPool(
          threadSafePool, downloadSleeper));

        downloadPool = ref_ptr<IDownloadPool>(synchedPool);
    }

    return downloadPool.get();
}

IDownloadCache* ServiceLocatorImpl::GetDownloadCache()
{
    if (!downloadCache.get())
    {
        downloadCache = ref_ptr<IDownloadCache>(
          new LocalFileCache(configuration.GetCacheDirName()));
    }

    return downloadCache.get();
}

IDownloader* ServiceLocatorImpl::GetDownloader()
{
    if (!downloader.get())
    {
        downloader = ref_ptr<IDownloader>(
          new CurlDownloader(*GetDownloadCache()));
    }

    return downloader.get();
}

IThread* ServiceLocatorImpl::GetDownloadThread()
{
    if (!downloadThread.get())
    {
        downloadThread = ref_ptr<IThread>(
          new VdrThread(CreateDownloadAction()));
    }

    return downloadThread.get();
}

IFeedUpdater* ServiceLocatorImpl::GetFeedUpdater()
{
    if (!feedUpdater.get())
    {
        feedUpdater = ref_ptr<IFeedUpdater>(
          new FeedUpdaterImpl(*GetFeedSources(), *GetDownloadPool(),
            *GetDownloadCache()));
    }

    return feedUpdater.get();
}

ref_ptr<IThreadAction> ServiceLocatorImpl::CreateDownloadAction()
{
    return ref_ptr<IThreadAction>(new DownloadAction(*GetDownloadPool(),
      *GetDownloader()));
}

cOsdMenu* ServiceLocatorImpl::CreateFeedMenu()
{
    return new OsdListMenu(
      ref_ptr<IListMenuPresenter>(new FeedMenuPresenter(*this)));
}

cOsdMenu* ServiceLocatorImpl::CreateItemMenu(Feed feed)
{
    return new OsdListMenu(
      ref_ptr<IListMenuPresenter>(new ItemMenuPresenter(*this, feed)), 9);
}

cOsdMenu* ServiceLocatorImpl::CreateItemView(Feed feed, Item item)
{
    ref_ptr<ItemViewPresenter> itemViewPresenter(
      new ItemViewPresenter(*this, feed.GetTitle(), item));

    return new OsdItemView(itemViewPresenter);
}

ref_ptr<IPesPacketBuffer> ServiceLocatorImpl::CreateSynchedPcmBuffer()
{
    ref_ptr<ISleeper>         getterSleeper(new VdrSleeper());
    ref_ptr<ISleeper>         setterSleeper(new VdrSleeper());
    ref_ptr<IPesPacketBuffer> buffer(new CircularPesPacketBuffer(500));

    return ref_ptr<IPesPacketBuffer>(new SynchedPesPacketBuffer(buffer, getterSleeper, setterSleeper));
}

cControl* ServiceLocatorImpl::CreatePlayerControl(Item item)
{
    ref_ptr<istream>          mp3File = GetDownloadCache()->CreateStreamByUrl(item.GetMp3Url());
    ref_ptr<IPcmDataSource>   decoder = ref_ptr<IPcmDataSource>(new Mp3Decoder(mp3File));

    ref_ptr<IPesPacketBuffer>  synchedBuffer = CreateSynchedPcmBuffer();

    ref_ptr<VdrPesPlayer>     player(new VdrPesPlayer());

    ref_ptr<IThreadAction>    decodeAction(new DecodeAction(decoder, synchedBuffer));
    ref_ptr<IThread>          decodeThread(new VdrThread(decodeAction));

    ref_ptr<IThreadAction>    playbackAction(new PlaybackAction(synchedBuffer, *player));
    ref_ptr<IThread>          playbackThread(new VdrThread(playbackAction));

    player->Init(decodeThread, playbackThread);

    ref_ptr<PlaybackController> playbackController(new PlaybackController(
      mp3File, synchedBuffer, playbackThread));

    return new PlaybackControl(item, player, playbackController);
}

IErrorLogger* ServiceLocatorImpl::GetErrorLogger()
{
    if (!errorLogger.get())
    {
        errorLogger = ref_ptr<IErrorLogger>(
          new SyslogErrorLogger());
    }

    return errorLogger.get();
}

cMenuSetupPage* ServiceLocatorImpl::CreateSetupMenu()
{
    return new OsdSetupMenu(configuration);
}
