// -*-C++-*- 

/*  src/platform/Win32Platform.cpp  */

/*
 * Author: Philogelos A. <Philogelos@yahoo.com>
 * Maintainer: Philogelos A.
 * Keywords: C++, library, containers
 *
 * Copyright (C) 1998 Philogelos A.
 *
 * This file is part of Quercus Robusta.
 *
 * Quercus Robusta is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Library General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this software; see the file COPYING.LIB.  If not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 */


/* $Id$ */

#include "platform/Win32Platform.hpp"

#if defined( USE_WIN32 )

#if !defined(_INLINE)
static char cvsid[] = "@(#)$Id$";
static char debugFileId[] = __FILE__;
#endif

#include <sys/types.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <limits.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <process.h>
#include <conio.h>

#include "String.hpp"
#include "Debug.hpp"
#include "platform/threads/Thread.hpp"
#include "platform/threads/ThreadFactory.hpp"
#include "platform/threads/ThreadsSystem.hpp"
#include "platform/threads/MGuard.hpp"
#include "platform/threads/Semaphore.hpp"
#include "exceptions/Errno.hpp"

static Mutex     *logMutex = NULL;

Win32Platform::Win32Platform()
{
  static char moduleName[ MODULE_NAME_LENGTH ];
  COORD size;


  ::GetModuleFileName( NULL, moduleName, MODULE_NAME_LENGTH );
  setImageName( moduleName );

  logMutex = THR_SYSTEM -> mutex();
  TCAST( logMutex, Object ) -> dontManage();

  size.X = CONSOLE_WIDTH;
  size.Y = CONSOLE_HEIGHT;

  AllocConsole();
  if( !SetConsoleScreenBufferSize( 
								  GetStdHandle( STD_OUTPUT_HANDLE ), size ) )
	{
	  DLOG
  		( "Cannot SetConsoleScreenBufferSize(): %s", 
  		  PLAF -> getErrorDescription( GetLastError() ) );
	}
}

Win32Platform::~Win32Platform()
{}

void Win32Platform::rawErrorVPrintf( const char * format, va_list raw ) 
{
  preC_( format != NULL );

  char *buffer;
  DWORD length;

  length = PLAF -> vprintfLength( format, raw );
  buffer = new char[ length + 1 ];
  PLAF -> vsprintf( buffer, format, raw );

  MGuard _lock( logMutex, this );

  _cputs( buffer );
  delete []buffer;
}


char *Win32Platform::getErrorDescription( int errCode )
{
  LPVOID lpMsgBuf;
  static char errorMessage[ ERROR_MESSAGE_LENGTH + 1 ];
  Index retCode;

  ++callsNum;

  retCode = FormatMessage
	( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
	  NULL, errCode, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
	  ( LPTSTR ) &lpMsgBuf, 0, NULL );
  if( retCode > 0 )
	{
	  if( retCode > ERROR_MESSAGE_LENGTH )
		{
		  retCode = ERROR_MESSAGE_LENGTH;
		}
	  PLAF -> memcpy( errorMessage, ( char * ) lpMsgBuf, retCode );
	}
  else
	{
	  char *fallBack;

	  fallBack = "Error when formatting error message";
	  test_( PLAF -> strlen( fallBack ) < ERROR_MESSAGE_LENGTH );
	  PLAF -> strcpy( errorMessage, fallBack );
	  ::LocalFree( lpMsgBuf );
	}
  return errorMessage;
}

void Win32Platform::exitImmediate( long status )
{
  ++callsNum;
  _exit( ( UINT ) status );
}
  
void Win32Platform::suicide()
{
  ++callsNum;
  abort();
}

void Win32Platform::attachDebugger()
{
  static char systemCmd[ SYSTEM_CMD_LENGTH + 1 ];
  boolean MelTheProgrammer;

  ++callsNum;
  this -> sprintf( systemCmd, "%s %li", DEBUGGER_COMMAND, _getpid() );
  if( system( systemCmd ) == -1 )
	{
	  rawErrorPrintf( "Error when attaching debugger" );
	  suicide();
	}
  MelTheProgrammer = true;
  while( MelTheProgrammer )
	{}
}

void Win32Platform::setImageName( const char *aName )
{
  preC_( aName != NULL );
  
  imageName = aName;
}

const char *Win32Platform::getImageName() const
{
  return imageName;
}

char *Win32Platform::getTimeStampString()
{
  static char buffer[ 40 ];
  SYSTEMTIME instant;

  GetLocalTime( &instant );

  sprintf( buffer, "%4.4i/%2.2i/%2.2i %2.2i:%2.2i:%2.2i.%3.3i",
		   instant.wYear, instant.wMonth, instant.wDay,
		   instant.wHour, instant.wMinute, instant.wSecond,
		   instant.wMilliseconds );
  return buffer;
}

char *Win32Platform::getNL() const
{
  return "\r\n";
}

void *Win32Platform::allocateMemoryBootstrapWin32( Index aSize )
{
  return allocateMemoryBootstrapANSI( aSize );
}

void  Win32Platform::deallocateMemoryBootstrapWin32( void *aPointer )
{
  deallocateMemoryBootstrapANSI( aPointer );
}

boolean Win32Platform::equals( const Top *anOther ) const
{
  return( DCAST( anOther, Win32Platform ) == this );
}

Top *Win32Platform::clone() const
{
  return ( Top * ) this;
}

String  Win32Platform::toString() const
{
  return Object::toString() + " calls: " + String( callsNum );
}

String  Win32Platform::getClassName() const
{
  return "Win32Platform";
}

Win32Object::Win32Object( HANDLE handle )
{
  setHandle( handle );
}

Win32Object::~Win32Object()
{
  if( getHandle() != NULL )
	{
	  if( !CloseHandle( getHandle() ) && !std::uncaught_exception() ) 
		{
		  throw new Errno
			( String( "CloseHandle() in Win32Object::~Win32Object() " ) +
			  getClassName(), 
			  null, 
			  GetLastError() );
		}
	}
}

HANDLE Win32Object::getHandle() const
{
  return handle;
}

void Win32Object::setHandle( HANDLE aHandle )
{
  handle = aHandle;
}
		

#if defined( WRAP_MAIN )

#include "exceptions/Exception.hpp"
#include "containers/lists/List.hpp"
#include "Backtrace.hpp"
#include "new-operator.hpp"
#include "LinkManager.hpp"
#include "OGuard.hpp"


int WINAPI WinMain( HINSTANCE /* hInstance */, 
				    HINSTANCE /* hPrevInstance */, 
					LPSTR /* lpCmdLine */, 
					int /* nCmdShow */ )
{
  List *args;

  try
	{
	  int retCode;

	  Platform::getInstance() -> setImageName( __argv[ 0 ] );
	  Backtrace::initBacktrace();

#if defined( USE_THREADS )
	  Top::initThreads();
	  operatorNewInitThreads();
#endif	  

	  args = new List();
	  LinkManager::reg( args, args );

	  for( int i = 0 ; i < __argc ; ++i )
		{
		  test_( __argv[ i ] != NULL );
		  args -> append( new String( __argv[ i ] ) );
		}
	  test_( __argv[ __argc ] == NULL );

	  retCode = QRMain( args );

	  LinkManager::free( args, args );
	  return retCode;
	}
  catch( Exception *anEx )
	{
	  OGuard _ex( anEx, anEx );
	  Debug::getLogger() -> exception( anEx );
	}
  catch( Throwable *thrown )
	{
	  DLOG( "Throwable catched" );
	}
  catch( ... )
	{
	  DLOG( "Unidentified exception catched" );
	}
  return 0;
}
 
#endif

#if defined(_INLINE)
#include "../src/Debug.ipp"
#include "../src/String.ipp"
#endif

/* USE_WIN32 */
#endif

/* $Log$ */
