#include "Utils.h"
#include "PBitmapModif.h"

Utils util;

Utils::Utils()
{ 
	show_progress_load = OFF;
	loading_in_progress =OFF;
	progress_exists = false;

	sel_length = 0 ;

}

// r,g,b values are from 0 to 1 
// h = [0,360], s = [0,1], v = [0,1] 
//              if s == 0, then h = -1 (undefined)

void Utils::RGBtoHSV( float r, float g, float b, float *h, float *s, float *v ) 
{ 
	float min, max, delta;
        
    min = MIN( r, g); 
    max = MAX( r, g);
    min = MIN(min,b);
    max = MAX(min,b);
        
    *v = max;	// v
    delta = max - min;
    if( max != 0 ) 
    	*s = delta / max;	// s 
    else
    { 
    	// r = g = b = 0	// s = 0, v is undefined 
        *s = 0; 
        *h = -1; 
        return; 
	}
    if( r == max ) 
    	*h = ( g - b ) / delta;			// between yellow & magenta 
    else
    	if( g == max ) 
        	*h = 2 + ( b - r ) / delta;		// between cyan & yellow 
        else 
        	*h = 4 + ( r - g ) / delta;		// between magenta & cyan
  	*h *= 60;                               // degrees 
    if( *h < 0 ) 
    	*h += 360;
}

void Utils::HSVtoRGB( float *r, float *g, float *b, float h, float s, float v ) 
{ 
        int i; 
        float f, p, q, t;
        if( s == 0 )
        { 
                // grayscale
                *r = *g = *b = v; 
                return; 
        }
        h /= 60;                        // sector 0 to 5 
        i = (int)floor( h ); 
        f = h - i;                      // factorial part of h 
        p = v * ( 1 - s ); 
        q = v * ( 1 - s * f ); 
        t = v * ( 1 - s * ( 1 - f ) );
        switch( i ) { 
                case 0: 
                        *r = v; 
                        *g = t; 
                        *b = p; 
                        break; 
                case 1: 
                        *r = q; 
                        *g = v; 
                        *b = p; 
                        break; 
                case 2: 
                        *r = p; 
                        *g = v; 
                        *b = t; 
                        break; 
                case 3: 
                        *r = p; 
                        *g = q; 
                        *b = v; 
                        break; 
                case 4: 
                        *r = t; 
                        *g = p; 
                        *b = v; 
                        break; 
                default:                // case 5: 
                        *r = v; 
                        *g = p; 
                        *b = q; 
                        break; 
        }
}

void Utils::RescaleBitmap(BBitmap *src, BBitmap *dest, bool center_if_smaller)
{
//Rescales src copying it into dest.
//if src is smaller than 

//WARNING DEST BBITMAP MUST ACCEPT BVIEWS!

//erase in gray 216
uint8 *bits = (uint8*) dest->Bits();
uint32 pos =0;   
uint32 lg = dest->BitsLength();
do { *bits=216; bits++; } while(pos++ != lg-1);


		BPicture *my_pict; 

		dest->Lock(); //important!
		BView *virtualView = new BView(dest->Bounds(), NULL, B_FOLLOW_NONE, 0 );
		dest->AddChild( virtualView );

		virtualView->BeginPicture(new BPicture); 
    	
    	BRect rect = dest->Bounds();
		rect.InsetBy(1,1);
		
		
		if (center_if_smaller==true) //center without rescaling
		{
		//hori
		if ( dest->Bounds().Width() > src->Bounds().Width() )  
			{ 
				rect.right = src->Bounds().right;
				rect.OffsetBy( (dest->Bounds().Width()-rect.right)/2,0); //Centrage
			}
			
		//vert
		if ( dest->Bounds().Height() > src->Bounds().Height() ) 
			{
				rect.bottom = src->Bounds().bottom;
				rect.OffsetBy( 0, (dest->Bounds().Height()-rect.bottom)/2); //Centrage
			}
		}
		
		virtualView->DrawBitmap(src, src->Bounds(), rect);
			 
    	my_pict = virtualView->EndPicture();
    
		virtualView->DrawPicture(my_pict);
	  	dest->RemoveChild(virtualView );
	  	dest->Unlock();
 
}



BBitmap *Utils::FetchBitmap(char *filename, uint8 mode)
{
	BFile file;
	
	if (file.SetTo(filename, B_READ_ONLY) == B_OK)
	{
		uint8 dither=false;
		if (mode == 8) dither=true;
		
		BTranslatorRoster *roster = BTranslatorRoster::Default();
		
		//FOR PROGRESSIVE
		ProgressiveBitmapStream stream(NULL, NULL, dither,false); //dither true
		
		char str[8192];
		if (show_progress_load==ON)
		{			
			sprintf(str, Language.get("LOADING"));
			strcat(str," ");
			strcat(str,filename);
			stream.DisplayProgressBar(str);
		 }
	
		roster->Translate(&file, 0, 0, &stream, B_TRANSLATOR_BITMAP);
		
		//FOR PROGRESSIVE
		stream.SetDispose(false);
		return stream.Bitmap();
	
/*
		BBitmapStream stream;
		BBitmap *result = NULL;
		if (roster->Translate(&file, NULL, NULL, &stream, B_TRANSLATOR_BITMAP) < B_OK) return NULL;
		stream.DetachBitmap(&result);
		return result; 
*/
	}
	else return NULL;
}


status_t Utils::StoreTranslatorBitmap(BBitmap *bitmap, char *filename, uint32 type) 
{ 
	BAlert *alert;
	BTranslatorRoster *roster = BTranslatorRoster::Default(); 
	BBitmapStream stream(bitmap); // init with contents of bitmap 
	BFile file(filename, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);

	switch ( roster->Translate(&stream, NULL, NULL, &file, type) ) 
	{
		case B_NO_TRANSLATOR : 
	   		alert = new BAlert("","No translator.", "Pfff..."); 
	   		alert->Go(NULL);
	   		return B_ERROR;
			break;
   				
		case B_NOT_INITIALIZED : 
			alert = new BAlert("","Not initalized.", "Pfff..."); 
			alert->Go(NULL);
			return B_ERROR;
			break;
   				
		case B_BAD_VALUE : 
			alert = new BAlert("","Bad value, in or out source is NULL.", "Pfff..."); 
			alert->Go(NULL);
			return B_ERROR;
			break;
   				
		case B_NO_ERROR :
			return B_OK;	
   //		alert = new BAlert("","Saved. No problem.", "Yep!"); 
	   	//	alert->Go(NULL);
			break;
	}
	return B_ERROR; //by default
}

void Utils::PurgeClipboard(void)
{
BMessage *clip = (BMessage *)NULL;
	if (be_clipboard->Lock())
	{
		if ((clip = be_clipboard->Data()))
		{
			be_clipboard->Clear();
			clip->AddData("text/plain", B_MIME_TYPE, " ", 1);
			be_clipboard->Commit();
		}
		be_clipboard->Unlock();
	}
}

//puts file application in dossier_app AND the VERSION into version_txt
void Utils::AppDir()
{
	app_info ai; 
	BAppFileInfo afi; 
	BFile file; 
	
	app_info info;
	BPath path;
	be_app->GetAppInfo(&info);
	BEntry tmp_entry(&info.ref);
	BEntry file_entry;
	tmp_entry.GetParent(&file_entry);
	file_entry.GetPath(&path); 
	sprintf(dossier_app,path.Path());
	
	file.SetTo(&info.ref, B_READ_WRITE); 
	afi.SetTo(&file);
	version_info v_info;
	afi.GetVersionInfo(&v_info, B_APP_VERSION_KIND);
	
	char tmp[255];
	// Get name from short_info in rsrc file
	sprintf(version_txt,v_info.short_info);
	sprintf(tmp,"V%ld.%ld%ld",v_info.major,v_info.middle,v_info.minor);
	sprintf(version_only_txt,tmp);
	switch(v_info.variety)
	{
  		case 1: strcat(tmp," ALPHA"); break;
  		case 2: strcat(tmp," BETA"); break;
	}
	strcat(version_txt," ");
	strcat(version_txt,tmp);
}


void Utils::NotImplemented()
{
	BAlert *alert = new BAlert("",Language.get("NOT_YET"),"Oompf!");  alert->Go(NULL);
	mainWin->Activate();
}


BRect Utils::CheckRectangle(BRect ori_rect)
{
	//verify usability of a Rect
	
	BRect modified = ori_rect;
	
	ori_rect.left=0;
	ori_rect.top=0;
	if (ori_rect.left > -1) modified.left = floor(ori_rect.left);
	if (ori_rect.top  > -1) modified.top  = floor(ori_rect.top);
	modified.right =  int32(floor(ori_rect.right)); 
	modified.bottom = int32(floor(ori_rect.bottom)); 
	
	// VERY important that UL corner has smaller values than BR corner
	float tmp;
	if (modified.top  > modified.bottom) 
		{ tmp = modified.top;  modified.top = modified.bottom; modified.bottom = tmp; }
	if (modified.left > modified.right)  
		{ tmp = modified.left; modified.left = modified.right; modified.right = tmp; }
	
	return modified;
}

void Utils::CopyRect(BRect zone_src, BBitmap *la_source, 
					 BRect zone_dest, BBitmap *la_dest, uint8 nb_of_bytes)
{
	// First, clip source rect to destination
	if(zone_src.Width() > zone_dest.Width())
	{	zone_src.right=zone_src.left+zone_dest.Width();
	}

	if(zone_src.Height() > zone_dest.Height())
	{	zone_src.bottom=zone_src.top+zone_dest.Height();
	}

	// Second, check rectangle bounds against their own bitmaps
	BRect work_rect;

	work_rect.Set(	la_source->Bounds().left,
					la_source->Bounds().top,
					la_source->Bounds().right,
					la_source->Bounds().bottom	);
	if( !(work_rect.Contains(zone_src)) )
	{	// something in selection must be clipped
		if(zone_src.left < 0)
			zone_src.left = 0;
		if(zone_src.right > work_rect.right)
			zone_src.right = work_rect.right;
		if(zone_src.top < 0)
			zone_src.top = 0;
		if(zone_src.bottom > work_rect.bottom)
			zone_src.bottom = work_rect.bottom;
	}

	work_rect.Set(	la_dest->Bounds().left,
					la_dest->Bounds().top,
					la_dest->Bounds().right,
					la_dest->Bounds().bottom	);
	if( !(work_rect.Contains(zone_dest)) )
	{	// something in selection must be clipped
		if(zone_dest.left < 0)
			zone_dest.left = 0;
		if(zone_dest.right > work_rect.right)
			zone_dest.right = work_rect.right;
		if(zone_dest.top < 0)
			zone_dest.top = 0;
		if(zone_dest.bottom > work_rect.bottom)
			zone_dest.bottom = work_rect.bottom;
	}

	// Set pointers to the actual data
	uint8 *src_bits  = (uint8*) la_source->Bits();	
	uint8 *dest_bits = (uint8*) la_dest->Bits();	

	// Get row widths for offset looping
	uint32 src_width  = uint32 (la_source->BytesPerRow());
	uint32 dest_width = uint32 (la_dest->BytesPerRow());

	// Offset bitmap pointers to proper spot in each bitmap
	src_bits += uint32 ( (zone_src.top  * src_width)  + (zone_src.left  * nb_of_bytes) );
	dest_bits += uint32 ( (zone_dest.top * dest_width) + (zone_dest.left * nb_of_bytes) );

	uint32 line_length = uint32 ((zone_dest.right - zone_dest.left)*nb_of_bytes);
//	uint32 lines = uint32 (zone_dest.bottom-zone_dest.top+1+1);
	uint32 lines = uint32 (zone_dest.bottom-zone_dest.top+1);
		
	for (uint32 pos_y = 0; pos_y != lines; pos_y++)
	{ 
		memcpy(dest_bits,src_bits,line_length);

		// Increment offsets
   		src_bits += src_width;
   		dest_bits += dest_width;
	}

}

void Utils::CopyRectWithMask(BRect zone_src,  BBitmap *la_source, 
					 BRect zone_dest, BBitmap *la_dest, BBitmap *le_mask,
					 uint8 nb_of_bytes)
{	// Copies a bitmap in such a way that a RGBA_32 bitmap is copied with
	// the color bits sent to the image and the alpha channel is sent
	// to the selection mask. 

	// set work pointers to respective bitmaps
	uint8 *src_bits  = (uint8*) la_source->Bits();	
	uint8 *dest_bits = (uint8*) la_dest->Bits();	
	uint8 *t_msk_bits  = (uint8*) le_mask->Bits();	

	// Get width, in pixels, of each bitmap
	uint32 src_x  = uint32 (la_source->Bounds().Width()+1);
	uint32 dest_x = uint32 (la_dest->Bounds().Width()+1);

	// calculate offsets in each bitmap
    uint32 src_pos_bits  = uint32 (((zone_src.top  * src_x)  + zone_src.left)  * nb_of_bytes); //pixel de départ
    uint32 dest_pos_bits = uint32 (((zone_dest.top * dest_x) + zone_dest.left) * nb_of_bytes); //pixel de départ
    uint32 mask_pos_bits = uint32 (((zone_dest.top * dest_x) + zone_dest.left)); //pixel mask comme la dest

	// jump to offset
	src_bits      += src_pos_bits;	
	dest_bits     += dest_pos_bits;	
	t_msk_bits      += mask_pos_bits;	

	// length, in bytes, of each line(?)
	uint32 src_length  = uint32 ((src_x -  (zone_src.right - zone_src.left+1))   * nb_of_bytes);
	uint32 dest_length = uint32 ((dest_x - (zone_dest.right - zone_dest.left+1)) * nb_of_bytes); 
	uint32 mask_length = uint32 ((dest_x - (zone_dest.right - zone_dest.left+1))); 

	// index vars
	uint32 pos_x, pos_y;
	pos_x = 0;
	pos_y = 0;

	// dimensions of bitmaps in pixels
	uint32 line_size = uint32 ((zone_dest.right - zone_dest.left+1));
	uint32 lines     = uint32 (zone_dest.bottom-zone_dest.top+1);
	uint8 transp;
		
	while (pos_y != lines) 
	{ 
		while (pos_x != line_size)
		{
			// Get the alpha channel and convert from a 0-255 to a %
			transp = TheTables.tab_char_to_percent[*t_msk_bits];
			
			// Copy rgb triple, compensating for transparency
			*dest_bits  =  TheTables.tab_addition[TheTables.tab_normal[transp][*src_bits]] [TheTables.tab_pourcent_x_val[transp][*dest_bits]];
			dest_bits++; src_bits++; 
			*dest_bits  =  TheTables.tab_addition[TheTables.tab_normal[transp][*src_bits]] [TheTables.tab_pourcent_x_val[transp][*dest_bits]];
			dest_bits++; src_bits++; 
			*dest_bits  =  TheTables.tab_addition[TheTables.tab_normal[transp][*src_bits]] [TheTables.tab_pourcent_x_val[transp][*dest_bits]];
			
			// Advance to next pixel. bitmap pointers are advanced 2 to skip alpha
			pos_x++;
			t_msk_bits++;
			dest_bits+=2;
			src_bits+=2; 
		}
		
   		pos_y++;     
   		src_bits  += src_length;  
   		dest_bits += dest_length;
		t_msk_bits += mask_length;
		pos_x = 0;
	}
}

BBitmap* Utils::GrabRect(BRect zone_src,  BBitmap *la_source) 
{
	//zone_src.right-=1;
	//zone_src.bottom-=1;
	BBitmap* la_dest = new BBitmap(zone_src,B_RGB32);
	
	CopyRect(zone_src,la_source,BRect(0,0, zone_src.right-zone_src.left,
				 zone_src.bottom-zone_src.top),la_dest, 4);
	return la_dest;
}


BRect Utils::find_win_pos(float width, float height, BWindow *win_to_use_to_choose_screen)
{
	// Pick a good spot on screen
	BRect sr = BScreen(win_to_use_to_choose_screen).Frame();
	
	BRect r;
	
	r.left = ((sr.Width()-width)/2);
	r.top = ((sr.Height()-height)/2);
	r.right = r.left + width-1;
	r.bottom = r.top + height-1;
	
	
	return r;
}

BRect Utils::find_win_pos_on_main_screen(float width, float height)
{
	// special version without window in parameters
	BRect sr = BScreen(B_MAIN_SCREEN_ID).Frame();
	
	BRect r;
	
	r.left = ((sr.Width()-width)/2);
	r.top = ((sr.Height()-height)/2);
	r.right = r.left + width-1;
	r.bottom = r.top + height-1;

	return r;
}

BBitmap* Utils::load_bmp(const char* le_nom)
{ 
	BAlert* alert;	 
	char str[512];
	
	BFile fich;
// 	if (fich.SetTo(le_nom,B_READ_ONLY)!=B_NO_ERROR)
  	if (fich.SetTo(le_nom,B_READ_ONLY)!=B_OK)
	{ 
		sprintf(str,Language.get("COULD_NOT_LOAD"));
		strcat(str,"\n");
		strcat(str,le_nom);
		alert = new BAlert("",str,"Doh"); 
		alert->Go(NULL);
		return NULL;
	}
  	else 
	{  	return FetchBitmap((char*)le_nom,32);
	}
  	
} 

float Utils::ConvertUnits(float val, int32 src_unit,int32 dest_unit, float resolution, float res_units)
{
	float divider =0;
	if (res_units==DPI)
		divider=2.54; 		//dots per inch (dpi)
	else
		divider = 1;		//dots per centimeterd
	
	float inch_divider;
	if (divider ==1)
		inch_divider=2.54;
	else
		inch_divider=1;
	
	switch(src_unit)
	{
				
		case UNIT_CM:
			switch(dest_unit)
			{
				case UNIT_CM:			 				break;
				case UNIT_MM:   	val *= 10;	 		break;
				case UNIT_INCH: 	val /= 2.54;		break;
				case UNIT_PIXELS: 	val  *= (resolution/divider); break;
			}
			break;
					
					
		case UNIT_MM:
			switch(dest_unit)
			{
				case UNIT_CM:		val /= 10;			break;
				case UNIT_MM:   						break;
				case UNIT_INCH: 	val /= (2.54*10);	break;
				case UNIT_PIXELS: 	val  *= (resolution/divider)/10;	break;
			}
			break;
					
		case UNIT_INCH:
			switch(dest_unit)
			{
				case UNIT_CM:		val *= 2.54;			break; 
				case UNIT_MM:   	val *= (2.54*10);		break;
				case UNIT_INCH: 	break;
				case UNIT_PIXELS: 	val  *= (resolution*inch_divider);	break;
			}
			break;
					
		case UNIT_PIXELS:
			switch(dest_unit)
			{
				case UNIT_CM: 		val  /= (resolution/divider);		break;
										
				case UNIT_MM:   	val  /= (resolution/divider)/10;	break; 
									
				case UNIT_INCH: 	val  /= (resolution*inch_divider);	break; 
								
				case UNIT_PIXELS: 	break;
			}
			break;
												
										
	}
		
	return val;
}	
