/*
* 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 "cmdnscachemap.h"
#include "mdnsrecordinfo.h"
#include <mdns/crdtypesrv.h>
#include <mdns/crdtypea.h>
#include <mdns/crdtypeptr.h>
#include <mdns/crdtypetxt.h>


__FLOG_STMT(_LIT8(KComponent,"MDNSCache");)

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

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

void CMDNSCacheMap::ConstructL()
	{
	__FLOG_OPEN(KMDNSSubsystem, KComponent);
	__FLOG(_L8("ConstructL - Entry"));
	
	__FLOG(_L8("ConstructL - Exit")); 
	}

CMDNSCacheMap::CMDNSCacheMap(TUint aMaxCacheEntries):iCacheEntries(HashInfo::TPtrC8Hash,HashInfo::TPtrC8Ident),iterate(iCacheEntries)
	{
	iMaxCacheEntries = aMaxCacheEntries;
	}


CMDNSCacheMap::~CMDNSCacheMap()
	{
	__FLOG(_L8("Destructor - Entry"));
	RHashMap<TPtrC8,CServiceInfo*>::TIter iter(iCacheEntries);
	TInt count(0);
	TInt entriesToDelete(iCacheEntries.Count());
	for(count=0; count<entriesToDelete; count++)
		{
		 delete *iter.NextValue();
		}
		
	iCacheEntries.Close();
	__FLOG(_L8("Destructor - Exit"));
	__FLOG_CLOSE;
	}
	
TUint32 HashInfo::TPtrC8Hash(const TPtrC8& aPtr)
	{
	return DefaultHash::Des8(aPtr);
	}

TBool HashInfo::TPtrC8Ident(const TPtrC8& aL, const TPtrC8& aR)
	{
	return DefaultIdentity::Des8(aL, aR);
	}
		
	
void CMDNSCacheMap::UpdateL(const TDesC8& aName, CServiceInfo* aEntry)
	{
	__FLOG(_L8("Insert - Entry"));
	TTime currentTime;
	currentTime.UniversalTime();
	aEntry->SetAccessTime(currentTime);
	TBool authoritative = aEntry->IsAuthoritative();
	
	//Save the key
	aEntry->SetKeyL(aName);
	
	if(NumberOfEntries()<=iMaxCacheEntries)
		{
		iCacheEntries.InsertL(aEntry->Key(),aEntry);
		}
	else
	{
		if(aEntry->IsAuthoritative() && DeleteLeastRecentlyUsed())
			{
			iCacheEntries.InsertL(aEntry->Key(),aEntry);		
			}
		else
			{
			delete aEntry;
			__FLOG_VA((_L8("--------User::Leave on Insert  = %d--------"),KErrNoMemory));
			User::Leave(KErrNoMemory);
			}
		}
		
	__FLOG(_L8("Insert - Exit"));

	}

CServiceInfo* CMDNSCacheMap::Find(const TDesC8& aName)
	{
	TTime currentTime;
	RBuf8 key;
	key.CreateL(aName);
	
	//All keys are in LowerCase
	key.LowerCase();
	
	CServiceInfo** foundEntry = NULL;	
	foundEntry = iCacheEntries.Find(key);
	key.Close();
	currentTime.UniversalTime();
	if(foundEntry)
		{		
		(*foundEntry)->SetAccessTime(currentTime);
		return *foundEntry;
		}	
	return NULL;	
	}

void CMDNSCacheMap::FindEntries(const TDesC8& aName,RPointerArray <const CServiceInfo> & aEntries)
	{	
	_LIT8(KRegExp,"*");
	TInt error; 
	TBuf8 <256> serviceType(KRegExp);
	TTime currentTime;
	serviceType.Append(aName);
	THashMapIter<TPtrC8,CServiceInfo*> iter(iCacheEntries);
    TInt count;
     for(count=0; count<iCacheEntries.Count(); count++)
         {                        
         TPtrC8 nextKey=*iter.NextKey();
         //Match by the regular expression to get all Services by type
         error=nextKey.MatchF(serviceType);
         if( error != KErrNotFound)
        	 {
        	 CServiceInfo* currentEntry= *iter.CurrentValue();
        	 aEntries.Append(currentEntry);
        	 
        	 currentTime.UniversalTime();
        	 currentEntry->SetAccessTime(currentTime);
        	 }         
         }
	}

/*Not Used : Currently*/
void CMDNSCacheMap::AuthoritativeRecordsL(RPointerArray <const CServiceInfo> & aEntries)
	{
	THashMapIter<TPtrC8,CServiceInfo*> iter(iCacheEntries);
    TInt count;
    TTime currentTime;
     for(count=0; count<iCacheEntries.Count(); count++)
         {                        
         CServiceInfo* entry=*iter.NextValue();
         if(entry->IsAuthoritative())
        	 {
        	 aEntries.AppendL(entry);
        	 currentTime.UniversalTime();
        	 entry->SetAccessTime(currentTime);
        	 }         
         }
	}

TInt CMDNSCacheMap::DeleteL(const TDesC8& aName)
	{
	__FLOG(_L8("CMDNSCacheMap::DeleteL - Entry"));	
	TInt error = KErrNone;
	TBuf8 <256> key(aName);
	
	//All keys are in LowerCase
	key.LowerCase();

    #ifdef __FLOG_ACTIVE
    
        HBufC8* buf(HBufC8::NewLC(aName.Length()));
        buf->Des().Copy(aName); 
        __FLOG_VA((_L8("Deleted Entry = %S "),buf));        
        CleanupStack::PopAndDestroy(buf);
    
    #endif
	
	CServiceInfo* entryToDelete = NULL;
	TRAPD(err,entryToDelete=iCacheEntries.FindL(key));
	if(err!=KErrNotFound)
	    {
	    error = iCacheEntries.Remove(key);
	    delete entryToDelete;
	    __FLOG(_L8("CMDNSCacheMap::DeleteL - Delete Cache Entry Successful"));  
	    __FLOG(_L8("CMDNSCacheMap::DeleteL - Exit"));
	    }
	else
	    {
	    __FLOG(_L8("CMDNSCacheMap::DeleteL - Entry not found in Cache"));
	    }
	return error;
	}

void CMDNSCacheMap::DeleteAllEntries()
	{
	THashMapIter<TPtrC8,CServiceInfo*> iter(iCacheEntries);
	TInt count;
	for(count=0; count<iCacheEntries.Count(); count++)
		{                        
		TPtrC8 key=*iter.NextKey();
		/*TODO: check if object is properly deleted;
		 * destructors are called*/
		iCacheEntries.Remove(key);
		delete *iter.NextValue();
		}
	}


/* Scenarios:

1. All are authoritative in Cache, authoritative record comes for insertion : No Memory
2. All are authoritative in Cache, non-authoritative comes : No Memory
3. One or more non-authoritative, non-authoritative comes : No Memory
4. All are non-authoritative,authoritative comes, one of the records 
	based on LRU (not accurate)of which is deleted, but authoritative WILL BE 
   put into cache
  */

TBool CMDNSCacheMap::DeleteLeastRecentlyUsed()
	{
	THashMapIter<TPtrC8,CServiceInfo*> iter(iCacheEntries);
	TInt count;
	TTime accessTime,timeOfLRUEntry;
	CServiceInfo* entryToDelete = NULL;
	TBuf8<255> keyToDelete;
	TBool cacheFull = ETrue;

	for(count=0; count<iCacheEntries.Count(); count++)
		{                                 
		CServiceInfo* nextEntry = *iter.NextValue();
		accessTime = nextEntry->LastAccessTime();
		
		if(count==0)
			{
			timeOfLRUEntry = accessTime;
			if(!nextEntry->IsAuthoritative())
				{
				keyToDelete = *iter.CurrentKey();
				entryToDelete = nextEntry;
				}
			}
			
		else
		  {
		 	if((accessTime < timeOfLRUEntry) && !nextEntry->IsAuthoritative() )
		 		{
		 		timeOfLRUEntry = accessTime;
		 		keyToDelete = *iter.CurrentKey();
				entryToDelete = nextEntry;
		 		}		 	
		  }
		
		}
	
	TInt error = iCacheEntries.Remove(keyToDelete);	
	
	if(error !=  KErrNone)
		{
		cacheFull =  EFalse;								
		}
	else
	{
	#ifdef __FLOG_ACTIVE
		
		__FLOG_VA((_L8("Deleting Least Recently Used = %S "),&keyToDelete));
	
	#endif
	}
		
		
	delete entryToDelete;
	
	return cacheFull;
	}

TUint32 CMDNSCacheMap::NumberOfEntries()
	{
	return iCacheEntries.Count();
	}


CServiceInfo* CMDNSCacheMap::NextStaleEntry(TBool aActiveCacheMgmtEnabled,TBool aIteratorReset)
	{
/*
	 if(aIteratorReset)
	 	{
	 	iterate.Reset();
	 	}
	 	
	 CServiceInfo* nextEntry=*iterate.NextValue();
	 	
	 if(nextEntry->EntryExpired() || nextEntry->EntryToBeQueried())
	 	{
	 	__FLOG(_L8("Stale Entry Found"));
	 	return nextEntry;
	 	}

	 return NULL;
	 
*/
    THashMapIter<TPtrC8,CServiceInfo*> itr(iCacheEntries);
	CServiceInfo*const* nextEntry=itr.NextValue();
	
	while(nextEntry!=NULL)
	    {
        if((*nextEntry)->EntryExpired())
            {
            __FLOG(_L8("Stale Entry Found"));
            return (*nextEntry);
            }	    
        nextEntry=itr.NextValue();
	    }
	    
	return NULL;
    }

void CMDNSCacheMap::DumpCacheL()
	{
	RHashMap<TPtrC8,CServiceInfo*>::TIter iter(iCacheEntries);

	__FLOG(_L8("!!!!!!!!!!!!!!!!!!   BEGIN  CACHE DUMP   !!!!!!!!!!!!!!!!!!!!!"));
	for(TInt count=0; count<iCacheEntries.Count(); count++)
		{
		CServiceInfo* entry = *iter.NextValue();


		__FLOG_VA((_L8("--------------------Begin Entry = %d------------------"),count));	


		TPtrC8 key(*iter.CurrentKey());
		__FLOG_VA((_L8("--------------------Key is = %S------------------"),&key));

		if(entry->ServiceRecord())
			{
			__FLOG(_L8("--------Service-Record Data-----"));
			HBufC8* buf(HBufC8::NewLC(entry->ServiceRecord()->Name().Length()));
			buf->Des().Copy(entry->ServiceRecord()->Name());
			__FLOG_VA((_L8("Service Name = %S "),buf));	

			HBufC8* target(HBufC8::NewLC(entry->ServiceRecord()->Target().Length()));
			target->Des().Copy(entry->ServiceRecord()->Target());
			__FLOG_VA((_L8("Target Machine = %S "),target));

			__FLOG_VA((_L8("Priority = %d "),entry->ServiceRecord()->Priority()));
			__FLOG_VA((_L8("Port = %d "),entry->ServiceRecord()->Port()));
			__FLOG_VA((_L8("TTL = %d "),entry->ServiceRecord()->Ttl()));

			CleanupStack::PopAndDestroy(target);
			CleanupStack::PopAndDestroy(buf);
			}

		if(entry->AddressRecord())
			{
			__FLOG(_L8("-------Address-Record Data--------"));
			HBufC8* buf(HBufC8::NewLC(entry->AddressRecord()->Name().Length()));
			buf->Des().Copy(entry->AddressRecord()->Name());
			__FLOG_VA((_L8("Host Name = %S "),buf));		
			const TInetAddr addr(entry->AddressRecord()->Address());
			TBuf <255> ipaddr;
			addr.Output(ipaddr);

			__FLOG_VA((_L("Inet Address = %S "),&ipaddr));

			__FLOG_VA((_L8("TTL = %d "),entry->AddressRecord()->Ttl()));
			CleanupStack::PopAndDestroy(buf);
			}

		if(entry->PtrRecord())
			{
			__FLOG(_L8("-------Ptr-Record Data-----------"));
			HBufC8* buf(HBufC8::NewLC(entry->PtrRecord()->Name().Length()));
			buf->Des().Copy(entry->PtrRecord()->Name());
			__FLOG_VA((_L8("PTR  Name= %S "),buf));

			HBufC8* domainName(HBufC8::NewLC(entry->PtrRecord()->DomainName().Length()));
			domainName->Des().Copy(entry->PtrRecord()->DomainName());
			__FLOG_VA((_L8("PTR Domain Name = %S "),domainName));	

			__FLOG_VA((_L8("Type = %d "),entry->PtrRecord()->Type()));
			__FLOG_VA((_L8("TTL = %d "),entry->PtrRecord()->Ttl()));

			CleanupStack::PopAndDestroy(domainName);
			CleanupStack::PopAndDestroy(buf);
			}

		if(entry->TxtRecord())
			{
			__FLOG(_L8("--------Txt-Record Data---------"));
			const RArray <RBuf8>& array = entry->TxtRecord()->Text();

			HBufC8* name(HBufC8::NewLC(entry->TxtRecord()->Name().Length()));
			name->Des().Copy(entry->TxtRecord()->Name());
			__FLOG_VA((_L8("Text Name= %S "),name));
			CleanupStack::PopAndDestroy(name);


			for (TInt count=0;count<array.Count();count++)
				{
				HBufC8* text(HBufC8::NewLC(array[count].Length()));
				text->Des().Copy(array[count]);
				__FLOG_VA((_L8("Text Info = %S "),text));
				CleanupStack::PopAndDestroy(text);			
				}			
			__FLOG_VA((_L8("TTL = %d "),entry->TxtRecord()->Ttl()));
			}

		__FLOG_VA((_L8("--------Entry is Authoritative?? = %d--------"),entry->IsAuthoritative()));				

		__FLOG_VA((_L8("--------Last Access Time = %ld--------"),entry->LastAccessTime()));

		}
	__FLOG_VA((_L8("--------Total number of entries = %d--------"),iCacheEntries.Count()));
	}

	


	
	
	

	

	
