/*
 * Video On Demand Samples
 *
 * Copyright (C) 2015 Microchip Technology Germany II GmbH & Co. KG
 *
 * 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, see <http://www.gnu.org/licenses/>.
 *
 * You may also obtain this software under a propriety license from Microchip.
 * Please contact Microchip for further information.
 *
 */

#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include "Console.h"
#include "VodHandler.h"
#include "SourceFile.h"
#include "SourceFileConverted.h"
#include "AutoLock.h"

using namespace std;

#define MAX_IPC_PAYLOAD_LEN     (65000)

#define VOD_VERSION_MAJOR       ((uint8_t)3)
#define VOD_VERSION_MINOR       ((uint8_t)0)
#define VOD_VERSION_BUGFIX      ((uint8_t)4)
#define VOD_VERSION_BUILD       ((uint8_t)0)

#define IPC_PORT_NUMBER_VOD 	(5544)
#define FBLOCK_VOD              (17)
#define FUNC_VOD_SELECT_FILE    (1)
#define FUNC_VOD_PLAYMODE       (2)
#define FUNC_VOD_TIMEPOSITION   (3)
#define FUNC_VOD_REPETITION     (4)
#define FUNC_VOD_SERVERVERSION  (5)
#define FUNC_VOD_MEDIAPID       (6)
#define FUNC_VOD_FILE_LIST      (7)
#define FUNC_VOD_NEW_INDEPEND_STREAM (8)
#define FUNC_VOD_FAKE_MODE      (99)

#define IPC_PORT_NUMBER_NM      (5533)
#define FBLOCK_NETWORK_MANAGER  (10)
#define FUNC_NM_SERVERVERSION   (1)
#define FUNC_NM_SUBSCRIBE_MCM   (2)
#define FUNC_NM_UNSUBSCRIBE_MCM (3)
#define FUNC_NM_RECEIVED_MCM    (4)
#define FUNC_NM_SEND_MCM_ADR    (5)
#define FUNC_NM_SEND_MCM_DEV    (6)
#define FUNC_NM_COUNTER_ROUTE   (10)
#define FUNC_NM_CONNECTION_LIST (100)


static CVodHandler *s_mainClass = NULL;

CVodHandler::CVodHandler() : CThread( "CVodHandler", false ), valuesCleared( false ), statisticIsEnabled( false ),
    infoContainer( NULL )
{
    searchPath[0] = '.';
    searchPath[0] = '\0';
    
    pthread_mutex_init( &infoContainerMutex, NULL );

    mostIpcVod = new CMostIpc( IPC_PORT_NUMBER_VOD, true );
    vodSelectFile_Set = new CMsgFilter( FBLOCK_VOD, FUNC_VOD_SELECT_FILE,
        CMostMsg::OP_SET, OnVodSelectFile_Set );
    mostIpcVod->RegisterMessageHandler( vodSelectFile_Set );

    vodPlayMode_Set = new CMsgFilter( FBLOCK_VOD, FUNC_VOD_PLAYMODE, CMostMsg::OP_SET,
        OnVodPlayMode_Set );
    mostIpcVod->RegisterMessageHandler( vodPlayMode_Set );

    vodTimePostion_Set = new CMsgFilter( FBLOCK_VOD, FUNC_VOD_TIMEPOSITION,
        CMostMsg::OP_SET, OnVodTimePosition_Set );
    mostIpcVod->RegisterMessageHandler( vodTimePostion_Set );

    vodRepetition_Set = new CMsgFilter( FBLOCK_VOD, FUNC_VOD_REPETITION,
        CMostMsg::OP_SET, OnVodRepetition_Set );
    mostIpcVod->RegisterMessageHandler( vodRepetition_Set );

    vodServerVersion_Get = new CMsgFilter( FBLOCK_VOD, FUNC_VOD_SERVERVERSION,
        CMostMsg::OP_GET, OnVodServerVersion_Get );
    mostIpcVod->RegisterMessageHandler( vodServerVersion_Get );

    vodMediaPid_Get = new CMsgFilter( FBLOCK_VOD, FUNC_VOD_MEDIAPID,
        CMostMsg::OP_GET, OnVodMediaPid_Get );
    mostIpcVod->RegisterMessageHandler( vodMediaPid_Get );

    vodFileList_Get = new CMsgFilter( FBLOCK_VOD, FUNC_VOD_FILE_LIST,
        CMostMsg::OP_GET, OnVodFileList_Get );
    mostIpcVod->RegisterMessageHandler( vodFileList_Get );

    vodIndependStream_SetGet = new CMsgFilter( FBLOCK_VOD, FUNC_VOD_NEW_INDEPEND_STREAM,
        CMostMsg::OP_SETGET, OnVodIndependentStream_SetGet );
    mostIpcVod->RegisterMessageHandler( vodIndependStream_SetGet );

    vodFakeMode_Set = new CMsgFilter( FBLOCK_VOD, FUNC_VOD_FAKE_MODE,
        CMostMsg::OP_SET, OnFakeMode_Set );
    mostIpcVod->RegisterMessageHandler( vodFakeMode_Set );

    mostIpcNm = new CMostIpc( 0, false );
    nmServerVersion_Status = new CMsgFilter( FBLOCK_NETWORK_MANAGER, FUNC_NM_SERVERVERSION,
        CMostMsg::OP_STATUS, OnNmServerVersion_Status );
    mostIpcNm->RegisterMessageHandler( nmServerVersion_Status );

    nmConnectionList_Status = new CMsgFilter( FBLOCK_NETWORK_MANAGER, FUNC_NM_CONNECTION_LIST,
        CMostMsg::OP_STATUS, OnNmConnectionList_Status );
    mostIpcNm->RegisterMessageHandler( nmConnectionList_Status );

    RegisterBroadcastFiles( SelectFileType_Radio );
    RegisterBroadcastFiles( SelectFileType_Navigation );
    RegisterBroadcastFiles( SelectFileType_Cluster );
}

CVodHandler::~CVodHandler()
{
    Stop();

    if( NULL != mostIpcVod )
    {
        delete mostIpcVod;
        mostIpcVod = NULL;
    }

    if( NULL != vodSelectFile_Set )
    {
        delete vodSelectFile_Set;
        vodSelectFile_Set = NULL;
    }

    if( NULL != vodPlayMode_Set )
    {
        delete vodPlayMode_Set;
        vodPlayMode_Set = NULL;
    }

    if( NULL != vodTimePostion_Set )
    {
        delete vodTimePostion_Set;
        vodTimePostion_Set = NULL;
    }

    if( NULL != vodRepetition_Set )
    {
        delete vodRepetition_Set;
        vodRepetition_Set = NULL;
    }

    if( NULL != vodServerVersion_Get )
    {
        delete vodServerVersion_Get;
        vodServerVersion_Get = NULL;
    }

    if( NULL != vodMediaPid_Get )
    {
        delete vodMediaPid_Get;
        vodMediaPid_Get = NULL;
    }

    if( NULL != vodFileList_Get )
    {
        delete vodFileList_Get;
        vodFileList_Get = NULL;
    }

    if( NULL != vodFakeMode_Set )
    {
        delete vodFakeMode_Set;
        vodFakeMode_Set = NULL;
    }

    if( NULL != mostIpcNm )
    {
        delete mostIpcNm;
        mostIpcNm = NULL;
    }

    if( NULL != nmServerVersion_Status )
    {
        delete nmServerVersion_Status;
        nmServerVersion_Status = NULL;
    }

    if( NULL != nmConnectionList_Status )
    {
        delete nmConnectionList_Status;
        nmConnectionList_Status = NULL;
    }
    
    pthread_mutex_lock( &infoContainerMutex );
    if ( NULL != infoContainer )
    {
        delete infoContainer;
        infoContainer = NULL;
    }
    pthread_mutex_unlock( &infoContainerMutex );
    
    broadcastFiles.RemoveAll( true );
    allMultiplexer.RemoveAll(true);
    ignoreCdevPatterns.RemoveAll( true );
}

bool CVodHandler::ExistsFile( const char *pFileName )
{
    struct stat64 buffer;
    return ( stat64( pFileName, &buffer ) == 0 );
}

CMultiplexer *CVodHandler::GetMultiplexer( const char* deviceName )    
{
    if ( NULL == deviceName )
        return NULL;
    for( uint32_t i = 0; i < allMultiplexer.Size(); i++ )
    {   
        CMultiplexer *t = allMultiplexer[i];
        if (NULL == t)
            continue;
        if ( 0 == strcmp( deviceName, t->GetDriverName() ) )
        {
            return t;
        }
    }
    return NULL;
}

const char *CVodHandler::GetSubPath( SelectFileType_t fileType )
{
    switch( fileType )
    {
    case SelectFileType_Video:
        return "video";
    case SelectFileType_Audio:
        return "audio";
    case SelectFileType_Radio:
        return "radio";
    case SelectFileType_Navigation:
        return "navigation";
    case SelectFileType_Cluster:
        return "cluster";
    default:
        return "";
    }
}

void CVodHandler::RegisterBroadcastFiles( SelectFileType_t fileType )
{
    DIR *d;
    struct dirent *dir;
    char fullPath[200];
    snprintf( fullPath, sizeof ( fullPath ), "%s/%s", searchPath, GetSubPath( fileType ) );

    d = opendir( fullPath );
    if( d )
    {
        while( ( dir = readdir( d ) ) != NULL )
        {
            if( DT_REG == dir->d_type || DT_LNK == dir->d_type )
            {
                BroadcastEntry_t *entry = ( BroadcastEntry_t * )malloc( sizeof ( BroadcastEntry_t ) );
                if( NULL != entry )
                {
                    snprintf( entry->fileName, sizeof ( entry->fileName ), "%s/%s", fullPath, dir->d_name );
                    char *ext = rindex(dir->d_name, '.');
                    if (ext && !strcasecmp(ext, ".ts"))
                        entry->sourceFile = new CSourceFile( entry->fileName, false );
                    else
                        entry->sourceFile = new CSourceFileConverted( entry->fileName, false );
                    broadcastFiles.PushBack( entry );
                }
            }
        }
    }
}

CMultiplexer *CVodHandler::GetMultiplexer( const uint8_t *mac )
{
    volatile CAutoLock autoLock( &infoContainerMutex );
    if( NULL == mac )
    {
        ConsolePrintf( PRIO_ERROR,
            RED"GetMultiplexer was called with invalid parameters."RESETCOLOR"\n" );
        return NULL;
    }
    if( NULL == infoContainer )
        return NULL;
    CMultiplexer *pMultiplexer = NULL;
    char macAddr[20];
    snprintf( ( char * )&macAddr, sizeof( macAddr ), "%02X-%02X-%02X-%02X-%02X-%02X", mac[0], mac[1], mac[2], mac[3],
        mac[4], mac[5] );
    uint32_t sinkCount = infoContainer->GetAllInfoAmount();
    uint32_t mostInstance = 0xFFFFFFFF;
    uint32_t mostConnectionLabel = 0xFFFFFFFF;
    for (uint32_t i = 0; i < sinkCount; i++)
    {
        CConnectionInfo *con = infoContainer->GetInfo(i);
        if ( ( NULL != con ) && ( NULL != con->macAddr ) && ( 0 == strcmp( con->macAddr->ToString(), macAddr ) ) )
        {
            pMultiplexer = GetMultiplexer( con->deviceName );
            if ( NULL != pMultiplexer )
            {
                break;
            }
            else if ( ( EP_Isochron == con->dataType ) ||
                ( EP_Synchron == con->dataType && con->reservedBandwidth > 4 ) )
            {
                mostInstance = con->mostInstance;
                mostConnectionLabel = con->mostConnectionLabel;
                break;
            }
        }
    }
    if( NULL == pMultiplexer && ( 0xFFFFFFFF != mostInstance ) && ( 0xFFFFFFFF != mostConnectionLabel ) )
    {
        for (uint32_t i = 0; i < sinkCount; i++)
        {
            CConnectionInfo *con = infoContainer->GetInfo(i);
            if ( NULL != con && con->mostInstance == mostInstance && con->mostConnectionLabel == mostConnectionLabel )
            {
                pMultiplexer = GetMultiplexer( con->deviceName );
                if ( NULL != pMultiplexer )
                    break;
            }
        }
    }
    if( NULL == pMultiplexer )
    {
        ConsolePrintf( PRIO_ERROR, RED"CVodHandler::GetMultiplexer: Could not get Multiplexer for MAC: %s"RESETCOLOR"\n", macAddr );
    }
    return pMultiplexer;
}

void CVodHandler::OnVodSelectFile_Set( CMsgAddr *pAddr, CMostMsg *pMsg )
{
    if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg || pMsg->GetPayloadLen() < 8 )
    {
        ConsolePrintf( PRIO_ERROR, RED"OnVodSelectFile_Set parameters are invalid.\n"\
            "This could happen, if the client uses an outdated version of the IPC,"\
            " try to update your client."RESETCOLOR"\n" );
        return;
    }

    uint8_t *mac = pMsg->GetPayload();

    SelectFileType_t fileType = ( SelectFileType_t )pMsg->GetPayload()[6];
    char *movieName = ( char * )&pMsg->GetPayload()[7];
    ConsolePrintf( PRIO_HIGH, "OnVodSelectFile_Set IP=%s MAC=%02X-%02X-%02X-%02X-%02X-%02X type=%d video:'%s'\n",
        pAddr->GetInetAddress(), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], fileType, movieName );

    s_mainClass->StreamFile( mac, fileType, movieName );
}

void CVodHandler::OnVodPlayMode_Set( CMsgAddr *pAddr, CMostMsg *pMsg )
{
    if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg || pMsg->GetPayloadLen() < 7 )
    {
        ConsolePrintf( PRIO_ERROR, RED"OnVodPlayMode_Set parameters are invalid.\n"\
            "This could happen, if the client uses an outdated version of the IPC,"\
            " try to update your client."RESETCOLOR"\n" );
        return;
    }
    uint8_t *mac = pMsg->GetPayload();
    bool isPaused = 0 != pMsg->GetPayload()[6];
    ConsolePrintf( PRIO_HIGH, "OnVodPlayMode_IP=%s MAC=%02X-%02X-%02X-%02X-%02X-%02X paused:'%d'\n",
        pAddr->GetInetAddress(), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], isPaused );
    s_mainClass->SetPause( mac, isPaused );
}

void CVodHandler::OnVodTimePosition_Set( CMsgAddr *pAddr, CMostMsg *pMsg )
{
    if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg || pMsg->GetPayloadLen() < 8 )
    {
        ConsolePrintf( PRIO_ERROR, RED"OnVodTimePosition_Set parameters are invalid.\n"\
            "This could happen, if the client uses an outdated version of the IPC,"\
            " try to update your client."RESETCOLOR"\n" );
        return;
    }
    uint8_t *mac = pMsg->GetPayload();
    uint16_t perMil = *( ( uint16_t * )&pMsg->GetPayload()[6] );

    ConsolePrintf( PRIO_HIGH, "OnVodTimePosition_IP=%s MAC=%02X-%02X-%02X-%02X-%02X-%02X TimePos=%d/1000\n",
        pAddr->GetInetAddress(), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], perMil );
    s_mainClass->SetTimePos( mac, perMil );
}

void CVodHandler::OnVodRepetition_Set( CMsgAddr *pAddr, CMostMsg *pMsg )
{
    if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg || pMsg->GetPayloadLen() < 7 )
    {
        ConsolePrintf( PRIO_ERROR, RED"OnVodRepetition_Set parameters are invalid\n"\
            "This could happen, if the client uses an outdated version of the IPC,"\
            " try to update your client."RESETCOLOR"\n" );
        return;
    }
    uint8_t *mac = pMsg->GetPayload();
    bool isRepeated = 0 != pMsg->GetPayload()[6];
    ConsolePrintf( PRIO_HIGH, "OnVodRepetition_IP=%s MAC=%02X-%02X-%02X-%02X-%02X-%02X repeated=%d\n",
        pAddr->GetInetAddress(), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], isRepeated );
    s_mainClass->SetRepetition( mac, isRepeated );
}

void CVodHandler::OnVodServerVersion_Get( CMsgAddr *pAddr, CMostMsg *pMsg )
{
    if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg || pMsg->GetPayloadLen() < 6 )
    {
        ConsolePrintf( PRIO_ERROR, RED"OnVodServerVersion_Get parameters are invalid.\n"\
            "This could happen, if the client uses an outdated version of the IPC,"\
            " try to update your client."RESETCOLOR"\n" );
        return;
    }
    uint8_t *mac = pMsg->GetPayload();
    ConsolePrintf( PRIO_HIGH,
        GREEN"MOST-IPC Client connected (asked for server version). IP=%s MAC=%02X-%02X-%02X-%02X-%02X-%02X "RESETCOLOR
        "\n", pAddr->GetInetAddress(), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] );
    const uint8_t version[] =
    {
        VOD_VERSION_MAJOR, VOD_VERSION_MINOR,
        VOD_VERSION_BUGFIX, VOD_VERSION_BUILD
    };
    CMostMsgTx *mostMsg = new CMostMsgTx( pAddr, //Addr
        FBLOCK_VOD, // nFBlock
        0, // nInst
        FUNC_VOD_SERVERVERSION, // nFunc
        CMostMsg::OP_STATUS, // nOpType
        sizeof ( version ), //nPayloadLen
        version, //Payload
        500, //nTimeoutMs
        NULL, //MessageSentCB
        NULL ); //UserContext
    s_mainClass->mostIpcVod->SendMsg( mostMsg );
    mostMsg->RemoveReference();
}

void CVodHandler::OnVodMediaPid_Get( CMsgAddr *pAddr, CMostMsg *pMsg )
{
    if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg || pMsg->GetPayloadLen() < 6 )
    {
        ConsolePrintf( PRIO_ERROR, RED"OnVodMediaPid_Get parameters are invalid.\n"\
            "This could happen, if the client uses an outdated version of the IPC,"\
            " try to update your client."RESETCOLOR"\n" );
        return;
    }
    uint8_t *mac = pMsg->GetPayload();

    uint16_t videoPid = 0x100 + mac[5];
    uint16_t audioPid = 0x200 + mac[5];
    uint16_t pmtPid = 0x300 + mac[5];
    uint8_t payload[6];
    payload[0] = videoPid / 256;
    payload[1] = videoPid % 256;
    payload[2] = audioPid / 256;
    payload[3] = audioPid % 256;
    payload[4] = pmtPid / 256;
    payload[5] = pmtPid % 256;

    ConsolePrintf( PRIO_MEDIUM,
        GREEN"MOST-IPC Client asked for PIDs. IP=%s MAC=%02X-%02X-%02X-%02X-%02X-%02X"\
        " video-pid=%X audio-pid=%X pmt-pid=%X."RESETCOLOR"\n", pAddr->GetInetAddress(), mac[0], mac[1], mac[2],
        mac[3], mac[4], mac[5], videoPid, audioPid, pmtPid );

    CMostMsgTx *mostMsg = new CMostMsgTx( pAddr, //Addr
        FBLOCK_VOD, // nFBlock
        0, // nInst
        FUNC_VOD_MEDIAPID, // nFunc
        CMostMsg::OP_STATUS, // nOpType
        sizeof ( payload ), //nPayloadLen
        payload, //Payload
        500, //nTimeoutMs
        NULL, //MessageSentCB
        NULL ); //UserContext
    s_mainClass->mostIpcVod->SendMsg( mostMsg );
    mostMsg->RemoveReference();
}

void CVodHandler::OnVodFileList_Get( CMsgAddr *pAddr, CMostMsg *pMsg )
{
    if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg || pMsg->GetPayloadLen() < 6 )
    {
        ConsolePrintf( PRIO_ERROR, RED"OnVodFileList_Get parameters are invalid.\n"\
            "This could happen, if the client uses an outdated version of the IPC, "\
            " try to update your client."RESETCOLOR"\n" );
        return;
    }
    uint8_t *mac = pMsg->GetPayload();
    SelectFileType_t fileType = ( SelectFileType_t )pMsg->GetPayload()[6];

    ConsolePrintf( PRIO_MEDIUM,
        GREEN"MOST-IPC Client asked for File List IP=%s MAC=%02X-%02X-%02X-%02X-%02X-%02X."RESETCOLOR"\n",
        pAddr->GetInetAddress(), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] );

    DIR *d;
    struct dirent *dir;
    char fullPath[200];
    char *payload = ( char * )calloc( 1, MAX_IPC_PAYLOAD_LEN );
    if( NULL == payload )
        return;

    snprintf( fullPath, sizeof( fullPath ), "%s/%s", s_mainClass->searchPath, GetSubPath( fileType ) );

    d = opendir( fullPath );
    ConsolePrintfStart( PRIO_LOW, "Available Files:\n" );
    ConsolePrintfContinue( "File Name\n" );
    ConsolePrintfContinue( "----------------------------------------\n" );
    bool isFirst = true;
    if( d )
    {
        while( ( dir = readdir( d ) ) != NULL )
        {
            char *ext = rindex(dir->d_name, '.');
            if (ext && !strcasecmp(ext, ".ts"))
            {
                ConsolePrintfContinue( "%s\n", dir->d_name );
                if( ( strlen( payload ) + strlen( dir->d_name ) + 1 ) < MAX_IPC_PAYLOAD_LEN )
                {
                    if ( !isFirst )
                        strcat( payload, ":" );
                    isFirst = false;
                    strcat( payload, dir->d_name );
                }
            }
        }
    }
    ConsolePrintfExit( "----------------------------------------\n" );

    CMostMsgTx *mostMsg = new CMostMsgTx( pAddr, //Addr
        FBLOCK_VOD, // nFBlock
        0, // nInst
        FUNC_VOD_FILE_LIST, // nFunc
        CMostMsg::OP_STATUS, // nOpType
        ( strlen( payload ) + 1 ), //nPayloadLen
        ( uint8_t * )payload, //Payload
        500, //nTimeoutMs
        NULL, //MessageSentCB
        NULL ); //UserContext
    s_mainClass->mostIpcVod->SendMsg( mostMsg );

    free( payload );
    mostMsg->RemoveReference();
}

void CVodHandler::OnVodIndependentStream_SetGet( CMsgAddr *pAddr, CMostMsg *pMsg )
{
    if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg || pMsg->GetPayloadLen() < 2 )
    {
        ConsolePrintf( PRIO_ERROR, RED"OnVodIndependentStream_SetGet parameters are invalid.\n"RESETCOLOR"\n" );
        return;
    }
    uint8_t *payload = pMsg->GetPayload();
    uint8_t cdevIndex = payload[0];
    uint8_t streamIndex = payload[1];
    uint8_t responseOpType = CMostMsg::OP_ERROR;
    uint8_t responsePayloadLength = 2;
    uint8_t responsePayload[8];
    responsePayload[0] = cdevIndex;
    responsePayload[1] = streamIndex;
    if (s_mainClass->CreateMacIndependentStream(cdevIndex, streamIndex, &responsePayload[2]))
    {
        responseOpType = CMostMsg::OP_STATUS;
        responsePayloadLength = sizeof(responsePayload);
    }
    CMostMsgTx *mostMsg = new CMostMsgTx( pAddr, //Addr
        FBLOCK_VOD, // nFBlock
        0, // nInst
        FUNC_VOD_NEW_INDEPEND_STREAM, // nFunc
        responseOpType, // nOpType
        responsePayloadLength, //nPayloadLen
        responsePayload, //Payload
        500, //nTimeoutMs
        NULL, //MessageSentCB
        NULL ); //UserContext
    s_mainClass->mostIpcVod->SendMsg( mostMsg );
    mostMsg->RemoveReference();
}

void CVodHandler::OnFakeMode_Set( CMsgAddr *pAddr, CMostMsg *pMsg )
{
    if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg || pMsg->GetPayloadLen() < 7 )
    {
        ConsolePrintf( PRIO_ERROR, RED"OnFakeMode_Set parameters are invalid.\n"\
            "This could happen, if the client uses an outdated version of the IPC, "\
            " try to update your client."RESETCOLOR"\n" );
        return;
    }
    uint8_t *mac = pMsg->GetPayload();
    uint8_t fakeSinks = pMsg->GetPayload()[6];

    ConsolePrintf( PRIO_MEDIUM,
        GREEN"MOST-IPC Client IP=%s MAC=%02X-%02X-%02X-%02X-%02X-%02X hast started"\
        " %d fake sinks"RESETCOLOR"\n", pAddr->GetInetAddress(), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
        fakeSinks );

    s_mainClass->CreateSampleStreams( fakeSinks );
}

void CVodHandler::OnNmServerVersion_Status( CMsgAddr *pAddr, CMostMsg *pMsg )
{
    if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg )
    {
        ConsolePrintf( PRIO_ERROR, RED"OnNmServerVersion_Status parameters are invalid."RESETCOLOR"\n" );
        return;
    }
    if( !s_mainClass->networkManagerFound )
    {
        s_mainClass->networkManagerFound = true;
        strncpy( s_mainClass->networkManagerIP, pAddr->GetInetAddress(), sizeof ( s_mainClass->networkManagerIP ) );
    }
    if( pMsg->GetPayloadLen() >= 4 )
    {
        uint8_t *p = pMsg->GetPayload();
        ConsolePrintf( PRIO_HIGH, "Got Network Manager version V%02d.%02d.%02d.%02d\n", p[0], p[1], p[2], p[3] );
    }
}

void CVodHandler::OnNmConnectionList_Status( CMsgAddr *pAddr, CMostMsg *pMsg )
{
    if( NULL == s_mainClass || NULL == pAddr || NULL == pMsg )
    {
        ConsolePrintf( PRIO_ERROR, RED"OnNmConnectionList_Status parameters are invalid."RESETCOLOR"\n" );
        return;
    }
    volatile CAutoLock autoLock( &s_mainClass->infoContainerMutex );
    CConnectionInfoContainer *container = new CConnectionInfoContainer();
    if( !container->DeserializeFromString( ( char * )pMsg->GetPayload() ) )
    {
        ConsolePrintf( PRIO_ERROR, RED"Failed to parse connection list"RESETCOLOR"\n" );
        delete container;
        return;
    }
    container->PrintTable( false, false );
    if( 0 == container->GetAllInfoAmount() )
    {
        ConsolePrintf( PRIO_MEDIUM, "No data in connection list, ignoring..\n" );
        delete container;
        return;
    }
    CMultiplexer *multiplexer;
    for( uint32_t i = 0; i < container->GetAmountOfCDevs(); i++ )
    {
        CConnectionInfo *info = container->GetConnectionInfoOfCdev( i );
        if( NULL == info )
            continue;
        if( NULL != s_mainClass->GetMultiplexer( info->deviceName ) )
        {
            ConsolePrintf( PRIO_LOW, "Multiplexer exists for device: '%s', ignoring\n", info->deviceName );
            continue;
        }
        ConsolePrintfStart( PRIO_MEDIUM, "CDev: %s, Type: %d, Direction: %s, MAC: %s, Buffer:%d", info->deviceName,
            info->dataType, ( info->isTX ? "TX" : "RX" ), info->macAddr->ToString(), info->bufferSize );
        
        bool ignored = false;
        for (uint32_t i = 0; i < s_mainClass->ignoreCdevPatterns.Size(); i++)
        {
            char *pattern = s_mainClass->ignoreCdevPatterns[i];
            if ( NULL == pattern )
                continue;
            if ( NULL != strstr(info->deviceName, pattern) )
            {
                ignored = true;
                break;
            }
        }
        
        if( ( !ignored && info->isTX ) && ( ( EP_Isochron == info->dataType ) ||
                ( EP_Synchron == info->dataType && info->reservedBandwidth > 4 ) ) )
        {
            ConsolePrintfExit( ", creating Multiplexer.\n" );
            try
            {
                multiplexer = new CMultiplexer( info->deviceName, info->bufferSize );
                s_mainClass->allMultiplexer.PushBack( multiplexer );
            }
            catch( int e )
            {
                ConsolePrintf( PRIO_ERROR, RED"Failed to instance Multiplexer!"\
                    " Err-Code:%d"RESETCOLOR"\n", e );
            }
        }
        else
        {
            ConsolePrintfExit( ", will be ignored.\n" );
        }
    }
    if( NULL != s_mainClass->infoContainer )
        delete s_mainClass->infoContainer;
    s_mainClass->infoContainer = container;
}

CVodHandler *CVodHandler::GetInstance()
{
    if( NULL == s_mainClass )
        s_mainClass = new CVodHandler();
    return s_mainClass;
}

void CVodHandler::DestroyInstance()
{
    if( NULL != s_mainClass )
    {
        delete s_mainClass;
        s_mainClass = NULL;
    }
}

void CVodHandler::SetSearchPath( const char *pSearchPath )
{
    if( NULL == pSearchPath )
        return;
    strncpy( searchPath, pSearchPath, sizeof ( searchPath ) );

    RegisterBroadcastFiles( SelectFileType_Radio );
    RegisterBroadcastFiles( SelectFileType_Navigation );
    RegisterBroadcastFiles( SelectFileType_Cluster );
}


 void CVodHandler::SetCdevIgnorePattern( const char *pIgnorePattern )
 {
     if (NULL == pIgnorePattern)
         return;
     char *str = (char *)malloc(strlen(pIgnorePattern) + 1);
     strcpy(str, pIgnorePattern);
     ignoreCdevPatterns.PushBack(str);
 }

bool CVodHandler::StreamFile( const uint8_t *mac, SelectFileType_t fileType, const char *fileName )
{
    if( NULL == mac || NULL == fileName || 0 == strlen( fileName ) )
    {
        ConsolePrintf( PRIO_ERROR,
            RED"StreamFile was called with invalid parameters."RESETCOLOR"\n" );
        return false;
    }

    char fullPath[200];
    snprintf( fullPath, sizeof ( fullPath ), "%s/%s/%s", searchPath, GetSubPath( fileType ), fileName );
    if( !ExistsFile( fullPath ) )
    {
        ConsolePrintf( PRIO_ERROR, RED"File was not found: '%s'"RESETCOLOR"\n", fullPath );
        return false;
    }
    CMultiplexer *pMultiplexer = GetMultiplexer( mac );
    if( NULL == pMultiplexer )
    {
        ConsolePrintf( PRIO_ERROR, RED"Could not get Multiplexer for MAC=%02X-%02X-%02X-%02X-%02X-%02X"RESETCOLOR"\n",
            mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] );
        return false;
    }
    CSource *pSource = NULL;
    for( uint32_t i = 0; i < broadcastFiles.Size(); i++ )
    {
        BroadcastEntry_t *entry = broadcastFiles[i];
        if( NULL != entry && ( 0 == strcmp( fullPath, entry->fileName ) ) )
            pSource = entry->sourceFile;
    }
    if( NULL == pSource )
    {
        char *ext = rindex(fullPath, '.');
        if (ext && !strcasecmp(ext, ".ts"))
            pSource = new CSourceFile( fullPath, true );
        else
            pSource = new CSourceFileConverted( fullPath, true );
    }


    CMacAddr macAdress;
    macAdress.CopyValuesFromByteArray( mac );
    pMultiplexer->PlayStream( &macAdress, pSource );
    return true;
}

bool CVodHandler::SetPause( const uint8_t *mac, bool isPaused )
{
    CMultiplexer *pMultiplexer = GetMultiplexer( mac );
    if( NULL == pMultiplexer )
        return false;
    CMacAddr macAdress;
    macAdress.CopyValuesFromByteArray( mac );
    pMultiplexer->SetPause( &macAdress, isPaused );
    return true;
}

bool CVodHandler::SetRepetition( const uint8_t *mac, bool isRepeated )
{
    CMultiplexer *pMultiplexer = GetMultiplexer( mac );
    if( NULL == pMultiplexer )
        return false;
    CMacAddr macAdress;
    macAdress.CopyValuesFromByteArray( mac );
    pMultiplexer->SetRepetition( &macAdress, isRepeated );
    return true;
}

bool CVodHandler::SetTimePos( const uint8_t *mac, uint16_t timePosPerMile )
{
    CMultiplexer *pMultiplexer = GetMultiplexer( mac );
    if( NULL == pMultiplexer )
        return false;
    CMacAddr macAddress;
    macAddress.CopyValuesFromByteArray( mac );
    pMultiplexer->SetPos( &macAddress, timePosPerMile );
    return true;
}

bool CVodHandler::StopStream( const uint8_t *mac )
{
    CMultiplexer *pMultiplexer = GetMultiplexer( mac );
    if( NULL == pMultiplexer )
        return false;
    CMacAddr macAddress;
    macAddress.CopyValuesFromByteArray( mac );
    pMultiplexer->StopStream( &macAddress );
    return true;
}

void CVodHandler::ToggleStatisticsPrint()
{
    statisticIsEnabled = !statisticIsEnabled;
    if (statisticIsEnabled)
        Start();
    else
        Stop();
    valuesCleared = false;
    ConsolePrintf( PRIO_HIGH, "Cyclic printing of statistic informations: %d\n", statisticIsEnabled );
}

void CVodHandler::NM_SendServerVersionGetRequest( const char *ip )
{
    CMsgAddr *msgAddr = new CMsgAddr( ip, IPC_PORT_NUMBER_NM, IpcUdp_V2_0 );
    CMostMsgTx *mostMsg = new CMostMsgTx( msgAddr,
        FBLOCK_NETWORK_MANAGER,
        0,
        FUNC_NM_SERVERVERSION,
        CMostMsg::OP_GET,
        0,
        NULL,
        500,
        NULL,
        NULL );
    s_mainClass->mostIpcNm->SendMsg( mostMsg );
    mostMsg->RemoveReference();
}

void CVodHandler::ConnectToNetworkManager()
{
    do
    {
        NM_SendServerVersionGetRequest( "10.0.0.255" );
        NM_SendServerVersionGetRequest( "127.0.0.1" );
        sleep( 1 );
        if( !networkManagerFound )
            ConsolePrintf( PRIO_ERROR, YELLOW"No Network Manager found, retrying.."RESETCOLOR"\n" );
    }
    while( !networkManagerFound );
}

void CVodHandler::GetConnectionListFromNetworkManager()
{
    if( !networkManagerFound )
    {
        ConsolePrintf( PRIO_ERROR,
            RED"GetConnectionListFromNetworkManager called without server found, aborting.."RESETCOLOR
            "\n" );
        return;
    }
    CMsgAddr *msgAddr = new CMsgAddr( networkManagerIP, IPC_PORT_NUMBER_NM, IpcTcp_V2_0 );
    CMostMsgTx *mostMsg = new CMostMsgTx( msgAddr,
        FBLOCK_NETWORK_MANAGER,
        0,
        FUNC_NM_CONNECTION_LIST,
        CMostMsg::OP_GET,
        0,
        NULL,
        500,
        NULL,
        NULL );
    s_mainClass->mostIpcNm->SendMsg( mostMsg );
    mostMsg->RemoveReference();
}

bool CVodHandler::CreateMacIndependentStream(uint8_t cdevIndex, uint8_t streamIndex, uint8_t *outMac)
{
    volatile CAutoLock autoLock( &infoContainerMutex );
    if (NULL == outMac)
    {
        ConsolePrintf( PRIO_ERROR, RED"CreateMacIndependentStream NULL was passed to parameters"RESETCOLOR"\n" );
        return false;
    }
    if( !networkManagerFound )
    {
        ConsolePrintf( PRIO_ERROR, RED"CreateMacIndependentStream called without"\
            " server found, aborting.."RESETCOLOR"\n" );
        return false;
    }
    if( NULL == infoContainer )
    {
        ConsolePrintf( PRIO_ERROR, RED"CreateMacIndependentStream called without any"\
            " info stored, aborting.."RESETCOLOR"\n" );
        return false;
    }
    CMultiplexer *multiplexer = allMultiplexer[cdevIndex];
    if (NULL == multiplexer)
    {
        ConsolePrintf( PRIO_ERROR, RED"CreateMacIndependentStream with invalid CDEV index:%d"\
            RESETCOLOR"\n", cdevIndex );
        return false;
    }
    for( uint32_t i = 0; NULL != infoContainer && i < infoContainer->GetAllInfoAmount(); i++ )
    {   
        CConnectionInfo *info = infoContainer->GetInfo( i );
        if (NULL == info || NULL == info->deviceName || 0 != strcmp(info->deviceName, multiplexer->GetDriverName()))
            continue;
        
        outMac[0] = 0x2A;
        outMac[1] = 0x11;
        outMac[2] = 0x11;
        outMac[3] = 0x11;
        outMac[4] = (uint8_t)(i & 0xFF);
        outMac[5] = streamIndex;
        CMacAddr mac;
        mac.CopyValuesFromByteArray(outMac);
        CConnectionInfo *fake = infoContainer->GetInfo( &mac, streamIndex, info->mostInstance, false );
        if( NULL == fake )
        {
            ConsolePrintf( PRIO_ERROR, RED"CreateMacIndependentStream failed to create"\
                " new Info object.."RESETCOLOR"\n" );
            return false;
        }
        fake->mostInstance = info->mostInstance;
        fake->dataType = info->dataType;
        fake->reservedBandwidth = info->reservedBandwidth;
        fake->mostConnectionLabel = info->mostConnectionLabel;
        fake->deviceType = 0xAFE;
        fake->inSocketCreated = true;
        fake->outSocketCreated = true;
        fake->socketsConnected = true;
        return true;
    }
    ConsolePrintf( PRIO_ERROR, RED"CreateMacIndependentStream failed to get"\
        " valid Multiplexer for CDEV index: %d"RESETCOLOR"\n", cdevIndex );
    return false;
}

void CVodHandler::CreateSampleStreams( uint8_t amount )
{
    volatile CAutoLock autoLock( &infoContainerMutex );
    if( !networkManagerFound )
    {
        ConsolePrintf( PRIO_ERROR, RED"CreateSampleStreams called without"\
            " server found, aborting.."RESETCOLOR"\n" );
        return;
    }
    if( NULL == infoContainer )
    {
        ConsolePrintf( PRIO_ERROR, RED"CreateSampleStreams called without any"\
            " info stored, aborting.."RESETCOLOR"\n" );
        return;
    }
    uint32_t cdevs = infoContainer->GetAmountOfCDevs();
    uint32_t cdevPos = 0;
    static uint32_t pidNr = 0x40;
    static int32_t lastFileCnt = -1;
    for( uint8_t i = 0; i < amount; i++ )
    {
        char fullPath[200];
        snprintf( fullPath, sizeof ( fullPath ), "%s/video/test-%d.ts", searchPath, ++lastFileCnt );
        
        bool ignored;
        CConnectionInfo *info = NULL;
        do
        {
            ignored = false;
            if( ( cdevPos % cdevs ) == 0 )
                ++pidNr;
            info = infoContainer->GetConnectionInfoOfCdev( cdevPos );
            if( NULL == info )
            {
                ConsolePrintf( PRIO_ERROR, RED"CreateSampleStreams failed to get"\
                    " info.."RESETCOLOR"\n" );
                return;
            }

            for (uint32_t i = 0; i < s_mainClass->ignoreCdevPatterns.Size(); i++)
            {
                char *pattern = s_mainClass->ignoreCdevPatterns[i];
                if ( NULL == pattern )
                    continue;
                if ( NULL != strstr( info->deviceName, pattern ) )
                {
                    if( ++cdevPos >= cdevs )
                        cdevPos = 0;
                    ignored = true;
                    break;
                }
            }
        }
        while(ignored);
        CMultiplexer *mp = GetMultiplexer(info->deviceName);
        if( NULL == mp )
        {
            ConsolePrintf( PRIO_ERROR, RED"CreateSampleStreams failed to get"\
                " valid Multiplexer for device name: %s"RESETCOLOR"\n", info->deviceName );
            return;
        }

        //Create fake sink entry

        CMacAddr *macAddr = new CMacAddr( 0xAF,0xFE,0,0,cdevPos, pidNr );
        ConsolePrintf( PRIO_HIGH, "Will add file '%s' to Multiplexer with MAC: %s to CDEV: %s\n",
            fullPath, macAddr->ToString(), info->deviceName );
        CConnectionInfo *fake = infoContainer->GetInfo( macAddr, 47, info->mostInstance, false );
        if( NULL == fake )
        {
            ConsolePrintf( PRIO_ERROR, RED"CreateSampleStreams failed to create"\
                " new Info object.."RESETCOLOR"\n" );
            return;
        }
        fake->mostInstance = info->mostInstance;
        fake->dataType = info->dataType;
        fake->reservedBandwidth = info->reservedBandwidth;
        fake->mostConnectionLabel = info->mostConnectionLabel;
        fake->deviceType = 0xAFE;
        fake->inSocketCreated = true;
        fake->outSocketCreated = true;
        fake->socketsConnected = true;
        
        char *ext = rindex(fullPath, '.');
        if (ext && !strcasecmp(ext, ".ts"))
            mp->PlayStream( fake->macAddr, new CSourceFile( fullPath, true ) );
        else
            mp->PlayStream( fake->macAddr, new CSourceFileConverted( fullPath, true ) );
        mp->SetRepetition( fake->macAddr, true );

        if( ++cdevPos >= cdevs )
            cdevPos = 0;
    }
    ConsolePrintf( PRIO_HIGH, GREEN"CreateSampleStreams created %d sample streams.."\
        RESETCOLOR"\n", amount );
    infoContainer->PrintTable( false, false );
}

void CVodHandler::Run()
{
#define THREAD_SLEEP_TIME_IN_SEC 1
    if( statisticIsEnabled )
    {
        if( !valuesCleared )
        {
            valuesCleared = true;
        }
        uint64_t allBytesWritten = 0;

        ConsolePrintfStart( PRIO_HIGH, "==============Statistic Start==============\n" );
        pthread_mutex_lock( &infoContainerMutex );
        for( uint32_t i = 0; NULL != infoContainer && i < infoContainer->GetAllInfoAmount(); i++ )
        {
            bool success = false;
            CMultiplexer::MultiplexStats_t stats;
            CConnectionInfo *info = infoContainer->GetInfo( i );
            if( NULL == info || NULL == info->macAddr || ( ( EP_Isochron != info->dataType ) &&
                ( EP_Synchron == info->dataType && info->reservedBandwidth <= 4 ) ) )
                continue;
            CConnectionInfo *remoteInfo =
                infoContainer->GetRemoteConnectionInfoByMacAddress( info->macAddr->GetBytes() );
            if( NULL == remoteInfo || remoteInfo == info )
                continue;
            CMultiplexer *multiplexer = GetMultiplexer( remoteInfo->deviceName );
            if( NULL == multiplexer )
                continue;
            success = multiplexer->GetStatistics( info->macAddr, &stats );
            if( success )
            {
                allBytesWritten += stats.bytesSent + stats.bytesStuffed;
                ConsolePrintfContinue( "Device:%s, Stream %s, sent:%lld (kBit/s), stuffed:%lld (kBit/s),"\
                    " read:%lld (kBit/s), PCR-error:%lld, Buf Ovfl-error:%lld, "\
                    "Buf Unfl-error:%lld, TS-error:%lld\n", multiplexer->GetDriverName(),
                    info->macAddr->ToString(), ( 8 * stats.bytesSent / 1000 / THREAD_SLEEP_TIME_IN_SEC ), ( 8 *
                    stats.bytesStuffed / 1000 / THREAD_SLEEP_TIME_IN_SEC ), ( 8 * stats.bytesRead / 1000 /
                    THREAD_SLEEP_TIME_IN_SEC ), stats.errPcr, stats.errBufOverflow, stats.errBufUnderflow,
                    stats.errTs );
            }
        }
        for( uint32_t i = 0; NULL != infoContainer && i < infoContainer->GetAmountOfCDevs(); i++ )
        {
            bool success = false;
            CMultiplexer::MultiplexStats_t stats;
            CConnectionInfo *info = infoContainer->GetConnectionInfoOfCdev( i );
            if( NULL == info )
                continue;
            
            CMultiplexer *multiplexer = GetMultiplexer( info->deviceName );
            if( NULL == multiplexer )
                continue;
            success = multiplexer->GetStatistics( info->macAddr, &stats );
            if( success )
            {
                allBytesWritten += stats.bytesSent + stats.bytesStuffed;
                ConsolePrintfContinue( "Device:%s, Stream %s, sent:%lld (kBit/s), stuffed:%lld (kBit/s),"\
                    " read:%lld (kBit/s), PCR-error:%lld, Buf Ovfl-error:%lld, "\
                    "Buf Unfl-error:%lld, TS-error:%lld\n", multiplexer->GetDriverName(),
                    info->macAddr->ToString(), ( 8 * stats.bytesSent / 1000 / THREAD_SLEEP_TIME_IN_SEC ), ( 8 *
                    stats.bytesStuffed / 1000 / THREAD_SLEEP_TIME_IN_SEC ), ( 8 * stats.bytesRead / 1000 /
                    THREAD_SLEEP_TIME_IN_SEC ), stats.errPcr, stats.errBufOverflow, stats.errBufUnderflow,
                    stats.errTs );
            }
        }
        pthread_mutex_unlock( &infoContainerMutex );
        ConsolePrintfContinue( "Overall sent performance of all streams: %lld (kBit/s)\n", ( 8 * allBytesWritten /
            1000 / THREAD_SLEEP_TIME_IN_SEC ) );
        ConsolePrintfExit( "===============Statistic End===============\n" );
    }
    usleep( THREAD_SLEEP_TIME_IN_SEC * 1000000 );
}