/*
 * 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 3 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 <stdio.h>
#include <stddef.h>
#include "Console.h"
#include "NodeDatabase.h"

CSafeVector<CNodeEntry *> CNodeEntry::allNodes;

CNodeConnectionEntry::CNodeConnectionEntry( CNodeEntry *parent ) : parentNode( parent ), channelConfig( NULL ),
    channelId( 0xFFFFFFFF ), inHandle( 0xFFFFFFFF ),
    outHandle( 0xFFFFFFFF ),
    connectHandle( 0xFFFFFFFF ),
    splittedSourceHandle( 0xFFFFFFFF ),
    mostConnectionLabel( 0xFFFFFFFF ),
    inSocketState( NodeConnection_NotUsed ),
    outSocketState( NodeConnection_NotUsed ),
    connectedSocketState( NodeConnection_NotUsed ),
    bufferSize( -1 )
{
    memset( deviceName, 0, sizeof( deviceName ) );
};

CNodeConnectionEntry::~CNodeConnectionEntry()
{
    if( NULL != channelConfig )
    {
        delete channelConfig;
        channelConfig = NULL;
    }
}

CNodeEntry::CNodeEntry() : deviceInstance( 0xFF ), nodeAddress( 0xFFFF ), deviceType( 0xFFFF ), macAddress( NULL ),
    isTsiPortOpened(false), isMlbPortOpened( false ), isUsbPortOpened( false ), isI2SPortOpened( false )
{
}

CNodeEntry::~CNodeEntry()
{
    if( NULL != macAddress )
        delete macAddress;
}

uint16_t CNodeEntry::GetAmountOfNodeEntries()
{
    return allNodes.Size();
}

CNodeEntry *CNodeEntry::GetNodeEntry( uint16_t index )
{
    return allNodes[index];
}

CNodeEntry *CNodeEntry::GetNodeEntry( uint8_t deviceInstance, uint16_t nodeAddress )
{
    CNodeEntry *entry = NULL;
    for( uint32_t i = 0; i < allNodes.Size(); i++ )
    {
        if( ( allNodes[i]->deviceInstance == deviceInstance )
            && ( allNodes[i]->nodeAddress == nodeAddress ) )
        {
            entry = allNodes[i];
            break;
        }
    }
    if( NULL == entry )
    {
        entry = new CNodeEntry();
        entry->deviceInstance = deviceInstance;
        entry->nodeAddress = nodeAddress;
        allNodes.PushBack( entry );
    }
    return entry;
}

CNodeEntry *CNodeEntry::GetNodeEntry( CMacAddr *macAddress )
{
    if( NULL == macAddress )
        return NULL;
    CNodeEntry *entry = NULL;
    for( uint32_t i = 0; i < allNodes.Size(); i++ )
    {
        CNodeEntry *node = allNodes[i];
        if( ( NULL != node->macAddress ) && ( *node->macAddress == *macAddress ) )
        {
            entry = node;
            break;
        }
    }
    return entry;
}

CNodeEntry *CNodeEntry::GetNodeEntry( uint8_t deviceInstance, uint32_t deviceType, uint32_t terminalInstance )
{
    uint32_t inst = 0;
    CNodeEntry *entry = NULL;
    for( uint32_t i = 0; i < allNodes.Size(); i++ )
    {
        CNodeEntry *node = allNodes[i];
        if( ( allNodes[i]->deviceInstance == deviceInstance )
            && ( allNodes[i]->deviceType == deviceType ) )
        {
            if( inst == terminalInstance )
            {
                entry = node;
                break;
            }
            else
            {
                ++inst;
            }
        }
    }
    return entry;
}

uint32_t CNodeEntry::GetTerminalInstance( uint8_t deviceInstance, uint16_t nodeAddress, uint32_t deviceType )
{
    uint32_t terminalInst = 0;
    for( uint32_t i = 0; i < allNodes.Size(); i++ )
    {
        if( ( allNodes[i]->deviceInstance == deviceInstance )
            && ( allNodes[i]->deviceType == deviceType ) )
        {
            if( ( allNodes[i]->nodeAddress == nodeAddress ) )
                break;
            else
                ++terminalInst;
        }
    }
    return terminalInst;
}

void CNodeEntry::DeleteAllNodeEntries()
{
    allNodes.RemoveAll(true);
}

void CNodeEntry::DeleteNodeEntry( uint16_t index )
{
    CNodeEntry *ent = allNodes[index];
    if( NULL == ent )
        return;

    allNodes.Remove(ent);
    delete ent;
}

const char *CNodeEntry::GetConnectionStateString( NodeConnectionState_t conState )
{
    switch( conState )
    {
    case NodeConnection_NotUsed:
        return "Idle";
    case NodeConnection_Pending_Up:
        return "Pending-Up";
    case NodeConnection_Pending_Down:
        return "Pending-Down";
    case NodeConnection_Used:
        return "Established";
    default:
        return "Unknown State";
    }
}

void CNodeEntry::PrintAllInformations()
{
    ConsolePrintfStart( PRIO_LOW, "==================================================================\n" );
    for( uint32_t i = 0; i < allNodes.Size(); i++ )
    {
        CNodeEntry *entry = allNodes[i];
        ;
        if( NULL != entry )
        {
            ConsolePrintfContinue( "Inst:%d, NodeAddr:0x%X, DevType:0x%X, MAC:%s, open:%d\n", entry->deviceInstance,
                entry->nodeAddress, entry->deviceType, ( NULL != entry->macAddress ? entry->macAddress->ToString() :
                "None" ), ( entry->isUsbPortOpened || entry->isMlbPortOpened ) );
            for( uint32_t j = 0; j < entry->GetAmountOfConnections(); j++ )
            {
                CNodeConnectionEntry *con = entry->GetConnectionByIndex( j );
                ConsolePrintfContinue(
                    "Id:%d, inHandle:0x%X, outHandle:0x%X, conHandle:0x%X, conLabel:0x%X, inState:%s, outState:%s, conState:%s, bufferSize:%d, name:%s\n", con->channelId, con->inHandle, con->outHandle, con->connectHandle, con->mostConnectionLabel, GetConnectionStateString( con->inSocketState ), GetConnectionStateString( con->outSocketState ), GetConnectionStateString( con->connectedSocketState ), con->bufferSize, con->deviceName );
            }
        }
        if( i < allNodes.Size() - 1 )
            ConsolePrintfContinue( "------------------------------------------------------------------\n" );
    }
    ConsolePrintfExit( "==================================================================\n" );
}

uint16_t CNodeEntry::GetAmountOfConnections()
{
    return allConnections.Size();
}

CNodeConnectionEntry *CNodeEntry::GetConnectionByIndex( uint16_t index )
{
    return allConnections[index];
}

CNodeConnectionEntry *CNodeEntry::GetConnectionByChannelId( uint32_t channelId, bool createNewIfUnknown )
{
    CNodeConnectionEntry *entry = NULL;
    for( uint32_t i = 0; i < allConnections.Size(); i++ )
    {
        if( allConnections[i]->channelId == channelId )
        {
            entry = allConnections[i];
            break;
        }
    }
    if( createNewIfUnknown && NULL == entry )
    {
        entry = new CNodeConnectionEntry( this );
        entry->channelId = channelId;
        allConnections.PushBack( entry );
    }
    return entry;
}

CNodeConnectionEntry *CNodeEntry::GetConflictingConnection( uint32_t channelId, CNodeEntry *opposite )
{
    if( NULL == opposite )
        return NULL;
    if( opposite->deviceInstance != deviceInstance )
        return NULL;
    CNodeConnectionEntry *entry = NULL;
    uint32_t connectionLabel = 0xFFFFFFFF;
    for( uint32_t i = 0; i < allConnections.Size(); i++ )
    {
        if( allConnections[i]->channelId == channelId )
        {
            connectionLabel = allConnections[i]->mostConnectionLabel;
            break;
        }
    }
    if( 0xFFFFFFFF != connectionLabel )
    {
        for( uint32_t i = 0; i < opposite->allConnections.Size(); i++ )
        {
            if( opposite->allConnections[i]->mostConnectionLabel == connectionLabel )
            {
                entry = opposite->allConnections[i];
                break;
            }
        }
    }
    return entry;
}

void CNodeEntry::DeleteConnection( uint32_t channelId )
{
    uint32_t index = 0xFFFFFFFF;
    for( uint32_t i = 0; i < allConnections.Size(); i++ )
    {
        if( allConnections[i]->channelId == channelId )
        {
            index = i;
        }
    }
    if( index >= allNodes.Size() )
        return;
    CNodeConnectionEntry *ent = allConnections[index];
    allConnections.Remove(ent);
    delete ent;
}