#ifdef SYSTEM_WIN32
#pragma warning(disable:4996)
#endif

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef ZHLT_NETVIS
#ifdef SYSTEM_WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#endif

#ifdef STDC_HEADERS
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#endif

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef ZHLT_NETVIS
#include "../netvis/c2cpp.h"
#endif

#include "cmdlib.h"
#include "messages.h"
#include "hlassert.h"
#include "log.h"
#include "filelib.h"
#include <windows.h>

char*           g_Program = "Uninitialized variable ::g_Program";
char            g_Mapname[_MAX_PATH] = "Uninitialized variable ::g_Mapname";

developer_level_t g_developer = DEFAULT_DEVELOPER;
bool            g_verbose = DEFAULT_VERBOSE;
bool            g_log = DEFAULT_LOG;

unsigned long   g_clientid = 0;
unsigned long   g_nextclientid = 0;

static FILE*    CompileLog = NULL;
static bool     fatal = false;

bool g_noConColors = false;

////////

void            ResetTmpFiles()
{
    if (g_log)
    {
        char            filename[_MAX_PATH];

        safe_snprintf(filename, _MAX_PATH, "%s.bsp", g_Mapname);
        _unlink(filename);

        safe_snprintf(filename, _MAX_PATH, "%s.inc", g_Mapname);
        _unlink(filename);

        safe_snprintf(filename, _MAX_PATH, "%s.p0", g_Mapname);
        _unlink(filename);

        safe_snprintf(filename, _MAX_PATH, "%s.p1", g_Mapname);
        _unlink(filename);

        safe_snprintf(filename, _MAX_PATH, "%s.p2", g_Mapname);
        _unlink(filename);

        safe_snprintf(filename, _MAX_PATH, "%s.p3", g_Mapname);
        _unlink(filename);

        safe_snprintf(filename, _MAX_PATH, "%s.prt", g_Mapname);
        _unlink(filename);

        safe_snprintf(filename, _MAX_PATH, "%s.pts", g_Mapname);
        _unlink(filename);

        safe_snprintf(filename, _MAX_PATH, "%s.lin", g_Mapname);
        _unlink(filename);

        safe_snprintf(filename, _MAX_PATH, "%s.wic", g_Mapname);
        _unlink(filename);
    }
}

void            ResetLog()
{
    if (g_log)
    {
        char            logfilename[_MAX_PATH];

        safe_snprintf(logfilename, _MAX_PATH, "%s.log", g_Mapname);
        _unlink(logfilename);
    }
}

void            ResetErrorLog()
{
    if (g_log)
    {
        char            logfilename[_MAX_PATH];

        safe_snprintf(logfilename, _MAX_PATH, "%s.err", g_Mapname);
        _unlink(logfilename);
    }
}

void            CheckForErrorLog()
{
    if (g_log)
    {
        char            logfilename[_MAX_PATH];

        safe_snprintf(logfilename, _MAX_PATH, "%s.err", g_Mapname);
        if (q_exists(logfilename))
        {
            Log(">> There was a problem compiling the map.\n"
                ">> Check the file %s.log for the cause.\n",
                 g_Mapname);
            exit(1);
        }
    }
}

///////

void            LogError(const char* const message)
{
    if (g_log && CompileLog)
    {
        char            logfilename[_MAX_PATH];
        FILE*           ErrorLog = NULL;

        safe_snprintf(logfilename, _MAX_PATH, "%s.err", g_Mapname);
        ErrorLog = fopen(logfilename, "a");

        if (ErrorLog)
        {
            fprintf(ErrorLog, "%s: %s\n", g_Program, message);
            fflush(ErrorLog);
            fclose(ErrorLog);
            ErrorLog = NULL;
        }
        else
        {
            fprintf(stderr, "ERROR: Could not open error logfile %s", logfilename);
            fflush(stderr);
        }
    }
}

void CDECL      OpenLog(const int clientid)
{
    if (g_log)
    {
        char            logfilename[_MAX_PATH];

#ifdef ZHLT_NETVIS
    #ifdef SYSTEM_WIN32
        if (clientid)
        {
            char            computername[MAX_COMPUTERNAME_LENGTH + 1];
            unsigned long   size = sizeof(computername);

            if (!GetComputerName(computername, &size))
            {
                safe_strncpy(computername, "unknown", sizeof(computername));
            }
            safe_snprintf(logfilename, _MAX_PATH, "%s-%s-%d.log", g_Mapname, computername, clientid);
        }
        else
    #endif
    #ifdef SYSTEM_POSIX
        if (clientid)
        {
            char            computername[_MAX_PATH];
            unsigned long   size = sizeof(computername);

            if (gethostname(computername, size))
            {
                safe_strncpy(computername, "unknown", sizeof(computername));
            }
            safe_snprintf(logfilename, _MAX_PATH, "%s-%s-%d.log", g_Mapname, computername, clientid);
        }
    #endif
#endif
        {
            safe_snprintf(logfilename, _MAX_PATH, "%s.log", g_Mapname);
        }
        CompileLog = fopen(logfilename, "a");

        if (!CompileLog)
        {
            fprintf(stderr, "ERROR: Could not open logfile %s", logfilename);
            fflush(stderr);
        }
    }
}

void CDECL      CloseLog()
{
    if (g_log && CompileLog)
    {
        LogEnd();
        fflush(CompileLog);
        fclose(CompileLog);
        CompileLog = NULL;
    }
}

//
//  Every function up to this point should check g_log, the functions below should not
//

#ifdef SYSTEM_WIN32
// AJM: fprintf/flush wasnt printing newline chars correctly (prefixed with \r) under win32
//      due to the fact that those streams are in byte mode, so this function prefixes 
//      all \n with \r automatically.
//      NOTE: system load may be more with this method, but there isnt that much logging going
//      on compared to the time taken to compile the map, so its negligable.
void            Safe_WriteLog(const char* const message)
{
    const char* c;
    
    if (!CompileLog)
        return;

    c = &message[0];

    while (1)
    {
        if (!*c)
            return; // end of string

        if (*c == '\n')
            fputc('\r', CompileLog);

        fputc(*c, CompileLog);

        c++;
    }
}
#endif

void            WriteLog(const char* const message)
{

#ifndef SYSTEM_WIN32
    if (CompileLog)
    {
        fprintf(CompileLog, message);
        fflush(CompileLog);
    }
#else
    Safe_WriteLog(message);
#endif

    fprintf(stdout, message);
    fflush(stdout);
}

// =====================================================================================
//  CheckFatal 
// =====================================================================================
void            CheckFatal()
{
    if (fatal)
    {
        hlassert(false);
        exit(1);
    }
}

#define MAX_ERROR   2048
#define MAX_WARNING 2048
#define MAX_MESSAGE 2048

// =====================================================================================
//  Error
//      for formatted error messages, fatals out
// =====================================================================================
void CDECL      Error(const char* const error, ...)
{
    char            message[MAX_ERROR];
    char            message2[MAX_ERROR];
    va_list         argptr;
    
 /*#if defined( SYSTEM_WIN32 ) && !defined( __MINGW32__ ) && !defined( __BORLANDC__ )
    {
        char* wantint3 = getenv("WANTINT3");
		if (wantint3)
		{
			if (atoi(wantint3))
			{
				__asm
				{
					int 3;
				}
			}
		}
    }
#endif*/

    va_start(argptr, error);
    _vsnprintf_s(message, MAX_ERROR, error, argptr);
    va_end(argptr);

    safe_snprintf(message2, MAX_MESSAGE, "Error: %s\n", message);

	ConColor(CONCOLOR_RED, CONCOLOR_BGDEF_BLACK); //SILENCER
    WriteLog(message2);
	ConColor(CONCOLOR_TXTDEF_WHITE, CONCOLOR_BGDEF_BLACK); //SILENCER

    LogError(message2);

    fatal = 1;
    CheckFatal();
}

// =====================================================================================
//  Fatal
//      For formatted 'fatal' warning messages
//      automatically appends an extra newline to the message
//      This function sets a flag that the compile should abort before completing
// =====================================================================================
void CDECL      Fatal(assume_msgs msgid, const char* const warning, ...)
{
    char            message[MAX_WARNING];
    char            message2[MAX_WARNING];

    va_list         argptr;

    va_start(argptr, warning);
    _vsnprintf_s(message, MAX_WARNING, warning, argptr);
    va_end(argptr);

    safe_snprintf(message2, MAX_MESSAGE, "Error: %s\n", message);

	ConColor(CONCOLOR_RED, CONCOLOR_BGDEF_BLACK); //SILENCER
    WriteLog(message2);
	ConColor(CONCOLOR_TXTDEF_WHITE, CONCOLOR_BGDEF_BLACK); //SILENCER

    LogError(message2);

    {
        char            message[MAX_MESSAGE];
        const MessageTable_t* msg = GetAssume(msgid);

        safe_snprintf(message, MAX_MESSAGE, "%s\nDescription: %s\nHowto Fix: %s\n", msg->title, msg->text, msg->howto);
        PrintOnce(message);
    }

    fatal = 1;
}

// =====================================================================================
//  PrintOnce
//      This function is only callable one time. Further calls will be ignored
// =====================================================================================
void CDECL      PrintOnce(const char* const warning, ...)
{
    char            message[MAX_WARNING];
    char            message2[MAX_WARNING];
    va_list         argptr;
    static int      count = 0;

    if (count > 0) // make sure it only gets called once
    {
        return;
    }
    count++;

    va_start(argptr, warning);
    _vsnprintf_s(message, MAX_WARNING, warning, argptr);
    va_end(argptr);

    safe_snprintf(message2, MAX_MESSAGE, "Error: %s\n", message);
    WriteLog(message2);
    LogError(message2);
}

// =====================================================================================
//  Warning
//      For formatted warning messages
//      automatically appends an extra newline to the message
// =====================================================================================
void CDECL      Warning(const char* const warning, ...)
{
    char            message[MAX_WARNING];
    char            message2[MAX_WARNING];

    va_list         argptr;

    va_start(argptr, warning);
    _vsnprintf_s(message, MAX_WARNING, warning, argptr);
    va_end(argptr);

    safe_snprintf(message2, MAX_MESSAGE, "Warning: %s\n", message);
	ConColor(CONCOLOR_YELLOW, CONCOLOR_BGDEF_BLACK); //SILENCER
    WriteLog(message2);
	ConColor(CONCOLOR_TXTDEF_WHITE, CONCOLOR_BGDEF_BLACK); //SILENCER
}

// =====================================================================================
//  Verbose
//      Same as log but only prints when in verbose mode
// =====================================================================================
void CDECL      Verbose(const char* const warning, ...)
{
    if (g_verbose)
    {
        char            message[MAX_MESSAGE];

        va_list         argptr;

        va_start(argptr, warning);
        _vsnprintf_s(message, MAX_MESSAGE, warning, argptr);
        va_end(argptr);

        WriteLog(message);
    }
}

// =====================================================================================
//  Developer
//      Same as log but only prints when in developer mode
// =====================================================================================
void CDECL      Developer(developer_level_t level, const char* const warning, ...)
{
    if (level <= g_developer)
    {
        char            message[MAX_MESSAGE];

        va_list         argptr;

        va_start(argptr, warning);
        _vsnprintf_s(message, MAX_MESSAGE, warning, argptr);
        va_end(argptr);

        WriteLog(message);
    }
}

// =====================================================================================
//  DisplayDeveloperLevel
// =====================================================================================
static void     DisplayDeveloperLevel()
{
    char            message[MAX_MESSAGE];

    safe_strncpy(message, "Developer messages enabled : [", MAX_MESSAGE);
    if (g_developer >= DEVELOPER_LEVEL_MEGASPAM)
    {
        safe_strncat(message, "MegaSpam ", MAX_MESSAGE);
    }
    if (g_developer >= DEVELOPER_LEVEL_SPAM)
    {
        safe_strncat(message, "Spam ", MAX_MESSAGE);
    }
    if (g_developer >= DEVELOPER_LEVEL_FLUFF)
    {
        safe_strncat(message, "Fluff ", MAX_MESSAGE);
    }
    if (g_developer >= DEVELOPER_LEVEL_MESSAGE)
    {
        safe_strncat(message, "Message ", MAX_MESSAGE);
    }
    if (g_developer >= DEVELOPER_LEVEL_WARNING)
    {
        safe_strncat(message, "Warning ", MAX_MESSAGE);
    }
    if (g_developer >= DEVELOPER_LEVEL_ERROR)
    {
        safe_strncat(message, "Error", MAX_MESSAGE);
    }
    if (g_developer)
    {
        safe_strncat(message, "]\n", MAX_MESSAGE);
        Log(message);
    }
}

// =====================================================================================
//  Log
//      For formatted log output messages
// =====================================================================================
void CDECL      Log(const char* const warning, ...)
{
    char            message[MAX_MESSAGE];

    va_list         argptr;

    va_start(argptr, warning);
    _vsnprintf_s(message, MAX_MESSAGE, warning, argptr);
    va_end(argptr);

    WriteLog(message);
}

// =====================================================================================
//  LogArgs
// =====================================================================================
static void     LogArgs(int argc, char** argv)
{
    int             i;
    Log("Command line: ");
    for (i = 0; i < argc; i++) {
        if (strchr(argv[i], ' ')) {
            Log("\"%s\"", argv[i]);
        } else {
            Log("%s ", argv[i]);
        }
    }
    Log("\n");
}

// =====================================================================================
//  Banner
// =====================================================================================
void            Banner()
{
	// SILENCER -->
	// Made this whole section a lot better, as in, more dynamic
	// Message block - keep line lengths the same or the code below will screw up
	char message_block[] =
		" Super Half-Life Compilation Tools -- Custom Build.      \n"
		" Based on code modifications by Sean 'Zoner' Cavanaugh.  \n"
		" Based on Valve's version, modified with permission.     \n"
		" Further development done by Silencer and Protector.     \n"
		" Submit detailed bug reports to amckern@yahoo.com.       ";
	
	int linelength = 0;
	int lines = 1;
	bool gotlength = false;
	const int msgblen = (int)strlen(message_block);
	for(int i = 0; i < msgblen; i++) {
		if(message_block[i] != '\n') {
			if(!gotlength) {
				linelength++;
			}
		} else {
			lines++;
			gotlength = true;
		}
	}
	
	ConColor(CONCOLOR_BGDEF_BLACK, CONCOLOR_TXTDEF_WHITE);
    Log("         %s " ZHLT_VERSIONSTRING " " HACK_VERSIONSTRING " (%s)           ", g_Program, __DATE__);
	ConColor(CONCOLOR_TXTDEF_WHITE, CONCOLOR_BGDEF_BLACK);
	Log("\n");
	
	for(int i = 0; i < lines; i++) {
		ConColor(CONCOLOR_BGDEF_BLACK, CONCOLOR_TXTDEF_WHITE);
		Log("  ");
		ConColor(CONCOLOR_TXTDEF_WHITE, CONCOLOR_BGDEF_BLACK);
		char line[128];
		int j = 0;
		while(j < linelength && j < 127) {
			line[j] = message_block[(i * (linelength + 1)) + j];
			j++;
		}
		line[j] = '\0';
		Log(line);
		ConColor(CONCOLOR_BGDEF_BLACK, CONCOLOR_TXTDEF_WHITE);
		Log("  ");
		ConColor(CONCOLOR_TXTDEF_WHITE, CONCOLOR_BGDEF_BLACK);
		Log("\n");
	}
	// <-- SILENCER
}

// =====================================================================================
//  LogStart
// =====================================================================================
void            LogStart(int argc, char** argv)
{
    Banner();
	ConColor(CONCOLOR_BGDEF_BLACK, CONCOLOR_TXTDEF_WHITE); //SILENCER
    Log("                  -----  BEGIN  %s -----                  ", g_Program);
	ConColor(CONCOLOR_TXTDEF_WHITE, CONCOLOR_BGDEF_BLACK); //SILENCER
	Log("\n\n");
    LogArgs(argc, argv);
    DisplayDeveloperLevel();
}

// =====================================================================================
//  LogEnd
// =====================================================================================
void            LogEnd()
{
    Log("\n-----   END   %s -----\n\n\n\n", g_Program);
}

// =====================================================================================
//  hlassume
// =====================================================================================

void hlassume(bool exp, assume_msgs msgid) // Removed option to append info messages again, because it slowed down tools
{
    if (!exp)
	{
		/*//SILENCER: Commenting this feature out// SILENCER -->
		char* check;
		char appendmsgdefault[2048] = "Info: No additional information given.";

		va_list p_args;

		va_start(p_args, msgid);

		if((check = va_arg(p_args, char*)) != NULL) {
			safe_snprintf(appendmsg, 2047, "Info: %s", check);
		}

		va_end(p_args);
		// <-- SILENCER*/

		char            message[MAX_MESSAGE];
		const MessageTable_t* msg = GetAssume(msgid);

		safe_snprintf(message, MAX_MESSAGE, "%s\nDescription: %s\nHowto Fix: %s\n", msg->title, msg->text, msg->howto);
		Error(message);
    }
}

// =====================================================================================
//  seconds_to_hhmm
// =====================================================================================
static void seconds_to_hhmm(unsigned int elapsed_time, unsigned& days, unsigned& hours, unsigned& minutes, unsigned& seconds)
{
    seconds = elapsed_time % 60;
    elapsed_time /= 60;

    minutes = elapsed_time % 60;
    elapsed_time /= 60;

    hours = elapsed_time % 24;
    elapsed_time /= 24;

    days = elapsed_time;
}

// =====================================================================================
//  LogTimeElapsed
// =====================================================================================
void LogTimeElapsed(float elapsed_time)
{
    unsigned days = 0;
    unsigned hours = 0;
    unsigned minutes = 0;
    unsigned seconds = 0;

    seconds_to_hhmm(elapsed_time, days, hours, minutes, seconds);

    if (days)
    {
        Log("%.2f seconds elapsed [%ud %uh %um %us]\n", elapsed_time, days, hours, minutes, seconds);
    }
    else if (hours)
    {
        Log("%.2f seconds elapsed [%uh %um %us]\n", elapsed_time, hours, minutes, seconds);
    }
    else if (minutes)
    {
        Log("%.2f seconds elapsed [%um %us]\n", elapsed_time, minutes, seconds);
    }
    else
    {
        Log("%.2f seconds elapsed\n", elapsed_time);
    }
}

// SILENCER -->
void ConColor(const short c_text_color, const short c_background_color) {
	if(!g_noConColors) {
		SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), (16 * c_background_color) + c_text_color);
	}
}
// <-- SILENCER
