//=-- PlatformAbstractionWin32.cpp - Definition of the Abstraction Layer -----=
//
//  This file gives a definition of the Platform Abstraction Layer for the 
//  Win32 platform.  The Platform Abstraction Layer is the method used to 
//  isolate system specific functionality from the core of MagicStats.  The
//  Platform Abstraction Layer for unix is stored in the 
//  PlatformAbstractionUnix.cpp file.
//
//=---------------------------------------------------------------------------=
//  This file is copyright (c) 1997-2000 Chris Lattner
//=---------------------------------------------------------------------------=

#include "PlatformAbstraction.h"   // Defines WINDOWS if under windows

#ifdef WINDOWS

#include "ResourceManager.h"
#include "LinkedList.h"
#include "MSFilename.h"
#include "Options.h"

#define WIN32_LEAN_AND_MEAN
#include <windows.h>         // LoadLibrary
#include <mmsystem.h>        // timeGetTime
#include <io.h>              // _findfirst/_findnext/_findclose
#include <sys/stat.h>        // stat
#include <string.h>
#include <direct.h>          // chdir, getcwd

const char *PlatformAbstraction::PluginSuffix       = ".dll";

// Windows prefers the \ character as the directory seperator.
//
const char  PlatformAbstraction::DirectorySeperator = '\\';

PlatformAbstraction::PlatformAbstraction() {     // Initialize platform specific stuff...

  // Should be relative to the install location read from the registry.
  ResourceManager::AddResourceLocation("\\Page\\", 
                                       "/stats/", ResourceManager::ServerResource);
}

PlatformAbstraction::~PlatformAbstraction() {    // Shutdown platform specific stuff...
}

// LoadPlugin - Implemented in terms of LoadLibrary for windows...
//
// Loads a shared object into this address space, returns an error message
// on error, or a null pointer on success....
//
const char *PlatformAbstraction::LoadPlugin(const MSFilename &Filename) {
  static char ErrorCode[10000];

  if (LoadLibrary(Filename) == 0) {
    // Convert the number in GetLastError() into an error string we can pass back...

    LPVOID lpMsgBuf;

    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | 
                  FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(),
                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
                  (LPTSTR) &lpMsgBuf, 0, NULL);
    // lpMsgBuf now points to the error string...

    strcpy(ErrorCode, (const char*)lpMsgBuf);

    // Free the buffer.
    LocalFree( lpMsgBuf );
    return ErrorCode;
  }

  return 0;
}

const char *PlatformAbstraction::GetServerConfigFilename() {
  return "Global.cfg";  // Should be relative to install directory taken from registry
}

// <windows.h> #defines GetUserName, which conflicts with us.  We'll just use
// GetUserNameA instead, which may not be terribly portable, but will work.
#undef GetUserName
#define WinGetUserName GetUserNameA

String PlatformAbstraction::GetUserName() { 
  char Buffer[1000];
  unsigned long Size = 1000;
  if (::WinGetUserName(Buffer, &Size))
    return Buffer;
  
  return "Unknown User";
}

int PlatformAbstraction::ScanDirectory(const MSFilename &Dir, LinkedList<String> &Dest, FileType FT) {
  struct _finddata_t FindBuf;
  long FindHandle;
  int Attrib;

  switch (FT) {
  case FileType::RegularFile:
    Attrib = _S_IFREG;
    break;
  case FileType::Directory:
    Attrib = _A_SUBDIR;
    break;
  }

  char SearchBuffer[2000];
  strcpy(SearchBuffer, Dir);
  strcat(SearchBuffer, "\\*.*");

  
  if ((FindHandle = _findfirst(SearchBuffer, &FindBuf)) == -1) 
    return 1;  // No entries found...

  do {
    if (FindBuf.attrib & Attrib)      // Check to see if it's the right type of file.
      Dest.AddToTail(FindBuf.name);
  } while (_findnext(FindHandle, &FindBuf) != -1);

  _findclose(FindHandle);
  return 0;
}

// Change all Unix style / directory seperators into windows style \ seperators
//
void PlatformAbstraction::DecodeFilename(String &Filename) { 
  int i = Filename.strchr('/');
  while (i != -1) {
    Filename[i] = '\\';
    i = Filename.strchr('/', i);
  }
}

// ChDir - Set the current working directory in the local filesystem.
//
int PlatformAbstraction::ChDir(const MSFilename &Directory) {
  return chdir(Directory);
}

// GetCWD - Get the current working directory in the local filesystem.
//
MSFilename PlatformAbstraction::GetCWD(void) {
  char Buffer[10000];
  return getcwd(Buffer, 9999);
}

// Unix and windows don't agree on any one function to do case insensitive
// string comparisons.  Because Unix is better than Windows, we name the 
// function the unix name.
//
int PlatformAbstraction::strcasecmp(const char *s1, const char *s2) {
  return _stricmp(s1, s2);
}

// CreateHTMLSymlink - Creates a link between OutputFilename and LinkFilename.
// on a unix platform, this would create an actual symlink between the two 
// files, but on a machine without link support, this could create a meta 
// refresh page to forward the user to their destination...
void PlatformAbstraction::CreateHTMLSymlink(const MSFilename &OutputFilename, 
                                            const MSFilename &LinkFilename) {
}

// GetSystemTime() - Return the time in seconds (with as much precision as 
// possible) to the caller from an abitrary reference point (like when the 
// machine was booted or 1970, whatever.  :)
//
double PlatformAbstraction::GetSystemTime() {
  return timeGetTime() / 1000.0;
}

// InstallInterruptHandler(int *x) - Install a signal handler to catch the
// interrupt character (ie CTRL-C).  When pressed, set the specified int
// to a true value...
//
void PlatformAbstraction::InstallInterruptHandler(volatile int *Notifier) {
  if (MSOptions::Verbose)
    cout << "InstallInterruptHandler...\n";
#pragma message("TODO: InstallInterruptHandler should be implemented for Win32")
}

#endif
