//=================================================================
//
//        TestResource.cpp
//
//        Resource test class
//
//=================================================================
//####COPYRIGHTBEGIN####
//
// -------------------------------------------
// The contents of this file are subject to the Cygnus eCos Public License
// Version 1.0 (the "License"); you may not use this file except in
// compliance with the License.  You may obtain a copy of the License at
// http://sourceware.cygnus.com/ecos
// 
// Software distributed under the License is distributed on an "AS IS"
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the
// License for the specific language governing rights and limitations under
// the License.
// 
// The Original Code is eCos - Embedded Cygnus Operating System, released
// September 30, 1998.
// 
// The Initial Developer of the Original Code is Cygnus.  Portions created
// by Cygnus are Copyright (C) 1998, 1999 Cygnus Solutions.
// All Rights Reserved.
// -------------------------------------------
//
//####COPYRIGHTEND####
//=================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):     sdf
// Contributors:  sdf
// Date:          1999-04-01
// Description:   This class abstracts a test resource for use in the testing infrastructure
// Usage:
//
//####DESCRIPTIONEND####
#include "stdafx.h"
#include "eCosTestUtils.h"
#include "TestResource.h"

CTestResource *CTestResource::pFirstInstance=0;
unsigned int CTestResource::nCount=0;

CTestResource::CTestResource(
		const char *pszName,
		CeCosTest::TargetType Target,
		const char *pszHost,
		int nPort
    ):
	m_strName(pszName),
	m_Target(Target),
	m_strHost(pszHost),
	m_nPort(nPort),
	m_bSim(true),
	m_bLocked(false),
	m_cControl1('\0'),
	m_cControl2('\0'),
	m_nBaud(0),
	m_nResetPort(0)
{
	TRACE("Test resource %10s %10s %10s:%04d %d\n",
			Name(),CeCosTest::Image(m_Target),Host(),Port(),m_bSim);	
    Chain();
}

CTestResource::CTestResource(
		const char *pszName,
		CeCosTest::TargetType Target,
		const char *pszHost,
		int nPort,
        const char * pszDownloadPort, int nBaud, const char *pszResetHost,unsigned int nResetPort,
			 char cControl1, char cControl2
    ):
	m_strName(pszName),
	m_Target(Target),
	m_strHost(pszHost),
	m_nPort(nPort),
	m_bSim(false),
	m_bLocked(false),
	m_cControl1(cControl1),
	m_cControl2(cControl2),
	m_nBaud(nBaud),
	m_strPort(pszDownloadPort),
	m_strResetHost(pszResetHost),
	m_nResetPort(nResetPort)
{
	TRACE("Test resource %10s %10s %10s:%04d %d\n",
			Name(),CeCosTest::Image(m_Target),Host(),Port(),m_bSim);	
    Chain();
}

CTestResource::~CTestResource(){
	ENTERCRITICAL;
        if(m_pPrevInstance || m_pNextInstance){
            nCount--;
        }
        if(pFirstInstance==this){
            pFirstInstance=m_pNextInstance;
        }
        if(m_pPrevInstance){
            m_pPrevInstance->m_pNextInstance=m_pNextInstance;
        }
        if(m_pNextInstance){
            m_pNextInstance->m_pPrevInstance=m_pPrevInstance;
        }
	LEAVECRITICAL;
}

CTestResource * CTestResource::Lookup(const char * pszHost, int nPort, bool bSim)
{
    LOCKRESOURCES;
	CTestResource *pResource;
	for(pResource=pFirstInstance;pResource;pResource=pResource->m_pNextInstance){
		if(nPort==pResource->Port() && bSim==pResource->m_bSim && 0==strcmp(pszHost,pResource->Host())){
			break;
		}
	}
    UNLOCKRESOURCES;
	return pResource;
}

unsigned int CTestResource::GetMatchCount (const CeCosTest::ExecutionParameters &e,bool bIgnoreLocking)
{
    LOCKRESOURCES;
    unsigned int i=0;
	for(const CTestResource *pResource=pFirstInstance;pResource;pResource=pResource->m_pNextInstance){
		if(pResource->Matches(e,bIgnoreLocking)){
            i++;
		}
    }
    UNLOCKRESOURCES;
    return i;
}

unsigned int CTestResource::GetMatches (const CeCosTest::ExecutionParameters &e, const CTestResource **&ar,bool bIgnoreLocking)
{
    LOCKRESOURCES;
    ar=new const CTestResource *[GetMatchCount(e)];
    unsigned int i=0;
	for(CTestResource *pResource=pFirstInstance;pResource;pResource=pResource->m_pNextInstance){
		if(pResource->Matches(e,bIgnoreLocking)){
		    ar[i++]=pResource;
		}
    }
    UNLOCKRESOURCES;
    return i;
}

unsigned int CTestResource::Choose (const CeCosTest::ExecutionParameters &e, const CTestResource * const * const &ar, unsigned int nCount, int nExclude)
{
    LOCKRESOURCES;
    // Initial implementation is stochastic
    assert(nCount>0);
    assert(nExclude==-1 || (nCount>1 && (unsigned)nExclude<=nCount));
    unsigned int rc;
    do {
        rc=rand() % nCount;
    } while (rc==(unsigned)nExclude);
    UNLOCKRESOURCES;
    return rc;
}

bool CTestResource::LoadFile(const char * pszFileName)
{
	#ifdef _WIN32
	if(0==pszFileName || '\0'==*pszFileName){
		return LoadReg();
	}
	#endif

    LOCKRESOURCES;
	DeleteAllInstances();
	bool rc=true;
	FILE *f=fopen(pszFileName,"rt");
	if(0==f){
		TRACE("Failed to open %s - %s\n",pszFileName,strerror(errno));
		rc=false;
	} else {
		int nLine=0;
		char szLine[256];
		while(0!=fgets(szLine,sizeof szLine-1,f)){
			nLine++;

			// Boardname target host port sim
			char szName[256]={'\0'};
			char szTarget[256]={'\0'};
            char szDownloadPort[256]={'\0'};
			char szHost[256]={'\0'};
			int nPort=0;
			char szSim[256]={'\0'};
			sscanf(szLine,"%s %s %s %d %s",szName,szTarget,szHost,&nPort,szSim,szDownloadPort);
			if('\0'!=szName[0]){

				CeCosTest::TargetType target=CeCosTest::FromStr(szTarget);

				if(target==CeCosTest::TargetTypeMax){
					TRACE("%s:Illegal target type '%s' at line %d:\n%s\n",pszFileName,szTarget,nLine,szLine);
					rc=false;
					continue;
				}

				if('\0'==szHost[0]){
					TRACE("%s:Illegal download host at line %d:\n%s\n",pszFileName,nLine,szLine);
					rc=false;
					continue;
				}

				if(0==nPort){
					TRACE("%s:Illegal download port at line %d:\n%s\n",pszFileName,nLine,szLine);
					rc=false;
					continue;
				}
				bool bSim;
				switch(szSim[0]){
					case '1':
					case 't':
					case 'T':
					case 'y':
					case 'Y':
						bSim=true;
						break;
					case '0':
					case 'f':
					case 'F':
					case 'n':
					case 'N':
						bSim=false;
						break;
					default:
						TRACE("%s:Illegal sim indicator '%c' at line %d:\n%s\n",pszFileName,szSim[0],nLine,szLine);
						rc=false;
						continue;
				}
                
                if(bSim){
				    new CTestResource(szName, target, szHost,nPort);
                } else {
                    new CTestResource(szName, target, szHost,nPort,szDownloadPort,38400,"aloo",5000,'A','5');
                }
			}
		}
		fclose(f);
	}
	UNLOCKRESOURCES;
	return rc;
}

void CTestResource::DeleteAllInstances()
{
    LOCKRESOURCES;
    while(pFirstInstance){
        delete pFirstInstance;
    }
    UNLOCKRESOURCES;
}

void CTestResource::Output(FILE *f)
{
	CTestResource *pResource;
    LOCKRESOURCES;
	for(pResource=pFirstInstance;pResource;pResource=pResource->m_pNextInstance){
		fprintf(f,"%s\t%s:%04d %d %s\n",pResource->Name(),pResource->Host(),pResource->Port(),pResource->m_bSim,pResource->DownloadPort());	
	}
    UNLOCKRESOURCES;
}

bool CTestResource::SaveFile(const char * pszFileName)
{
	#ifdef _WIN32
	if(0==pszFileName || '\0'==*pszFileName){
		return SaveReg();
	}
	#endif
    LOCKRESOURCES;
	bool rc=false;
	FILE *f=fopen(pszFileName,"wt");
	if(f){
		Output(f);
		fclose(f);
		rc=true;
	}
    UNLOCKRESOURCES;
	return rc;
}

#ifdef _WIN32
static const char szRegKey[]="Software\\Cygnus Solutions\\eCos\\testing";

bool CTestResource::LoadReg  ()
{
	LOCKRESOURCES;
    DeleteAllInstances();
	bool rc=true;
	HKEY hKey;
	if(ERROR_SUCCESS==RegOpenKeyEx (HKEY_CURRENT_USER, szRegKey, 0L, KEY_ENUMERATE_SUB_KEYS, &hKey)){
		char szName[256];
		DWORD dwSizeName=sizeof szName;
		FILETIME ftLastWriteTime;
		for(DWORD dwIndex=0;ERROR_SUCCESS==RegEnumKeyEx(hKey, dwIndex, szName, &dwSizeName, NULL, NULL, NULL, &ftLastWriteTime); dwIndex++){
			HKEY hKey2;
			if(ERROR_SUCCESS!=RegOpenKeyEx (hKey, szName, 0L, KEY_READ, &hKey2)){
				TRACE("Failed to open %s\\%s\n",szRegKey,szName);
			} else {
				char szHost[256];
				DWORD dwSizeHost=sizeof szHost;
				char szTarget[MAX_PATH];
				DWORD dwSizeTarget=sizeof szTarget;
                char szDownloadPort[256]={'\0'};
                DWORD dwSizeDownloadPort=sizeof szDownloadPort;
                //char szDownloadPort2[256]={'\0'};
                //DWORD dwSizeDownloadPort2=sizeof szDownloadPort2;
                char szResetHost[256]={'\0'};
                DWORD dwSizeResetHost=sizeof szResetHost;
                int   nResetPort=5000;
                DWORD dwSizeResetPort=sizeof nResetPort;
				int nPort;
				DWORD dwSizePort=sizeof nPort;
				int  bSim;
				DWORD dwSizeSim=sizeof bSim;
				int  cControl1='A';
				DWORD dwSizeControl1=sizeof cControl1;
				int  cControl2='1';
				DWORD dwSizeControl2=sizeof cControl2;
				int  nBaud=38400;
				DWORD dwSizeBaud=sizeof nBaud;
				if(
					ERROR_SUCCESS==RegQueryValueEx(hKey2, "Target",		NULL, NULL, (unsigned char *)szTarget,	    &dwSizeTarget) &&
					ERROR_SUCCESS==RegQueryValueEx(hKey2, "Host",		NULL, NULL, (unsigned char *)szHost,	    &dwSizeHost) &&
					ERROR_SUCCESS==RegQueryValueEx(hKey2, "Port",		NULL, NULL, (unsigned char *)&nPort,    	&dwSizePort) &&
					ERROR_SUCCESS==RegQueryValueEx(hKey2, "Simulator",  NULL, NULL, (unsigned char *)&bSim,		    &dwSizeSim)){
					CeCosTest::TargetType target=CeCosTest::FromStr(szTarget);

					if(target==CeCosTest::TargetTypeMax){
						TRACE("%s\\%s:Illegal target type '%s'\n",szRegKey,szName,szTarget);
						rc=false;
						continue;
					}
					RegQueryValueEx(hKey2, "Serial",		NULL, NULL, (unsigned char *)szDownloadPort,&dwSizeDownloadPort);
					//RegQueryValueEx(hKey2, "Serial2",		NULL, NULL, (unsigned char *)szDownloadPort2,&dwSizeDownloadPort2);
					RegQueryValueEx(hKey2, "ResetHost",     NULL, NULL, (unsigned char *)szResetHost,   &dwSizeResetHost);
					RegQueryValueEx(hKey2, "ResetPort",     NULL, NULL, (unsigned char *)&nResetPort,   &dwSizeResetPort);
					RegQueryValueEx(hKey2, "Baud",          NULL, NULL, (unsigned char *)&nBaud,        &dwSizeBaud);
					RegQueryValueEx(hKey2, "Control1",      NULL, NULL, (unsigned char *)&cControl1,    &dwSizeControl1);
					RegQueryValueEx(hKey2, "Control2",      NULL, NULL, (unsigned char *)&cControl2,    &dwSizeControl2);

                    if(bSim){
				        new CTestResource(szName, target, szHost,nPort);
                    } else {
                        new CTestResource(szName, target, szHost,nPort,szDownloadPort,nBaud,szResetHost,nResetPort,(char)cControl1,(char)cControl2);
                        //pResource->m_strPort2=szDownloadPort2;
                    }
				}
				RegCloseKey(hKey2);
			}
			dwSizeName=sizeof szName;
		}
		RegCloseKey(hKey);
	}
	UNLOCKRESOURCES;

	return rc;
}

bool CTestResource::SaveReg  ()
{
    LOCKRESOURCES;
	bool rc=false;
	HKEY hKey;

	if(ERROR_SUCCESS==RegOpenKeyEx (HKEY_CURRENT_USER, szRegKey, 0L, KEY_ENUMERATE_SUB_KEYS, &hKey)){
		char szName[256];
		DWORD dwSizeName=sizeof szName;
		FILETIME ftLastWriteTime;
		for(DWORD dwIndex=0;ERROR_SUCCESS==RegEnumKeyEx(hKey, dwIndex, szName, &dwSizeName, NULL, NULL, NULL, &ftLastWriteTime); dwIndex++){
            if(ERROR_SUCCESS!=RegDeleteKey(hKey,szName)){
                rc=false;
            }
			dwSizeName=sizeof szName;
		}
		RegCloseKey(hKey);
    }

    if(ERROR_SUCCESS==RegOpenKeyEx (HKEY_CURRENT_USER, szRegKey, 0L, KEY_CREATE_SUB_KEY, &hKey)){
		rc=true;
		for(CTestResource *pResource=pFirstInstance;pResource;pResource=pResource->m_pNextInstance){
			HKEY hKey2;
			DWORD dwDisp;
			if(ERROR_SUCCESS==RegCreateKeyEx( hKey, pResource->Name(), 0, (char *)REG_NONE, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKey2, &dwDisp)){
				const char *pszHost=pResource->Host();
				const char *pszTarget=CeCosTest::Image(pResource->Target());
				const char *pszDownloadPort=pResource->DownloadPort();
				//const char *pszDownloadPort2=pResource->m_strPort2;
				DWORD        dwPort=pResource->Port();
				DWORD        dwSim =pResource->m_bSim;
                const char *pszResetHost=pResource->ResetHost();
                DWORD dwResetPort=(DWORD)pResource->ResetPort();
                DWORD dwBaud=(DWORD)pResource->Baud();
				DWORD dwControl1=(DWORD)pResource->Control1();
				DWORD dwControl2=(DWORD)pResource->Control2();
                if(
					ERROR_SUCCESS==RegSetValueEx (hKey2, "Target",     0, REG_SZ,    (const unsigned char *)pszTarget, strlen(pszTarget)) &&
					ERROR_SUCCESS==RegSetValueEx (hKey2, "Host",       0, REG_SZ,    (const unsigned char *)pszHost, strlen(pszHost)) &&
					ERROR_SUCCESS==RegSetValueEx (hKey2, "Port",	   0, REG_DWORD, (const unsigned char *)&dwPort, sizeof(DWORD)) &&
					ERROR_SUCCESS==RegSetValueEx (hKey2, "Simulator",  0, REG_DWORD, (const unsigned char *)&dwSim , sizeof(DWORD))
                    ){
                    if(!pResource->Sim()){
    					RegSetValueEx (hKey2, "Serial",     0, REG_SZ,    (const unsigned char *)pszDownloadPort, strlen(pszDownloadPort));
    					//RegSetValueEx (hKey2, "Serial2",    0, REG_SZ,    (const unsigned char *)pszDownloadPort, strlen(pszDownloadPort));
                        RegSetValueEx (hKey2, "ResetHost",  0, REG_SZ,    (const unsigned char *)pszResetHost, strlen(pszResetHost));
					    RegSetValueEx (hKey2, "ResetPort",  0, REG_DWORD, (const unsigned char *)&dwResetPort, sizeof(DWORD));
					    RegSetValueEx (hKey2, "Baud",       0, REG_DWORD, (const unsigned char *)&dwBaud, sizeof(DWORD));
					    RegSetValueEx (hKey2, "Control1",   0, REG_DWORD, (const unsigned char *)&dwControl1, sizeof(DWORD));
					    RegSetValueEx (hKey2, "Control2",   0, REG_DWORD, (const unsigned char *)&dwControl2, sizeof(DWORD));
                    }
				}
				RegCloseKey(hKey2);
			}
		}
		RegCloseKey(hKey);
	}
    UNLOCKRESOURCES;

	return rc;
}
#endif

bool CTestResource::Lock()
{
	if(!m_bLocked){
		m_bLocked=true;
		return true;
	} else {
		return false;
	}
}

bool CTestResource::Unlock()
{
	if(m_bLocked){
		m_bLocked=false;
		return true;
	} else {
		return false;
	}

}


bool CTestResource::LoadSocket(const char * pszHost, int nTcpPort)
{
    LOCKRESOURCES;
    DeleteAllInstances();
    bool rc=true;
    for(;;){
		CeCosTestSocket sock(pszHost,nTcpPort, 1000);
		if(sock.Ok()){
		    // Write the message to the socket
            int nRequest=0; // read
		    if(!sock.send(&nRequest, sizeof nRequest)){
			    TRACE("Failed to write to socket\n");
                rc=false;
		    } else {
                int nResources;
                if(sock.recv(&nResources,sizeof nResources)){
                    while(nResources--){
                        int nLen;
                        CeCosTestUtils::String strImage;
                        if(sock.recv(&nLen,sizeof nLen) && sock.recv((const char *)strImage.GetBuffer(nLen),1+nLen)){
                            new CTestResource (strImage);
                        } else {
                            rc=false;
                            break;
                        }
				    }
                } else {
                    rc=false;
                }
		    }
            break;
        } else {
            TRACE("Failed to load resources - sleep and retry\n");
            CeCosTestUtils::Sleep(1000);
        }
    }
    UNLOCKRESOURCES;
    return rc;
}

void CTestResource::Image(CeCosTestUtils::String &str)
{
    EscapeWhitespace(m_strName);
    CeCosTestUtils::String strTarget(CeCosTest::Image(Target()));
    EscapeWhitespace(strTarget);

    str.Format(
        //"%s %s %s %d %d %s %d %s %d %c %c %s",
        "%s %s %s %d %d %s %d %s %d %c %c",
        (const char *)Name(),
        (const char *)strTarget,
        (const char *)Host(),
        Port(),
        Sim(),
        (const char *)DownloadPort(), 
        Baud(),
        (const char *)ResetHost(),
        ResetPort(),
        Control1(), 
        Control2()
        //,(const char *)m_strPort2
        );
}

CTestResource::CTestResource(const char * pszImage)
{
    int nLen=strlen(pszImage);
    CeCosTestUtils::String strTarget;
    sscanf(pszImage, 
        //"%s %s %s %d %d %s %d %s %d %c %c %s",
        "%s %s %s %d %d %s %d %s %d %c %c",
        m_strName.GetBuffer(nLen),
        strTarget.GetBuffer(nLen),
        m_strHost.GetBuffer(nLen),
        &m_nPort,
        &m_bSim,
        m_strPort.GetBuffer(nLen),
        &m_nBaud,
        m_strResetHost.GetBuffer(nLen),
        &m_nResetPort,
        &m_cControl1, 
        &m_cControl2
        //,m_strPort2.GetBuffer(nLen)
        );
    UnescapeWhitespace(strTarget);
    m_Target=CeCosTest::FromStr(strTarget);
    UnescapeWhitespace(m_strName);
    Chain();
    m_bLocked=false;
}

void CTestResource::Chain()
{
    LOCKRESOURCES;
    nCount++;
    m_pNextInstance=pFirstInstance;
    if(m_pNextInstance){
        m_pNextInstance->m_pPrevInstance=this;
    }
    m_pPrevInstance=0;
    pFirstInstance=this;
    UNLOCKRESOURCES;
}

bool CTestResource::Matches  (const CeCosTest::ExecutionParameters &e,bool bIgnoreLocking) const
{
    return (bIgnoreLocking||!m_bLocked) &&  e.Target()==m_Target; 
};

bool CTestResource::SaveSocket(const char * pszHost, int nTcpPort)
{
    LOCKRESOURCES;
    bool rc=true;
	CeCosTestSocket sock(pszHost,nTcpPort, 1000);
	if(sock.Ok()){
		// Write the message to the socket
        int nRequest=1; //write
		if(!sock.send(&nRequest, sizeof nRequest)){
			TRACE("Failed to write to socket\n");
            rc=false;
		} else {
            int nResources=0;
            CTestResource *pResource;
            for(pResource=CTestResource::First();pResource;pResource=pResource->Next()){
                nResources++;
            }
            if(sock.send (&nResources, sizeof nResources,"resource count")){
                for(pResource=CTestResource::First();pResource;pResource=pResource->Next()){
                    CeCosTestUtils::String strImage;
                    pResource->Image(strImage);
                    int nLen=strImage.GetLength();
                    if(!sock.send (&nLen, sizeof nLen,"reply")||!sock.send ((const char *)strImage, 1+nLen,"reply")){
                        rc=false;
                        break;
                    }
                }
            } else {
                rc=false;
            }
        }
    } else {
        rc=false;
    }
    UNLOCKRESOURCES;
	return rc;
}

void CTestResource::EscapeWhitespace(CeCosTestUtils::String &str)
{
    char *c=str.GetBuffer();
    while(*c){
        if(isspace(*c)){
            *c='\255';
        }
        c++;
    }

}

void CTestResource::UnescapeWhitespace(CeCosTestUtils::String & str)
{
    char *c=str.GetBuffer();
    while(*c){
        if('\255'==*c){
            *c=' ';
        }
        c++;
    }

}

static int nLock=0;
void CTestResource::LockResources(const char *pszFile,int nLine)
{
    CeCosTestUtils::EnterCriticalSection(pszFile,nLine);
    nLock++;
}

void CTestResource::UnlockResources(const char *pszFile,int nLine)
{
    nLock--;
    CeCosTestUtils::LeaveCriticalSection(pszFile,nLine);
}


bool CTestResource::ResourcesLocked()
{
    return (nLock>0);
}

CeCosTest::ServerStatus CTestResource::Query() const
{
	CeCosTest::ExecutionParameters e(m_Target);
	e.SetRequest(CeCosTest::ExecutionParameters::QUERY);
    CeCosTestSocket *pSock=0;
    CeCosTest::ServerStatus s=CeCosTest::Connect(m_strHost,m_nPort,pSock,e);
    delete pSock;
	return s;
}
