#include <windows.h>
#include <stdlib.h>
#include "includes.h"
#include "common.h"
#ifdef HAVE_HESOID
    #include "ResolveLocker.h"
    int ResolveLocker(USER_OPTIONS *attachOption);
#endif
#ifndef NO_AFS_SOURCE
    #include <afs\stds.h>
    #include <afs\smb_iocons.h>
    #include <afs\afsint.h>
    #include <afs\pioctl_nt.h>
#else
    #define VIOCGETVOLSTAT  0x7
    #define afs_int32   int

    struct VolumeStatus {
	    afs_int32 Vid;
	    afs_int32 ParentId;
	    char Online;
	    char InService;
	    char Blessed;
	    char NeedsSalvage;
	    afs_int32 Type;
	    afs_int32 MinQuota;
	    afs_int32 MaxQuota;
	    afs_int32 BlocksInUse;
	    afs_int32 PartBlocksAvail;
	    afs_int32 PartMaxBlocks;
    };

    typedef struct VolumeStatus VolumeStatus;

    typedef struct ViceIoctl {
	    long in_size;
            long out_size;
            void *in;
	    void *out;
    } viceIoctl_t;
extern long pioctl(char *pathp, long opcode, struct ViceIoctl *blob, int follow);
#endif

#define MAX_PARAMS 20
#define ival(s) strtol(s, NULL, 0)
#define AFSDLL "afsauthent.dll"

#define WINTORTURE_ASFDLL_ONLINE        1
#define WINTORTURE_ASFDLL_OFFLINE       2
#define WINTORTURE_ASFDLL_NOTFOUND      3
#define WINTORTURE_ASFPIOCTL_NOTFOUND   4

extern int verbose;

extern void EndSecondTime(int cmd);
extern void StartSecondTime(int cmd);
extern void LogMessage(int ProcessNumber, char *HostName, char *FileName, char *message, int LogID);
extern void LogStats(char *FileName, int ToLog, int Iteration,  int NumberOfProcesses, int NumberOfThreads,
                     char *HostName, int ProcessNumber, struct cmd_struct ThreadCommandInfo[],
                     char *CommandLine, char *TargetDirectory);
extern void SubstituteString(char *s,const char *pattern,const char *insert, size_t len);

int  IsOnline(char *strPath);
BOOL run_netbench(int client, char *ClientTxt, char *PathToSecondDir);

HANDLE  MutexHandle;
HANDLE  FileMutexHandle;
HANDLE  ShutDownEventHandle;
HANDLE  PauseEventHandle;
HANDLE  ContinueEventHandle;
HANDLE  OSMutexHandle;

__declspec( thread ) int    AfsTrace;
__declspec( thread ) int    CurrentLoop;
__declspec( thread ) int    ProcessNumber = 0;
__declspec( thread ) int    LogID = 0;
__declspec( thread ) int    LineCount = 0;
__declspec( thread ) int    *pThreadStatus;
__declspec( thread ) DWORD  TickCount1,TickCount2, MilliTickStart;
__declspec( thread ) int    BufferSize = 4096;
__declspec( thread ) char   *IoBuffer = NULL;
__declspec( thread ) char   AfsLocker[256];
__declspec( thread ) char   OriginalAfsLocker[256];
__declspec( thread ) char   HostName[256];
__declspec( thread ) struct cmd_struct ThreadCommandInfo[CMD_MAX_CMD + 1];
__declspec( thread ) FTABLE ftable[MAX_FILES];
__declspec( thread ) HANDLE hWinEventHandle;
__declspec( thread ) EXIT_STATUS *pExitStatus;
__declspec( thread ) DWORD  LastKnownError;


DWORD WINAPI StressTestThread(LPVOID lpThreadParameter)
{
    int         j;
    int         rc;
    size_t      count;
    int         ProcessID;
    char        EventName[64];
    char        FileName[256];
    char        CommandLine[512];
    char        TargetDirectory[512];
    char        WorkingDirectory[512];
    char        ClientText[128];
    char        PathToSecondDir[256];
    char        temp[512];
    BOOL        PrintStats;
    PARAMETERLIST *pParameterList;
    struct cmd_struct *WinCommandInfo;

    pParameterList = (PARAMETERLIST *)lpThreadParameter;
    pThreadStatus = pParameterList->pThreadStatus;
    BufferSize = pParameterList->BufferSize;
    ProcessNumber = pParameterList->ProcessNumber;
    PrintStats = pParameterList->PrintStats;
    CurrentLoop = pParameterList->CurrentLoop;
    ProcessID = pParameterList->ProcessID;
    LogID = pParameterList->LogID;
    AfsTrace = pParameterList->AfsTrace;
    pExitStatus = pParameterList->pExitStatus;
    strcpy(TargetDirectory, pParameterList->TargetDirectory);
    strcpy(CommandLine, pParameterList->CommandLine);
    strcpy(ClientText, pParameterList->ClientText);
    strcpy(PathToSecondDir, pParameterList->PathToSecondDir);
    strcpy(AfsLocker, pParameterList->AfsLocker);
    strcpy(HostName, pParameterList->HostName);
    WinCommandInfo = ( struct cmd_struct *)pParameterList->CommandInfo;

    sprintf(EventName, "%d%sEvent%05d", ProcessID, HostName, ProcessNumber);
    hWinEventHandle = OpenEvent(EVENT_ALL_ACCESS, TRUE, EventName);

    sprintf(FileName, "Thread_%05d.log", ProcessNumber);
    GetCurrentDirectory(sizeof(WorkingDirectory), WorkingDirectory);
    sprintf(temp, "%s\\log%05d\\%s", WorkingDirectory, LogID, HostName);
    CreateDirectory(temp, NULL);


    memset(ftable, '\0', sizeof(ftable[0]) * MAX_FILES);
    IoBuffer = malloc(BufferSize);
    if (!IoBuffer)
    {
        strcpy(pExitStatus->Reason, "Unable to allocate buffer");
        pExitStatus->ExitStatus = 1;
        SetEvent(hWinEventHandle);
        ExitThread(1);
    }
    memset(IoBuffer, (unsigned char)getpid(), BufferSize);

    ShutDownEventHandle = CreateEvent(NULL, TRUE, FALSE, "AfsShutdownEvent");
    PauseEventHandle = CreateEvent(NULL, TRUE, FALSE, "AfsPauseEvent");
    ContinueEventHandle = CreateEvent(NULL, TRUE, FALSE, "AfsContinueEvent");
    OSMutexHandle = CreateMutex(NULL, FALSE, "WinTortureOSMutex");
    MutexHandle = CreateMutex(NULL, FALSE, "WinTortureMutex");

    strcpy(OriginalAfsLocker, AfsLocker);

    while (1)
    {
        LastKnownError = 0;
        for (j = 0; j <= CMD_MAX_CMD; j++)
        {
            WinCommandInfo[j].count = 0;
            WinCommandInfo[j].min_sec = 0;
            WinCommandInfo[j].max_sec = 0;
            WinCommandInfo[j].MilliSeconds = 0;
            WinCommandInfo[j].total_sec = 0;
            WinCommandInfo[j].total_sum_of_squares = 0;
            WinCommandInfo[j].ErrorCount = 0;
            WinCommandInfo[j].ErrorTime = 0;
            ThreadCommandInfo[j].count = 0;
            ThreadCommandInfo[j].min_sec = 1000;
            ThreadCommandInfo[j].max_sec = 0;
            ThreadCommandInfo[j].MilliSeconds = 0;
            ThreadCommandInfo[j].total_sec = 0;
            ThreadCommandInfo[j].total_sum_of_squares = 0;
            ThreadCommandInfo[j].ErrorCount = 0;
            ThreadCommandInfo[j].ErrorTime = 0;
        }

        run_netbench(ProcessNumber, ClientText, PathToSecondDir);
        if (LastKnownError != ERROR_NETNAME_DELETED)
            break; 
        sprintf(temp, "entered error %d processing\n", LastKnownError);
        LogMessage(ProcessNumber, HostName, FileName, temp, LogID);

        count = strlen(pExitStatus->Reason);
        if (count != 0)
            memset(pExitStatus->Reason, '\0', count);
        pExitStatus->ExitStatus = 0;
        (*pThreadStatus) = 1;
        count = 0;
        
        while ((rc = IsOnline(OriginalAfsLocker)) != WINTORTURE_ASFDLL_ONLINE)
        {
            if ((count > 3) || (rc == WINTORTURE_ASFDLL_NOTFOUND) || (rc == WINTORTURE_ASFPIOCTL_NOTFOUND))
            {
                LastKnownError = 0;
                sprintf(temp, "AFS path \"%s\" appears to be off-line\n", OriginalAfsLocker);
                strcpy(pExitStatus->Reason, temp);
                pExitStatus->ExitStatus = 1;
                (*pThreadStatus) = 0;
                printf("%s", temp);
                LogMessage(ProcessNumber, HostName, FileName, temp, LogID);
                strcpy(temp, "Stress test is terminating\n");
                printf(temp);
                LogMessage(ProcessNumber, HostName, FileName, temp, LogID);
                break;
            }
            sprintf(temp, "AFS path \"%s\" is online, sleeping 10 seconds before continuing\n", OriginalAfsLocker);
            LogMessage(ProcessNumber, HostName, FileName, temp, LogID);
            ++count;
            Sleep(10 * 1000);
        }
        sprintf(temp, "leaving error 0x%x processing\n", LastKnownError);
        LogMessage(ProcessNumber, HostName, FileName, temp, LogID);
        if (count > 3)
            break;
    }
    free(IoBuffer);

    for (j = 0; j <= CMD_MAX_CMD; j++)
    {
        WinCommandInfo[j].count += ThreadCommandInfo[j].count;
        WinCommandInfo[j].min_sec += ThreadCommandInfo[j].min_sec;
        WinCommandInfo[j].max_sec += ThreadCommandInfo[j].max_sec;
        WinCommandInfo[j].total_sec += ThreadCommandInfo[j].total_sec;
        WinCommandInfo[j].total_sum_of_squares += ThreadCommandInfo[j].total_sum_of_squares;
        WinCommandInfo[j].ErrorCount += ThreadCommandInfo[j].ErrorCount;
        WinCommandInfo[j].ErrorTime += ThreadCommandInfo[j].ErrorTime;
    }

    memset(WorkingDirectory, '\0', sizeof(WorkingDirectory));
    GetCurrentDirectory(sizeof(WorkingDirectory), WorkingDirectory);
    sprintf(FileName, "%s\\log%05d\\%s\\Thread_%05d_Stats.log", WorkingDirectory, LogID, HostName, ProcessNumber);

    if (PrintStats)
    {
        WaitForSingleObject(MutexHandle, 4 * 1000);
        LogStats(FileName, 0, CurrentLoop, 0, 0, HostName, ProcessNumber, ThreadCommandInfo, CommandLine, TargetDirectory);
        ReleaseMutex(MutexHandle);
    }

    LogStats(FileName, 1, CurrentLoop, 0, 0, HostName, ProcessNumber, ThreadCommandInfo,CommandLine, TargetDirectory);

    SetEvent(hWinEventHandle);
    CloseHandle(hWinEventHandle);
//    CloseHandle(OSMutexHandle);
//    CloseHandle(MutexHandle);

    ExitThread(0);
    return(0);
}

BOOL run_netbench(int client, char *ClientText, char *PathToSecondDir)
{
    pstring line;
    pstring line1;
    char    cname[20];
    char    *params[MAX_PARAMS];
    char    temp[256];
    char    FileName[256];
    char    *pPtr;
    int     rc;
    int     i;
    int     IncreaseBy;
    BOOL    correct = TRUE;
    DWORD   dwFlags = 0;
    DWORD   NumberOfBytes;
    DWORD   TotalBytesRead;
    HANDLE  hFile;
    enum    states bm_state;
    CRITICAL_SECTION CriticalSection;


    InitializeCriticalSection(&CriticalSection);

    sprintf(cname, "client%d", client);

    sprintf(temp, "Started Iteration %d\n", CurrentLoop);
    sprintf(FileName, "Thread_%05d.log", ProcessNumber);
    LogMessage(ProcessNumber, HostName, FileName, temp, LogID);
    sprintf(temp, "Thread %d started\n", ProcessNumber);
    sprintf(FileName, "Thread_%05d.log", ProcessNumber);
    LogMessage(ProcessNumber, HostName, FileName, temp, LogID);

    hFile = CreateFile(ClientText, GENERIC_READ | STANDARD_RIGHTS_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 

    if (hFile == INVALID_HANDLE_VALUE) 
    {
        perror(ClientText);
        return(-1);
    }

    StartSecondTime(CMD_NONAFS);
    TotalBytesRead = 0;
    while (1)
    {
        memset(line, '\0', sizeof(line));
        NumberOfBytes = 0;
        rc = ReadFile(hFile, line, 128, &NumberOfBytes, NULL);
        if (rc && NumberOfBytes == 0)
            break;
        pPtr = strchr(line, '\n');
        IncreaseBy = 0;
        if (pPtr != NULL)
        {
            IncreaseBy += 1;
            (*pPtr) = '\0';
            if ((*(pPtr - 1)) == '\r')
            {
                IncreaseBy += 1;
                (*(pPtr - 1)) = '\0';
            }
        }
        TotalBytesRead += (DWORD)(strlen(line) + IncreaseBy);
        SetFilePointer(hFile, TotalBytesRead, 0, FILE_BEGIN);
        strcpy(line1, line);
        if (rc = WaitForSingleObject(PauseEventHandle, 0) == WAIT_OBJECT_0)
        {
            strcpy(temp, "AFS suspend request received\n");
            sprintf(FileName, "Thread_%05d.log", ProcessNumber);
            LogMessage(ProcessNumber, HostName, FileName, temp, LogID);
            while (WaitForSingleObject(ContinueEventHandle, 5000) == WAIT_TIMEOUT);
            strcpy(temp, "AFS continue request received\n");
            sprintf(FileName, "Thread_%05d.log", ProcessNumber);
            LogMessage(ProcessNumber, HostName, FileName, temp, LogID);
        }
        if (rc = WaitForSingleObject(ShutDownEventHandle, 0) == WAIT_OBJECT_0)
        {
            strcpy(temp, "AFS shutdown request received\n");
            sprintf(FileName, "Thread_%05d.log", ProcessNumber);
            LogMessage(ProcessNumber, HostName, FileName, temp, LogID);
            break;
        }
        LineCount++;
        if (strlen(line) == 0)
            continue;
        if (line[0] == '#')
            continue;
        /*printf("[%d] %s\n", LineCount, line);*/

        for (i = 0; i < MAX_PARAMS; i++)
            params[i] = NULL;

        SubstituteString(line,"client1", cname, sizeof(line));
        sprintf(temp, "%s%05d", HostName, LogID);
        SubstituteString(line,"clients", temp, sizeof(line));
        SubstituteString(line,"\\\\afs\\locker", AfsLocker, sizeof(line));
        if (strlen(PathToSecondDir) != 0)
    		SubstituteString(line,"\\\\afs\\lcolby", PathToSecondDir, sizeof(line));

        if (verbose)
        {
            EnterCriticalSection(&CriticalSection);
            printf("Thread%05d - %-6d - %s\n", ProcessNumber, LineCount, line);
            LeaveCriticalSection(&CriticalSection);
        }

        pPtr = line;
        i = 0;
        while (pPtr != NULL)
        {
            if ((*pPtr) == ' ')
            {
                (*pPtr) = '\0';
                ++pPtr;
                continue;
            }
            params[i] = pPtr;
            ++i;
            pPtr = strstr(pPtr, " ");
            if (pPtr != NULL)
            {
                (*pPtr) = '\0';
                ++pPtr;
            }
            
        }

        params[i] = "";

        if (i < 1) 
            continue;

        if (!strncmp(params[0],"SMB", 3)) 
        {
            printf("ERROR: You are using a dbench 1 load file\n");
            if (GetHandleInformation((HANDLE)hWinEventHandle, &dwFlags))
                break;
        }
        if (!strcmp(params[0], "BM_SETUP"))
        {
            bm_state = BM_SETUP;
        } 
        else if (!strcmp(params[0], "BM_WARMUP"))
        {
            bm_state = BM_WARMUP;
        } 
        else if (!strcmp(params[0], "BM_MEASURE"))
        {
            bm_state = BM_MEASURE;
            if (verbose)
                fprintf(stderr, "setting state to BM_MEASURE\n");
        } 
        else if (!strcmp(params[0],"RECONNECT")) 
        {
            if (verbose)
                fprintf(stderr, "Reconnecting ...\n");
        } 
        else if (!strcmp(params[0], "SYNC"))
        {
            int length = atoi(params[1]), st = 0;
            if (verbose)
                fprintf(stderr, "Syncing for %d seconds\n", length);
        } 
        else if (!strcmp(params[0],"NTCreateX")) 
        {
            if (nb_createx(params[1], ival(params[2]), ival(params[3]), ival(params[4])) == -1)
                break;
        } 
        else if (!stricmp(params[0],"SetLocker"))
        {
            if (nb_SetLocker(params[1]) == -1)
                break;
        } 
        else if (!stricmp(params[0],"Xrmdir")) 
        {
            if (nb_Xrmdir(params[1], params[2]) == -1)
                break;
        } 
        else if (!stricmp(params[0],"Mkdir")) 
        {
            if (nb_Mkdir(params[1]) == -1)
                break;
        } 
        else if (!stricmp(params[0],"Attach")) 
        {
            if (nb_Attach(params[1], params[2]) == -1)
                break;
        } 
        else if (!stricmp(params[0],"Detach")) 
        {
            if (nb_Detach(params[1], params[2]) == -1)
                break;
        } 
        else if (!stricmp(params[0],"CreateFile")) 
        {
            if (nb_CreateFile(params[1], atol(params[2])) == -1)
                break;
        } 
        else if (!stricmp(params[0],"CopyFiles")) 
        {
            if (nb_CopyFile(params[1], params[2]) == -1)
                break;
        } 
        else if (!stricmp(params[0],"DeleteFiles")) 
        {
            if (nb_DeleteFile(params[1]) == -1)
                break;
        } 
        else if (!stricmp(params[0],"Move")) 
        {
            if (nb_Move(params[1], params[2]) == -1)
                break;
        } 
        else if (!stricmp(params[0],"Xcopy")) 
        {
            if (nb_xcopy(params[1], params[2]) == -1)
                break;
        } 
        else if (!strcmp(params[0],"Close")) 
        {
            if (nb_close(ival(params[1])) == -1)
                break;
        } 
        else if (!strcmp(params[0],"Rename")) 
        {
            if (nb_rename(params[1], params[2]) == -1)
                break;
        } 
        else if (!strcmp(params[0],"Unlink")) 
        {
            if (nb_unlink(params[1]) == -1)
                break;
        } 
        else if (!strcmp(params[0],"Deltree"))
        {
            if (nb_deltree(params[1]) == -1)
                break;
        } 
        else if (!strcmp(params[0],"Rmdir")) 
        {
            if (nb_rmdir(params[1]) == -1)
                break;
        } 
        else if (!strcmp(params[0],"QUERY_PATH_INFORMATION"))
        {
            if (nb_qpathinfo(params[1], ival(params[2])) == -1)
                break;
        } 
        else if (!strcmp(params[0],"QUERY_FILE_INFORMATION")) 
        {
            if (nb_qfileinfo(ival(params[1])) == -1)
                break;
        } 
        else if (!strcmp(params[0],"QUERY_FS_INFORMATION")) 
        {
            if (nb_qfsinfo(ival(params[1])) == -1)
                break;
        } 
        else if (!strcmp(params[0],"FIND_FIRST")) 
        {
            if (nb_findfirst(params[1]) == -1)
                break;
        } 
        else if (!strcmp(params[0],"WriteX")) 
        {
            if (nb_writex(ival(params[1]), ival(params[2]), ival(params[3]), ival(params[4])) == -1)
                break;
        } 
        else if (!strcmp(params[0],"ReadX")) 
        {
            if (nb_readx(ival(params[1]), ival(params[2]), ival(params[3]), ival(params[4])) == -1)
                break;
        } 
        else if (!strcmp(params[0],"Flush")) 
        {
            if (nb_flush(ival(params[1])) == -1)
                break;
        } 
        else if (!strcmp(params[0],"LockingX")) 
        {
            if (nb_lock(ival(params[1]), ival(params[2]), ival(params[3]), ival(params[4]),
                        (unsigned char)ival(params[5]), ival(params[6])) == -1)
                break;
        } 
        else 
        {
            printf("Unknown operation %s\n", params[0]);
            printf("Line read = %s\n", line1);
            break;
        }
    }
    CloseHandle(hFile);

//    nb_cleanup(cname);

    EndSecondTime(CMD_NONAFS);

    for (i = 0; i < MAX_FILES; i++) 
    {
        if (ftable[i].handle > 0)
            nb_close(ftable[i].handle);
    }

	DeleteCriticalSection(&CriticalSection);

    return(correct);
}

typedef long ( __cdecl  *PPIOCTL)(char *pathp, long opcode, struct ViceIoctl *blobp, int follow);

int IsOnline(char *strPath)
{
    int     bret;
    char    space[2048];
    int     code;
    int     rc;
    struct  ViceIoctl blob;
    struct  VolumeStatus *status;
    static PPIOCTL ppioctl = NULL;
    static HINSTANCE hAfsDll = NULL;

    rc = WaitForSingleObject(OSMutexHandle, 5 * 1000);
    bret = FALSE;
    if (hAfsDll == NULL)
        hAfsDll = LoadLibrary(AFSDLL);
    if (hAfsDll)
    {
        if (ppioctl == NULL)
            ppioctl = (PPIOCTL)GetProcAddress(hAfsDll, "pioctl");
        if (ppioctl != NULL)
        {
            blob.in_size = 0;
            blob.out_size = sizeof(space);
            blob.out = space;
#if 0
            if (!(code = ppioctl(strPath, VIOCGETVOLSTAT, &blob, 1)))
            {
                bret = WINTORTURE_ASFDLL_ONLINE;
                status = (VolumeStatus *)space;
                if (!status->Online || !status->InService || !status->Blessed || status->NeedsSalvage)
                    bret = WINTORTURE_ASFDLL_OFFLINE;
            }
#else
            errno = 0;
            code = ppioctl(strPath, VIOC_PATH_AVAILABILITY, &blob, 1);
            if (!code) {
                switch (errno) {
                case ENXIO:
                case ENOSYS:
                case EBUSY:
                    bret = WINTORTURE_ASFDLL_OFFLINE;
                    break;
                default:
                    bret = WINTORTURE_ASFDLL_ONLINE;
                    break;
                }
            }
#endif 
        }
        else
            bret = WINTORTURE_ASFPIOCTL_NOTFOUND;
    }
    else
        bret = WINTORTURE_ASFDLL_NOTFOUND;

    if (rc == WAIT_OBJECT_0)
        ReleaseMutex(OSMutexHandle);

    return(bret);
}

