//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. =========
//
// The copyright to the contents herein is the property of Charles G. Cleveland.
// The contents may be used and/or copied only with the written permission of
// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose: 
//
// $Workfile: UIUtil.cpp $
// $Date: 2002/05/23 04:03:05 $
//
//-------------------------------------------------------------------------------
// $Log: UIUtil.cpp,v $
// Revision 1.8  2002/05/23 04:03:05  Flayra
// - Post-crash checkin.  Restored @Backup from around 4/16.  Contains changes for last four weeks of development.
//
//===============================================================================
#include "ui/UIUtil.h"
#include "ui/UITags.h"
#include "VGUI_Label.h"
#include "cl_dll/hud.h"
#include "cl_dll/cl_util.h"

void UIDrawVariableBarSpriteHoles(int inSprite, int inX, int inY, float inPercentage, float inGammaSlope, bool inTrueHolesFalseAdditive, float inSecondaryPercentage)
{
	// Assumes that frame 0 is the empty sprite, frame 1 is full sprite
	const int kEmptyFrame = 0;
	const int kFullFrame = 1;

	int theSpriteWidth = SPR_Width(inSprite, kFullFrame);
	int theSpriteHeight = SPR_Height(inSprite, kFullFrame);

	int theColorComponent = 255/inGammaSlope;
	
	// Draw empty sprite
	SPR_Set(inSprite, theColorComponent, theColorComponent, theColorComponent);
	if(inTrueHolesFalseAdditive)
	{
		SPR_DrawHoles(kEmptyFrame, inX, inY, NULL);
	}
	else
	{
		SPR_DrawAdditive(kEmptyFrame, inX, inY, NULL);
	}

	// Draw secondary level if specified, at half brightness
	int theFilledHeight = theSpriteHeight*inPercentage;
	if(inSecondaryPercentage != -1)
	{
		theFilledHeight = theSpriteHeight*inSecondaryPercentage;
		int theSecondaryColorComponent = .5f*theColorComponent;

		SPR_EnableScissor(inX, inY + (theSpriteHeight - theFilledHeight), theSpriteWidth, theFilledHeight);
		
		SPR_Set(inSprite, theSecondaryColorComponent, theSecondaryColorComponent, theSecondaryColorComponent);
		if(inTrueHolesFalseAdditive)
		{
			SPR_DrawHoles(kFullFrame, inX, inY, NULL);
		}
		else
		{
			SPR_DrawAdditive(kFullFrame, inX, inY, NULL);
		}
		
		SPR_DisableScissor();
	}

	theFilledHeight = theSpriteHeight*inPercentage;
	
	// Draw partially full sprite
	// Enable scissor so it's not all drawn
	SPR_EnableScissor(inX, inY + (theSpriteHeight - theFilledHeight), theSpriteWidth, theFilledHeight);

	SPR_Set(inSprite, theColorComponent, theColorComponent, theColorComponent);
	if(inTrueHolesFalseAdditive)
	{
		SPR_DrawHoles(kFullFrame, inX, inY, NULL);
	}
	else
	{
		SPR_DrawAdditive(kFullFrame, inX, inY, NULL);
	}
	
	SPR_DisableScissor();
}

void UIGetPosition(const TRDescription& inDesc, float& outXPos, float& outYPos, int& outWidth, int& outHeight)
{
    outXPos = UIDefaultXPos;
    outYPos = UIDefaultYPos;
    outWidth = UIDefaultWidth;
    outHeight = UIDefaultHeight;

    // Get normalized xpos if specified.  Don't care if they aren't specified, it will just fail and not change the default.
    inDesc.GetTagValue(UITagXPos, outXPos);
    inDesc.GetTagValue(UITagYPos, outYPos);
    inDesc.GetTagValue(UITagWidth, outWidth);
    inDesc.GetTagValue(UITagHeight, outHeight);
}

void UIStringToAlignment(const string& inString, int outAlignment)
{
    if(inString == "northwest")
    {
        outAlignment = vgui::Label::a_northwest;
    } 
    else if(inString == "north")
    {
        outAlignment = vgui::Label::a_north;
    }
    else if(inString == "northeast")
    {
        outAlignment = vgui::Label::a_northeast;
    }
    else if(inString == "west")
    {
        outAlignment = vgui::Label::a_west;
    }
    else if(inString == "center")
    {
        outAlignment = vgui::Label::a_center;
    }
    else if(inString == "east")
    {
        outAlignment = vgui::Label::a_east;
    }
    else if(inString == "southwest")
    {
        outAlignment = vgui::Label::a_southwest;
    }
    else if(inString == "south")
    {
        outAlignment = vgui::Label::a_southeast;
    }
    else 
    {
        // Default is centered
        outAlignment = UIDefaultAlignment;
    }
}

void UIStringToColor(const string& inString, vgui::Color& outColor)
{
    // Set default rgba in case the string is totally bogus (default is opaque white)
    int r = 255;
    int g = 255;
    int b = 255;
    int a = 255;
    int theColor = 0;
	
    // Get rgba values from inString
    if(sscanf(inString.c_str(), "%x", &theColor) == 1)
    {
        r = (theColor >> 24) & 0xFF;
        g = (theColor >> 16) & 0xFF;
        b = (theColor >> 8) & 0xFF;
        a = (theColor >> 0) & 0xFF;
    }
	
    // Set it to the read in values or the default if we couldn't read it
    outColor.setColor(r, g, b, a);
}

char UIKeyCodeToChar(vgui::KeyCode inKeyCode)
{
	static char theLookup[57] = {
		'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
		'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
		'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
		'\\', '*', '-', '+', 
		'\n', '.', '[', ']', 
		';', '\'', '\\',
	};

	int theOffset = (int)(inKeyCode);
	ASSERT(theOffset >= 0);
	ASSERT(theOffset < 57);

	char theReturnChar = theLookup[theOffset];
	return theReturnChar;
}

string UINameToSprite(const string& inName, int inScreenWidth, bool inNoRes)
{
	string theSpriteName;

	// Add 640/320 also
	string thePrefix;
	if(!inNoRes)
	{
		thePrefix = "640";
		if(inScreenWidth < 640)
		{
			thePrefix = "320";
		}
	}
	
	theSpriteName = "sprites/" + thePrefix;
	theSpriteName += inName;
	theSpriteName += ".spr";

	return theSpriteName;
}

vgui::Color UIBlendColor(const vgui::Color& inBaseColor, const vgui::Color& inDestColor, float inParametricPercentage)
{
	ASSERT(inParametricPercentage >= 0.0f);
	ASSERT(inParametricPercentage <= 1.0f);

	vgui::Color theBaseColor = inBaseColor;
	vgui::Color theDestColor = inDestColor;

	int theBaseR, theBaseG, theBaseB, theBaseA;
	theBaseColor.getColor(theBaseR, theBaseG, theBaseB, theBaseA);

	int theDestR, theDestG, theDestB, theDestA;
	theDestColor.getColor(theDestR, theDestG, theDestB, theDestA);
	
	int theOutR = theBaseR + (theDestR - theBaseR)*inParametricPercentage;
	int theOutG = theBaseG + (theDestG - theBaseG)*inParametricPercentage;
	int theOutB = theBaseB + (theDestB - theBaseB)*inParametricPercentage;
	int theOutA = theBaseA + (theDestA - theBaseA)*inParametricPercentage;
	
	vgui::Color theBlendedColor;
	theBlendedColor.setColor(theOutR, theOutG, theOutB, theOutA);

	return theBlendedColor;
}

int vguiRound(float inFloat)
{
    return (int)(inFloat + .5f);
}

int vguiAbs(int inNum)
{
    return (inNum > -inNum ? inNum : -inNum);
}

void vguiSimpleLine(int x0, int y0, int x1, int y1, int r, int g, int b, int a)
{
    //public void lineImproved(int x0, int y0, int x1, int y1, Color color)
    //{
        //int pix = color.getRGB();
        int dx = x1 - x0;
        int dy = y1 - y0;

        //raster.setPixel(pix, x0, y0);
        FillRGBA(x0, y0, 1, 1, r, g, b, a);
        //if (Math.abs(dx) > Math.abs(dy)) {          // slope < 1
        if(vguiAbs(dx) > vguiAbs(dy))
        {
            float m = (float) dy / (float) dx;      // compute slope
            float b = y0 - m*x0;
            dx = (dx < 0) ? -1 : 1;
            while (x0 != x1) 
            {
                x0 += dx;
                //raster.setPixel(pix, x0, Math.round(m*x0 + b));
                FillRGBA(x0, vguiRound(m*x0 + b), 1, 1, r, g, b, a);
            }
        } 
		else if (dy != 0) 
        {                              // slope >= 1
            float m = (float) dx / (float) dy;      // compute slope
            float b = x0 - m*y0;
            dy = (dy < 0) ? -1 : 1;
            while (y0 != y1) 
            {
                y0 += dy;
                //raster.setPixel(pix, Math.round(m*y0 + b), y0);
                FillRGBA(vguiRound(m*y0 + b), y0, 1, 1, r, g, b, a);
            }
        }
    //}
}

void vguiSimpleBox(int x0, int y0, int x1, int y1, int r, int g, int b, int a)
{
	// Draw lines around edges of box, don't duplicate corner pixels though, looks weird where additive

	// Top
	vguiSimpleLine(x0, y0, x1, y0, r, g, b, a);
	
	// Left
	vguiSimpleLine(x0, y0+1, x0, y1, r, g, b, a);
	
	// Right
	vguiSimpleLine(x1, y0+1, x1, y1, r, g, b, a);
	
	// Bottom
	vguiSimpleLine(x0+1, y1, x1, y1, r, g, b, a);
}

//int trunc(float inX)
//{
//    return 0;
//	//return integer part of x
//}
//
//float frac(float inX)
//{
//    return 0.0f;
//	//return fractional part of x
//}
// 
//float invfrac(inX)
//{
//    return 0.0f;
//	//return 1 - (fractional part of x)
//}
//
//
//void vguiWuLine(int x1, int y1, int x2, int y2)
//{
//
//        variable declerations:
//            int variables:
//		grad, xd, yd, length,xm,ym
//		xgap, ygap, xend, yend, xf, yf
//		brigheness1, brigheness2
//
//	    integer variables:
//		x, y, ix1, ix2, iy1, iy2
//
//	    byte variables:
//		c1,c2
//
//	code starts here:
//
//	    Width and Height of the line
//	    xd = (x2-x1)
//	    yd = (y2-y1)
//	
//	   
//	    if abs(xd) > abs(yd) then			check line gradient
//	        horizontal(ish) lines
//
//
//		if x1 > x2 then				if line is back to front
//		    swap x1 and x2			then swap it round
//		    swap y1 and y2
//	            xd = (x2-x1)				and recalc xd & yd
//	            yd = (y2-y1)
//		end if
//
//	 	grad = yd/xd                             gradient of the line
//		
//
//		End Point 1
//		-----------
//
//		xend = trunc(x1+.5)                      find nearest integer X-coordinate
//		yend = y1 + grad*(xend-x1)               and corresponding Y value
//		
//		xgap = invfrac(x1+.5)                    distance i
//		
//		ix1  = int(xend)                         calc screen coordinates
//		iy1  = int(yend)
//	
//		brightness1 = invfrac(yend) * xgap       calc the intensity of the other 
//		brightness2 =    frac(yend) * xgap       end point pixel pair.
//		
//		c1 = byte(brightness1 * MaxPixelValue)	 calc pixel values
//		c2 = byte(brightness2 * MaxPixelValue)	
//
//		DrawPixel(ix1,iy1), c1			 draw the pair of pixels
//		DrawPixel(ix1,iy1+1), c2
//
//		yf = yend+grad                           calc first Y-intersection for
//                                                         main loop
//
//		End Point 2
//		-----------
//
//		xend = trunc(x2+.5)                      find nearest integer X-coordinate
//		yend = y2 + grad*(xend-x2)               and corresponding Y value
//		
//		xgap = invfrac(x2-.5)                    distance i
//		
//		ix2  = int(xend)                         calc screen coordinates
//		iy2  = int(yend)
//	
//		brightness1 = invfrac(yend) * xgap       calc the intensity of the first 
//		brightness2 =    frac(yend) * xgap       end point pixel pair.
//		
//		c1 = byte(brightness1 * MaxPixelValue)	calc pixel values
//		c2 = byte(brightness2 * MaxPixelValue)	
//
//		DrawPixel(ix2,iy2), c1			draw the pair of pixels
//		DrawPixel(ix2,iy2+1), c2
//
//
//
//		MAIN LOOP
//		---------
//
//		Loop x from (ix1+1) to (ix2-1)			main loop
//
//		    brightness1 = invfrac(yf)		        calc pixel brightnesses
//		    brightness2 =    frac(yf)
//
//  		    c1 = byte(brightness1 * MaxPixelValue)	calc pixel values
//		    c2 = byte(brightness2 * MaxPixelValue)	
//
//		    DrawPixel(x,int(yf)), c1			draw the pair of pixels
// 		    DrawPixel(x,int(yf)+1), c2
//
//	            yf = yf + grad				update the y-coordinate
//
//		end of x loop					end of loop
//
//	    else
//		vertical(ish) lines
//
//		handle the vertical(ish) lines in the
//		same way as the horizontal(ish) ones
//		but swap the roles of X and Y
//	    end if
//// end of procedure
//}


//void vguiDrawLine(int x0, int y0, int x1, int y1, vgui::Color inDrawColor) {
//    int dx, dy, temp;
//    
//    // First clip input points to ICanvas
//    if(x0 < 0) {
//        x0 = 0;
//    }
//    if(x1 < 0) {
//        x1 = 0;
//    }
//    if(x0 >= width) {
//        x0 = width - 1;
//    }
//    if(x1 >= width) {
//        x1 = width - 1;
//    }
//    if(y0 < 0) {
//        y0 = 0;
//    }
//    if(y1 < 0) {
//        y1 = 0;
//    }
//    if(y0 >= height) {
//        y0 = height - 1;
//    }
//    if(y1 >= height) {
//        y1 = height - 1;
//    }
//    
//    dx = x1-x0;
//    dy = y1-y0;
//    
//    if(y0 == y1) { // Special case horizontal lines!
//        if(dx < 0) {
//            dx = -dx; // Change to dx = -dx;
//            x0 = x1;
//        }
//        // Call asm_drawHorizontalLine
//        drawHorizontalLine(x0, y0, dx, inDrawColor);
//        return;
//    }
//    
//    if(x0 == x1) {
//        if(dy < 0) {
//            dy = -dy;
//            y0 = y1;
//        }
//        // Call asm_drawVerticalLine
//        drawVerticalLine(x0, y0, dy, inDrawColor);
//        return;
//    }
//    
//    bufferDest = (unsigned long)(buffer + y0*width + x0);
//    if(fabs(dx) > fabs(dy)) {
//        // We're going to call octant0
//        oldYFract = (float)((float)(y1 - y0)/(float)fabs(x1 - x0));
//        bufferWidth = width;
//        if(oldYFract < 0) {
//            oldYFract = -oldYFract; // Probably not needed cause sign bit shifted out
//            bufferWidth = -width;
//        }
//        oldYFract++;
//        if(x0 < x1) {
//            drawDirection = 1;
//        } else {
//            drawDirection = -1;
//        }
//        if(fabs(dx) != 0) {
//            octant0(fabs(dx));
//        }
//    } else {
//        // We're going to call octant1
//        oldXFract = (float)((float)(x1 - x0)/(float)fabs((y1 - y0)));
//        oldXFract++;
//        drawDirection = 1;
//        if(oldXFract < 0) {
//            oldXFract = -oldXFract;
//            drawDirection = -1;
//        }
//        if(y0 < y1) {
//            bufferWidth = width;
//        } else {
//            bufferWidth = -width;
//        }
//        if(fabs(dy) != 0) {
//            octant1(fabs(dy));
//        }
//    }
//}

//void vguiOctant0(int inBufferDest, int inDeltaX, vgui::Color inDrawColor)
//{
//  asm_octant0_    PROC    NEAR
//    ; Set up destination
//    mov edi,[_bufferDest]
//    mov bl,[_drawColor]


//
//    ; Set up fractional error
//    mov eax,[_oldYFract]
//    shl eax,9
//    xor ax,ax
//    mov [_yFract],eax
//;    xor eax,eax
//    xor edx,edx
//
//NOTDONEOCTANT0:
//    mov [edi],bl
//    ;inc edi
//    add edi,[_drawDirection]
//
//    ; Now just figure out whether we go to the next line or not
//    add edx,[_yFract]
//    jnc DONTADDOCTANT0
//    add edi,[_bufferWidth]
//DONTADDOCTANT0:
//    sub ecx,1
//    jnz NOTDONEOCTANT0
//    ret
//}