//-----------------------------------------------------------------------------
//	BugDlg_CompressScreenshot
//
//	Compress .BMP to .JPG, Delete .BMP
//-----------------------------------------------------------------------------
bool BugDlg_CompressScreenshot()
{
	if ( !g_bug_szScreenshot[0] )
	{
		return false;
	}

	bool bSuccess = false;
	HBITMAP hBitmap = NULL;
	HDC hDC = NULL;
	char *pBMPBits = NULL;

	CUtlBuffer buf( 0, 0 );

	hBitmap = (HBITMAP)LoadImage( NULL, g_bug_szScreenshot, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_DEFAULTSIZE | LR_LOADFROMFILE );
	if ( !hBitmap )
		goto cleanUp;

	hDC = CreateCompatibleDC( NULL );
	if ( !hDC )
		goto cleanUp;

	BITMAPINFO bitmapInfo;
	ZeroMemory( &bitmapInfo, sizeof( BITMAPINFO ) );
	bitmapInfo.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );

	// populate the bmp info
	if ( !GetDIBits( hDC, hBitmap, 0, 0, NULL, &bitmapInfo, DIB_RGB_COLORS ) )
		goto cleanUp;

	pBMPBits = (char *)Sys_Alloc( bitmapInfo.bmiHeader.biSizeImage );
	if ( !pBMPBits )
		goto cleanUp;
	
	// could be bottom-up or top-down
	int nHeight = abs( bitmapInfo.bmiHeader.biHeight );

	if ( bitmapInfo.bmiHeader.biBitCount != 32 )
	{
		// unexpected format
		goto cleanUp;
	}

	if ( bitmapInfo.bmiHeader.biCompression != BI_RGB && bitmapInfo.bmiHeader.biCompression != BI_BITFIELDS )
	{
		// unexpected format
		goto cleanUp;
	}

	// don't want color masks
	bitmapInfo.bmiHeader.biCompression = BI_RGB;

	// get the raw bits
	if ( !GetDIBits( hDC, hBitmap, 0, nHeight, pBMPBits, &bitmapInfo, DIB_RGB_COLORS ) )
		goto cleanUp;

	JSAMPROW row_pointer[1]; 

	// compression data structure
	struct jpeg_compress_struct cinfo;
	ZeroMemory( &cinfo, sizeof( jpeg_compress_struct ) );

	// point at stderr
	struct jpeg_error_mgr jerr;
	cinfo.err = jpeg_std_error( &jerr );

	// create compressor
	jpeg_create_compress( &cinfo );

	// Hook CUtlBuffer to compression
	jpeg_UtlBuffer_dest( &cinfo, &buf );

	// image width and height, in pixels
	cinfo.image_width = bitmapInfo.bmiHeader.biWidth;
	cinfo.image_height = nHeight;
	cinfo.input_components = 3;
	cinfo.in_color_space = JCS_RGB;

	// Apply settings
	jpeg_set_defaults( &cinfo );
	jpeg_set_quality( &cinfo, 50, TRUE );

	// Start compressor
	jpeg_start_compress( &cinfo, TRUE);
	
	char *pRowBuffer = (char*)_alloca( bitmapInfo.bmiHeader.biWidth * 3 );
	row_pointer[0] = (JSAMPROW)pRowBuffer;

	// Write scanlines
	while ( cinfo.next_scanline < cinfo.image_height ) 
	{
		char *pSrc;
		if ( bitmapInfo.bmiHeader.biHeight < 0 )
		{
			// top down
			pSrc = &pBMPBits[cinfo.next_scanline * bitmapInfo.bmiHeader.biWidth * 4];
		}
		else
		{
			// bottom up
			pSrc = &pBMPBits[(nHeight-1 - cinfo.next_scanline) * bitmapInfo.bmiHeader.biWidth * 4];
		}
		
		// convert to BGR to RGB
		char *pDst = pRowBuffer;
		for ( int i=0; i<bitmapInfo.bmiHeader.biWidth; i++ )
		{
			pDst[0] = pSrc[2];
			pDst[1] = pSrc[1];
			pDst[2] = pSrc[0];
			pSrc += 4;
			pDst += 3;
		}		
		jpeg_write_scanlines( &cinfo, row_pointer, 1 );
	}

	// Finalize image
	jpeg_finish_compress( &cinfo );

	char jpgFilename[MAX_PATH];
	Sys_StripExtension( g_bug_szScreenshot, jpgFilename, sizeof( jpgFilename ) );
	Sys_AddExtension( ".jpg", jpgFilename, sizeof( jpgFilename ) );
	if ( !Sys_SaveFile( jpgFilename, buf.Base(), buf.TellMaxPut() ) )
		goto cleanUp;

	// remove the uncompressed version
	unlink( g_bug_szScreenshot );
	strcpy( g_bug_szScreenshot, jpgFilename );

	bSuccess = true;

cleanUp:
	if ( hBitmap )
		DeleteObject( hBitmap );
	if ( hDC )
		DeleteDC( hDC );
	if ( pBMPBits )
		Sys_Free( pBMPBits );

	return bSuccess;
}
/**
 * 
 * This function blocks the current process while waiting for an event that sends data which meet the condition. 
 *
 * @param[in] eventID   Identifier of the event that need to occur
 * @param[in] function  Pointer to the function that represents the condition function (return true if condition is met and continues the process). If function = 0 .. condition is always met.
 * @return sys_event_data *  Pointer to the event data struct that contains the values carried by the event
 */
sys_event_data *Sys_Wait_For_Condition(uint eventID, pConditionFunction function){

    Sys_Block_Process(sys_running_process->pcb.process_ID, eventID, function);
    
    //This is only executed if process continued
    //now get return value (tansmited event data)
    Sys_Start_AtomicSection();
    sys_process_event_handler *event = sys_running_process->pcb.event_register;
    sys_process_event_handler *prev_event = event;
    while(event != 0){
        if(event->eventID == eventID && event->condition == function){
            void *data = event->buffered_data;

            if(prev_event == event){
                sys_running_process->pcb.event_register = event->next;
            }else{
                prev_event->next = event->next;
            }
            event->next = 0;
            Sys_Free(event);//deletes event handler but leaves the data (return value)

            Sys_End_AtomicSection();
            return data;
        }
        prev_event = event;
        event = event->next;
    }

    Sys_End_AtomicSection();
    return 0;
}
//-----------------------------------------------------------------------------
//	BugDlg_UploadFile
//
//-----------------------------------------------------------------------------
bool BugDlg_UploadFile( char const *pLocalName, char const *pRemoteName, bool bDeleteLocal )
{
	FILE	*fp;
	int		len;
	void	*pLocalData;

	ConsoleWindowPrintf( BUG_COLOR, "Uploading %s to %s\n", pLocalName, pRemoteName );

	len = Sys_LoadFile( pLocalName, &pLocalData );
	if ( !pLocalData || !len )
	{
		ConsoleWindowPrintf( XBX_CLR_RED, "UploadFile: Unable to open local path '%s'\n", pLocalName );
		return false;
	}

	Sys_CreatePath( pRemoteName );

	fp = fopen( pRemoteName, "wb" );
	if ( !fp )
	{
		ConsoleWindowPrintf( XBX_CLR_RED, "UploadFile: Unable to open remote path '%s'\n", pRemoteName );
		Sys_Free( pLocalData );
		return false;
	}

	fwrite( pLocalData, len, 1, fp );

	fclose( fp );
	Sys_Free( pLocalData );

	if ( bDeleteLocal )
	{
		unlink( pLocalName );
	}

	return true;
}
/**
 * 
 * This function executes all event handlers and processes stored event data. First it checks the list of occurred events and then it executes all event handlers of these events
 *
 */
inline void Sys_Execute_All_EventHandler(){
    sys_occurred_event *o_event;
    
    Sys_Start_AtomicSection();
    o_event = sys_occurred_events;
    sys_occurred_events = 0;
    
    while(o_event != 0){//assuming there are less processes then events
        
        Sys_Execute_Events_in_ProcessList(o_event->eventID, sys_ready_processes);
        Sys_Execute_Events_in_ProcessList(o_event->eventID, sys_blocked_processes);

        
        sys_occurred_event *occured_event = o_event;
        o_event = o_event->next;
        
        occured_event->next = 0;
        Sys_Free(occured_event);
    }
    
    Sys_End_AtomicSection();
}
/**  
 *
 * This function adds the event-data to the local list of the process (pid).
 *
 * @param[in] pid      process identifier
 * @param[in] eventID  event identifier
 * @param[in] data     memory that contains the value of the occurred event
 * @param[in] length   length of the data (bytes)
 */
void Sys_Add_Event_to_Process(uint pid, uint eventID, void *data, uint length){

    sys_pcb_list_element *element;
    
    Sys_Start_AtomicSection();
        element = Sys_Find_Process(pid);
        if(element == 0){//no process with pid
            Sys_End_AtomicSection();
            return;
        }

        bool add_event = true; 
        sys_occurred_event **o_event = &sys_occurred_events;
        while(*o_event != 0){//check if the event (eventID) already occurred
            if((*o_event)->eventID == eventID){//it already occurred
                add_event = false;
                break;
            }
            o_event = &((*o_event)->next);
        }
    
        if(add_event){//if it hasn't occurred 
            //add eventID to the list of occurred events
            (*o_event) = Sys_Malloc(sizeof(sys_occurred_event));
            if((*o_event) == 0){
                Sys_End_AtomicSection();
                return; //no memory left
            }

            (*o_event)->eventID = eventID;
            (*o_event)->next = 0;
        }

        //NOW add the data
        sys_process_event_handler *event = element->pcb.event_register;
        while( (event = Sys_Next_EventHandler(event, eventID)) != 0 ){
            //check if the condition was met to add the event data
            bool is_condition_met = false;
            if(event->condition != 0){
                is_condition_met = event->condition(data);
            }else{//no condition is always met
                is_condition_met = true;
            }

            if( is_condition_met ){
                sys_event_data *e_data = (sys_event_data*) Sys_Malloc(sizeof(sys_event_data));
                if(e_data == 0){//if malloc fails .. exit
                    Sys_End_AtomicSection();
                    return;
                }

                //create the struct that holds the data
                if(length != 0){//if there is data
                    e_data->value = Sys_Malloc(length);
                    if(e_data->value == 0){//if malloc fails .. exit
                        Sys_Free(e_data);
                        Sys_End_AtomicSection();
                        return;
                    }

                    Sys_Memcpy(data, e_data->value, length);
                }else{
                    e_data->value = 0;
                }
                e_data->size = length;
                e_data->next = 0;

                //add the struct to the end of the buffered_data
                if(event->buffered_data == 0){
                    event->buffered_data = e_data;
                }else{
                    sys_event_data *set_data = event->buffered_data;
                    while(set_data->next != 0){
                        set_data = set_data->next;
                    }
                    set_data->next = e_data;
                }

                event = event->next;
            }
        }
    Sys_End_AtomicSection();
}