/*
* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*
*/


#include "cmdnscachemanager.h"
#include "cserviceinfo.h"
#include "ccacheentry.h"
#include "dnsconstants.h"

using namespace HashInfo;

EXPORT_C CMDNSCacheManager* CMDNSCacheManager::NewL(TUint aMaxCacheEntries)
	{
	CMDNSCacheManager* self = CMDNSCacheManager::NewLC(aMaxCacheEntries);
	CleanupStack::Pop(self);
	return self;
	}

EXPORT_C CMDNSCacheManager* CMDNSCacheManager::NewLC(TUint aMaxCacheEntries)
	{
	CMDNSCacheManager* self = new (ELeave)CMDNSCacheManager();
	CleanupStack::PushL(self);
	self->ConstructL(aMaxCacheEntries);
	return self;
	}

EXPORT_C CMDNSCacheManager* CMDNSCacheManager::NewL()
	{
	CMDNSCacheManager* self = new (ELeave)CMDNSCacheManager();
	return self;
	}

void CMDNSCacheManager::ConstructL(TUint aMaxCacheEntries)
	{
	iHashMap=CMDNSCacheMap::NewL(aMaxCacheEntries);
	}

CMDNSCacheManager::CMDNSCacheManager()
	{
	
	}

CMDNSCacheManager::~CMDNSCacheManager()
	{
	delete iHashMap;
	iAuthoritativeEntries.ResetAndDestroy();
	iAuthoritativeEntries.Close();
	}

/*Method to Insert / Update entries into Cache
 * @param aEntry Record Entry to Insert
 * @param aAuthoritative Flag to indicate whether the entry is Authoritative
 * @param aSessionId To Store the Session Id for each entry
 * @leave KErrNoMemory If Insert into Cache Failed because memory unavailability
 * @leave KErrNotSupported if any record other than SRV, PTR, TXT ot A come
 */
EXPORT_C void CMDNSCacheManager::UpdateCacheL(CDnsResourceData& aEntry,TBool aAuthoritative,TUint32 aSessionId)
	{			
	CDnsResourceData* resourceRecord = NULL;
	CServiceInfo* foundEntry = NULL;
	TBuf8<255> name;
	 _LIT8(KDot,".");
	//Clone the entry to Insert in Cache
	resourceRecord = aEntry.CloneL();				
	
	TUint16 recType(resourceRecord->Type());
	
	if(recType == EDnsType_SRV || recType == EDnsType_TXT || recType == EDnsType_A || recType == EDnsType_PTR)
		{
		if(recType == EDnsType_PTR)
			{
			//Extract the Key- Service Instance Name
			CRdTypePtr*  ptrRecord = static_cast <CRdTypePtr*> (resourceRecord);
			// before that insert the dot. 
		    name.Copy(ptrRecord->DomainName());
            if(name[name.Size()-1]!='.')
                name.Append(KDot);
		    ptrRecord->SetDomainNameL(name);
			foundEntry = iHashMap->Find(ptrRecord->DomainName());						
			}
		else
			{
			//Extract the Key- Service Instance Name or Host Name
			foundEntry = iHashMap->Find(resourceRecord->Name());			
			}

		if(foundEntry)
			{
			//Update if the entry is already present in the Cache.
			UpdateCacheEntry(foundEntry,resourceRecord);
			}
		else
			{			
			//Entry not found in Cache,Insert it fresh
			CServiceInfo* recordInfo = NULL;
			recordInfo = CServiceInfo::NewL();
			switch(recType)
				{
				case EDnsType_SRV:
				CRdTypeSrv*  srvRecord = static_cast <CRdTypeSrv*> (resourceRecord);			
				recordInfo->SetServiceRecord(srvRecord);
				break;

				case EDnsType_TXT:
				CRdTypeTxt*  txtRecord = static_cast <CRdTypeTxt*> (resourceRecord);
				recordInfo->SetTxtRecord(txtRecord);
				break;

				case EDnsType_PTR:
				CRdTypePtr*  ptrRecord = static_cast <CRdTypePtr*> (resourceRecord);
				recordInfo->SetPtrRecord(ptrRecord);			
				break;

				case EDnsType_A:
				CRdTypeA*  addressRecord = static_cast <CRdTypeA*> (resourceRecord);
				recordInfo->SetAddressRecord(addressRecord);
				break;
				
				
				default:
				User::Leave(KErrNotSupported);
				break;
				}
				
				recordInfo->SetAuthoritative(aAuthoritative);
				recordInfo->SetSessionId(aSessionId);

				
				// insert the entry
				if(recType == EDnsType_PTR )
					{					
					CRdTypePtr*  ptrRecord = static_cast <CRdTypePtr*> (resourceRecord);
					iHashMap->UpdateL(ptrRecord->DomainName(),recordInfo);			
					}
				else
	 				{
					iHashMap->UpdateL(resourceRecord->Name(),recordInfo);										
					}
				
				/*Maintain a list of all Services Published by us
				(Authoritative records)*/
				if(recordInfo->IsAuthoritative())
				{
				iAuthoritativeEntries.Append(recordInfo->CloneL());	
				}										
			}			
		
		}
	else 
		{
		User::Leave(KErrNotSupported);
		}

	}




/*Caters to finding varying types of records, for e.g:- find all the entries
	 that correspond to a particular service type or find the host name through the address record
	 @param aEntries List of entries that match the search constraint
	 @param	aName could represent any search constraint Service Instance Type/ Name or the HostName 
	 @param aType the desired record type
 */
EXPORT_C TInt CMDNSCacheManager::FindServiceL(RPointerArray <CCacheEntry> & aEntries,const TDesC8& aName,TDnsType aType)const
	{
	TInt error(KErrNotFound);
	TPtrC8 keyToFind(aName);
	TInt count;
	switch(aType)
		{
		case EDnsType_A:
		case EDnsType_SRV:
		case EDnsType_TXT:
			{
			//Search for a specific CacheEntry
			CServiceInfo* cacheEntry = iHashMap->Find(aName);
			if(cacheEntry)
				{
				CCacheEntry* entry = CCacheEntry::NewL();
				CacheEntryL(entry,cacheEntry);
				aEntries.Append(entry);
				error = KErrNone;
				}			
			}
			break;
		
		
		case EDnsType_PTR:
		//Match for all Cache Entries of the given Service Type
		RPointerArray <const CServiceInfo> entries;
		CleanupClosePushL(entries);
		iHashMap->FindEntries(aName,entries);
		
		for(count =0; count< entries.Count();count++)
			{
			if((*entries[count]).PtrRecord())
				{
				//The entries are considered only if they have PTR records
				CCacheEntry* entry = CCacheEntry::NewL();
				CacheEntryL(entry,entries[count]);							
				aEntries.Append(entry);	
				error = KErrNone;					
				}			
			}

		CleanupStack::PopAndDestroy(&entries);
		break;

		
        case EDnsQType_Any:
            {
            //Search for a specific CacheEntry
            CServiceInfo* cacheEntry = iHashMap->Find(aName);
            if(cacheEntry)
                {
                CCacheEntry* entry = CCacheEntry::NewL();
                CacheEntryL(entry,cacheEntry);
                aEntries.Append(entry);
                error = KErrNone;
                }
            else  // may be its a PTR record
                {
                RPointerArray <const CServiceInfo> entries;
                CleanupClosePushL(entries);
                iHashMap->FindEntries(aName,entries);
                
                for(count =0; count< entries.Count();count++)
                    {
                    if((*entries[count]).PtrRecord())
                        {
                        //The entries are considered only if they have PTR records
                        CCacheEntry* entry = CCacheEntry::NewL();
                        CacheEntryL(entry,entries[count]);                          
                        aEntries.Append(entry); 
                        error = KErrNone;                   
                        }           
                    }

                CleanupStack::PopAndDestroy(&entries);
                }
            }
            
        break;
		
		default:
		User::Leave(KErrNotSupported);		
		break;
		}
		return error;
	}

/*Deletes the specified entry from the Cache
	 @param aServiceInstanceName Instance Name , whose entry needs to be deleted 
	 @leave KErrNotFound if specified entry is not found
	 */
EXPORT_C TInt CMDNSCacheManager::DeleteEntryL(const TDesC8& aServiceInstanceName)
	{
	TInt err =  iHashMap->DeleteL(aServiceInstanceName);
	
	// check if it also on the authoritative list, if so delete it
	
	for(TInt i=0; i<iAuthoritativeEntries.Count();i++)
	    {
	    if(iAuthoritativeEntries[i]->Key().Compare(aServiceInstanceName)==0)
	        {
	        // found the entry, now delete it;
	        CServiceInfo* info = iAuthoritativeEntries[i];
	        iAuthoritativeEntries.Remove(i);
	        delete info;
	        }
	    }
	return err;
	}


EXPORT_C void CMDNSCacheManager::FlushCache()
	{
	iHashMap->DeleteAllEntries();
	}

EXPORT_C CCacheEntry* CMDNSCacheManager::NextStaleEntry(TBool aActiveCacheMgmtEnabled,TBool aIteratorReset)
 	{
	CServiceInfo* nextEntry = iHashMap->NextStaleEntry(aActiveCacheMgmtEnabled,aIteratorReset);
	CCacheEntry* nextCacheEntry = NULL;
	
	if(nextEntry)		
		{
		
		 nextCacheEntry = CCacheEntry::NewL();
		//Construct CCacheEntry object
		CacheEntryL(nextCacheEntry,nextEntry);
		
		//Mark whether the entry has to be deleted or to be queried for (i.e., has exceeded 80%)
		nextCacheEntry->SetEntryExpired(nextEntry->StaleEntry());
		}
	
	return nextCacheEntry;
	}

void CMDNSCacheManager::UpdateCacheEntry(CServiceInfo* aEntry,CDnsResourceData* aRecordInfo)
	{
	TUint16 recType(aRecordInfo->Type());
	switch(recType)
		{
		case EDnsType_SRV:
		CRdTypeSrv*  srvRecord = static_cast <CRdTypeSrv*> (aRecordInfo);
		//Delete the previous Service Record and Insert the fresh one
		delete aEntry->ServiceRecord();
		aEntry->SetServiceRecord(srvRecord);
		break;

		case EDnsType_TXT:
		CRdTypeTxt*  txtRecord = static_cast <CRdTypeTxt*> (aRecordInfo);
		//Delete the previous Text Record and Insert the fresh one
		delete aEntry->TxtRecord();
		aEntry->SetTxtRecord(txtRecord);
		break;

		case EDnsType_PTR:
		CRdTypePtr*  ptrRecord = static_cast <CRdTypePtr*> (aRecordInfo);
		//Delete the previous Ptr Record and Insert the fresh one
		delete aEntry->PtrRecord();
		aEntry->SetPtrRecord(ptrRecord);			
		break;

		case EDnsType_A:
		CRdTypeA*  addressRecord = static_cast <CRdTypeA*> (aRecordInfo);
		//Delete the previous Address Record and Insert the fresh one
		delete aEntry->AddressRecord();
		aEntry->SetAddressRecord(addressRecord);
		break;
		
		default:
		//Do nothing
		break;
		}
	}
	
EXPORT_C void CMDNSCacheManager::DumpCacheL()
	{
	iHashMap->DumpCacheL();
	}
	
EXPORT_C	TUint32 CMDNSCacheManager::CacheEntryCount()
	{
	return iHashMap->NumberOfEntries();	
	}
	
	
void CMDNSCacheManager::CacheEntryL(CCacheEntry* aTargetEntry,const CServiceInfo* aSourceEntry)const
	{
	//Check whether the specific records existed before making a copy 
	if(aSourceEntry->PtrRecord())
		{
		aTargetEntry->SetPtrRecord(static_cast <CRdTypePtr*>( aSourceEntry->PtrRecord()->CloneL()));
		}
		
	if(aSourceEntry->AddressRecord())	
		{
		//This case occurs only for an idividual Address Record
		aTargetEntry->SetAddressRecord(static_cast <CRdTypeA*>(aSourceEntry->AddressRecord()->CloneL()));
		}
	
	if(aSourceEntry->ServiceRecord())		
		{		
		aTargetEntry->SetServiceRecord(static_cast <CRdTypeSrv*>(aSourceEntry->ServiceRecord()->CloneL()));
		//Set Address Record ; if it exists
		CServiceInfo* entry = iHashMap->Find(aTargetEntry->ServiceRecord()->Target());
		if(entry)
			{
			aTargetEntry->SetAddressRecord(static_cast <CRdTypeA*>(entry->AddressRecord()->CloneL()));
			}		
		}
	
	if(aSourceEntry->TxtRecord())
		{
		aTargetEntry->SetTxtRecord(static_cast <CRdTypeTxt*>(aSourceEntry->TxtRecord()->CloneL()));
		}
		
	aTargetEntry->SetAuthoritative(aSourceEntry->IsAuthoritative());
	aTargetEntry->SetSessionId(aSourceEntry->SessionId());
	}

	
void CMDNSCacheManager::AuthoritativeEntriesL(RPointerArray <CCacheEntry>& aCacheEntries)
	{	
	for(TInt index =0; index < iAuthoritativeEntries.Count();index++)
		{				
		CCacheEntry* entry = CCacheEntry::NewL();
		CacheEntryL(entry,iAuthoritativeEntries[index]);							
		aCacheEntries.Append(entry);											
		}		
	}
	
TUint CMDNSCacheManager::NumberOfEntries()
	{
	return iHashMap->NumberOfEntries();
	}
