//THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 
//ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 
// PARTICULAR PURPOSE.
//
// Copyright © 1993, 1994  Microsoft Corporation.  All Rights Reserved.
//
//  MODULE:     Display.c
//
//  PURPOSE:    Output-related routines
//
//  PLATFORMS:  Chicago, NT, Win32s
//
//  FUNCTIONS:  
//      DrawAscii() - called by CharSet window to display the text
//      FindChar() - return the character that was drawn at the provided location
//      CircleBlt() - essentially, a StretchBlt, but draws cicular pels.
//      Radian() - convert from degrees to radians
//      CalcRot() - calculates new position after rotating by the given angle
//      DrawGlyph() - create bitmap of the given glyph and display it
//
//  SPECIAL INSTRUCTIONS: N/A
//

#include <windows.h>
#include <string.h>
#include <math.h>

#if defined (_WIN32)
#define _MoveTo(hdc,x,y) MoveToEx(hdc,x,y,NULL)
#else
#define _MoveTo(hdc,x,y) MoveTo(hdc,x,y)
#endif

// These are the 'exported' functions from this file:
void DrawAscii (HDC hdc, RECT *pRect, WORD direction);
void DrawGlyph (HDC hdc, RECT *pRect, BYTE glyph, HFONT hfont);
BYTE FindChar (HDC hdc, RECT *pRect, int x, int y);

#pragma warning(4 : 4309)
// A data structure that DrawAscii will use:
char ascii[]=
    {
    // Low Ascii:

    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
    20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,

    // Standard Ascii:

    ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',',
    '-', '.', '/', '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', '[', '\\', ']', '^', '_', '`',
    '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',
    '{', '|', '}', '~', '',

    // High Ascii:

    '\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207',
    '\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217',
    '\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227',
    '\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237',
    '\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247',
    '\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257',
    '\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267',
    '\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277',

    '\300', '\301', '\302', '\303', '\304', '\305', '\306', '\307',
    '\310', '\311', '\312', '\313', '\314', '\315', '\316', '\317',
    '\320', '\321', '\322', '\323', '\324', '\325', '\326', '\327',
    '\330', '\331', '\332', '\333', '\334', '\335', '\336', '\337',
    '\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
    '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
    '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
    '\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377',


    // End of the list:

    000, 000};
#pragma warning(3 : 4309)


//
//  FUNCTION: DrawAscii()
//
//  PURPOSE: A function that the CharSet window will use to display the text
//

void DrawAscii(HDC hdc, RECT *pRect, WORD direction)
{
    BYTE *pch;
    int h, w, incx, incy, width;
    unsigned int  wDisplay;
    POINT pt;
    TEXTMETRIC tm;
    BOOL bLineMode = FALSE;

    GetTextMetrics (hdc, &tm);
    h = tm.tmHeight;
    w = tm.tmMaxCharWidth;

    incx = 0;
    incy = h;
    pt.x = 0;
    pt.y = 0;

    pt.y = tm.tmAscent + tm.tmExternalLeading;

    pch = &(ascii[tm.tmFirstChar]);
    wDisplay = pRect->right -pRect->left;

    SetTextAlign (hdc, TA_BASELINE | TA_CENTER);
    SetBkMode (hdc, TRANSPARENT);

    while (pch[0] && (pch[0]<=tm.tmLastChar)) {
#if defined (WIN16)
        width = LOWORD (GetTextExtent (hdc, pch, 1));
#else
        SIZE size;
        GetTextExtentPoint (hdc, pch, 1, &size);
        width = size.cx;
#endif
        if ((unsigned)(pt.x + width) > wDisplay) {
            pt.x = 0;
            pt.y += incy;
        }
        TextOut (hdc, pt.x + (width/2), pt.y, pch, 1);
        pt.x += width;
        pch++;
    }
    return;

    direction;
}


//
//  FUNCTION: FindChar()
//
//  PURPOSE: Return the character that was drawn at the provided location
//           This allows us to 'click' on a character, and determined what
//           it was we actually clicked on.
//

BYTE FindChar (HDC hdc, RECT *pRect, int x, int y)
{
    BYTE *pch;
    int h, w, incx, incy, width;
    unsigned int wDisplay;
    POINT pt;
    TEXTMETRIC tm;
    BOOL bLineMode = FALSE;


    GetTextMetrics (hdc, &tm);
    h = tm.tmHeight;
    w = tm.tmMaxCharWidth;

    incx = 0;
    incy = h;
    pt.x = 0;
    pt.y = 0;

    pch = &(ascii[tm.tmFirstChar]);
    wDisplay = pRect->right -pRect->left;

    while (pch[0] && (pch[0]<=tm.tmLastChar)) {
#if defined (_WIN32)
        SIZE size;
        GetTextExtentPoint (hdc, pch, 1, &size);
        width = size.cx;
#else
        width = LOWORD (GetTextExtent (hdc, pch, 1));
#endif
        if ((unsigned)(pt.x + width) > wDisplay) {
            pt.x = 0;
            pt.y += incy;
        }
        if ((x>=pt.x) && (x <=(pt.x+width)) && (y>=pt.y) && (y<=pt.y+incy))
            return pch[0];
        pt.x += width;
        pch++;
    }
    return 0;
}


//
//  FUNCTION: CircleBlt()
//
//  PURPOSE: Essentially, a StretchBlt, but draws cicular pels.
//

BOOL CircleBlt (HDC hdcDest, int xDst, int yDst, int iWidthDst, int iHeightDst, HDC hdcSrc, int xSrc, int ySrc, int iWidthSrc, int iHeightSrc, DWORD dwROP)
{
    int x, y, x1, y1, x2, y2;
    DWORD rgb;
    float fXUnit, fYUnit;
    HBRUSH hbrush, hbrushOld;

    if (iWidthSrc != 0)
        fXUnit = (float)iWidthDst / (float)iWidthSrc;
    else
        fXUnit = (float)iWidthDst;

    if (iHeightSrc != 0)
        fYUnit = (float)iHeightDst / (float)iHeightSrc;
    else
        fYUnit = (float)iHeightDst;

    for (y=ySrc; y<(ySrc+iHeightSrc); y++) {
        for (x=xSrc; x<(xSrc+iWidthSrc); x++) {
            rgb = GetPixel (hdcSrc, x, y);
            if (rgb != -1 && rgb != 0x00FFFFFF) {
                hbrush = CreateSolidBrush (rgb);
                hbrushOld = SelectObject (hdcDest, hbrush);
                x1 = xDst + (int)((x-xSrc)*fXUnit);
                y1 = yDst + (int)((y-ySrc)*fYUnit);
                x2 = xDst + (int)(((x-xSrc)+1)*fXUnit)+1;
                y2 = yDst + (int)(((y-ySrc)+1)*fYUnit)+1;
                Ellipse (hdcDest, x1, y1, x2, y2);
                DeleteObject(SelectObject(hdcDest, hbrushOld));
            }
        }
    }

    return TRUE;

    dwROP;
}


//
//  FUNCTION: Radian()
//
//  PURPOSE: convert from degrees to radians
//

double Radian(double ang)
{
    return ang * (double)3.1415926535 / (double)180.0;
}


//
//  FUNCTION: CalcRot()
//
//  PURPOSE: calculates new position after rotating by the given angle
//

void CalcRot (double x, double y, double ang, double* x1, double* y1)
{
    ang = Radian(-ang);
    *x1 = (x * cos(ang)) - (y * sin(ang));
    *y1 = (x * sin(ang)) + (y * cos(ang));
}


//
//  FUNCTION: DrawGlyph()
//
//  PURPOSE: create bitmap of the given glyph and display it
//

void DrawGlyph (HDC hdc, RECT *pRect, BYTE glyph, HFONT hfont)
{
    TEXTMETRIC tm;
    LOGFONT lf;
    int wChar, h1, h2, w1, w2, x, y;
    double xTL, yTL, xTR, yTR, xBL, yBL, xBR, yBR, xML, yML, xMR, yMR;
    int cWidth, cExtentW, cExtentH;
    int unit, margin;
    RECT rect, rectChar;
    HDC hdcMem;
    HBITMAP hbitmap, hbitmapOld;
    HFONT hfontOld, hfontTmp;
    char szText[80];
    double fAng, fWa, fWb, fHa, fHb, fHeight, fWidth;
    BOOL bRotateable;
    double fxOff, fyOff;
#if defined(TT_AVAILABLE)
    ABC abc;
#endif

    GetTextMetrics (hdc, &tm);
    hfontTmp = SelectObject (hdc, GetStockObject(SYSTEM_FONT));
    GetObject (hfontTmp, sizeof(LOGFONT), &lf);

// Some fields of 'lf' need to be whacked out depending on the type of
// font involved. For instance, lfEscapement should only be non-zero for
// rotatable fonts.
    bRotateable = FALSE;

#if defined (TMPF_VECTOR)
    bRotateable = (tm.tmPitchAndFamily & TMPF_VECTOR);
#endif
#if defined (TMPF_TRUETYPE)
    bRotateable |= (tm.tmPitchAndFamily & TMPF_TRUETYPE);
#endif

    if (!bRotateable)
        lf.lfEscapement = 0;

    SelectObject (hdc, hfontTmp);
    GetCharWidth (hdc, glyph, glyph, &cWidth);

#if defined (_WIN32)
    {
        SIZE size;
        GetTextExtentPoint (hdc, (LPSTR)&glyph, 1, &size);
        cExtentW = size.cx;
        cExtentH = size.cy;
    }
#else
    cExtentW = LOWORD (GetTextExtent (hdc, (LPSTR)&glyph, 1));
    cExtentH = HIWORD (GetTextExtent (hdc, (LPSTR)&glyph, 1));
#endif

    wChar = max(cWidth, cExtentW);

#if defined(TT_AVAILABLE)
    if (GetCharABCWidths (hdc, (UINT)glyph, (UINT)glyph, &abc)) {
        wChar = abc.abcA + abc.abcB + abc.abcC;
        wChar = max(abc.abcA,0) + max (abc.abcB,0) + max (abc.abcC,0);
    } else {
        abc.abcA = 0;
        abc.abcB = wChar;
        abc.abcC = 0;
    }
#endif

// ****
    // Lets figure out how large of a bitmap we need to draw this character into.
    // This will take lfEscapement into account.

    // Turn the 'escapement' value into a valid angle
    fAng = 90.0 - ((double)lf.lfEscapement/10.0);

    // We now need to calculate two values for height and width. These
    // represent the bases of the triangles that are formed as the
    // character cell rotates. Thus fWa+fWb is the 'width' of the space
    // required by the given character at the current rotation.
    // At 0, 90, 180, 270 and 360, one of the two values will be '0'.
    fWa = (double)cExtentW * sin(Radian(fAng));
    fHa = (double)cExtentW * sin(Radian((double)lf.lfEscapement/10.0));
    fHb = (double)cExtentH * sin(Radian(fAng));
    fWb = (double)cExtentH * sin(Radian((double)lf.lfEscapement/10.0));

    // This gives us a width/height of:
    fHeight = fabs(fHa) + fabs(fHb);
    fWidth = fabs(fWa) + fabs(fWb);
    
// ****

    h1 = pRect->bottom - pRect->top;
    h2 = tm.tmHeight + tm.tmExternalLeading;
    h2 = (int)fHeight;
    if (h2 == 0) {
        MessageBeep (0);
        h2 = 10;
    }

    w1 = pRect->right - pRect->left;
    w2 = (int)fWidth;

    unit = (h1 / h2);

    margin = (h1 - (unit * h2)) / 2;
    margin = 0;
    // Define a rectangle that will enclose our drawing area
    SetRect (&rect, 0, 0, wChar*unit, (tm.tmHeight*unit) + (tm.tmExternalLeading*unit));
    OffsetRect (&rect, margin, margin);
    // Define a rectangle that will define the bounding box of the character
    SetRect (&rectChar, 0, 0, wChar*unit, tm.tmHeight*unit);
    OffsetRect (&rectChar, margin, margin + (tm.tmExternalLeading*unit));


// ****
    // These are floating point values.
    SetRect (&rect, 0, 0, (int)(fWidth)*unit, ((int)(fHeight)*unit) + (tm.tmExternalLeading*unit));
    OffsetRect (&rect, margin, margin);
    // Define a rectangle that will define the bounding box of the character
    SetRect (&rectChar, 0, 0, (int)(fWidth)*unit, ((int)(fHeight)*unit));
    OffsetRect (&rectChar, margin, margin + (tm.tmExternalLeading*unit));

    // Calculate the points of the 'character cell'. This will take full
    // rotation into consideration.
    CalcRot (0, 0, (double)lf.lfEscapement/10.0, &xTL, &yTL);
    CalcRot ((double)cExtentW, 0, (double)lf.lfEscapement/10.0, &xTR, &yTR);
    CalcRot (0, (double)cExtentH, (double)lf.lfEscapement/10.0, &xBL, &yBL);
    CalcRot ((double)cExtentW, (double)cExtentH, (double)lf.lfEscapement/10.0, &xBR, &yBR);
    CalcRot (0, (double)tm.tmAscent, (double)lf.lfEscapement/10.0, &xML, &yML);
    CalcRot ((double)cExtentW, (double)tm.tmAscent, (double)lf.lfEscapement/10.0, &xMR, &yMR);

    fxOff = min(xTL, min(xTR, min(xBL, xBR)));
    fyOff = min(yTL, min(yTR, min(yBL, yBR)));

// Draw a rectangle around the drawing limits of the character.
    _MoveTo (hdc, (int)((xTL-fxOff)*(double)unit), (int)((yTL-fyOff)*(double)unit));
    LineTo (hdc, (int)((xTR-fxOff)*(double)unit), (int)((yTR-fyOff)*(double)unit));
    LineTo (hdc, (int)((xBR-fxOff)*(double)unit), (int)((yBR-fyOff)*(double)unit));
    LineTo (hdc, (int)((xBL-fxOff)*(double)unit), (int)((yBL-fyOff)*(double)unit));
    LineTo (hdc, (int)((xTL-fxOff)*(double)unit), (int)((yTL-fyOff)*(double)unit));

// Draw the baseline, which indicates the advance width of the character
    _MoveTo (hdc, (int)((xML-fxOff)*(double)unit), (int)((yML-fyOff)*(double)unit));
    LineTo (hdc, (int)((xMR-fxOff)*(double)unit), (int)((yMR-fyOff)*(double)unit));

    hdcMem = CreateCompatibleDC (hdc);
    if (hdcMem) {
        hbitmap = CreateCompatibleBitmap (hdc, (int)(fWidth), (int)(fHeight));
        if (hbitmap) {
            hbitmapOld = SelectObject (hdcMem, hbitmap);
            BitBlt (hdcMem, 0, 0, (int)(fWidth), (int)(fHeight), NULL, 0, 0, WHITENESS);
            hfontOld = SelectObject (hdcMem, hfont);

            SetTextAlign (hdcMem, TA_LEFT | TA_BASELINE);
            if (TextOut (hdcMem, (int)(xML-fxOff), (int)(yML-fyOff), (LPSTR)&glyph, 1)) {


                /*
                // Just for debugging, lets mark some special pixels
                SetPixel (hdcMem, (int)(xML-fxOff), (int)(yML-fyOff), 0x808080); // Base Lft
                SetPixel (hdcMem, (int)(xTL-fxOff), (int)(yTL-fyOff), 0x808080); // Top Left
                SetPixel (hdcMem, (int)(xTR-fxOff), (int)(yTR-fyOff), 0x808080); // Top Right
                SetPixel (hdcMem, (int)(xBL-fxOff), (int)(yBL-fyOff), 0x808080); // Btm Left
                SetPixel (hdcMem, (int)(xBR-fxOff), (int)(yBR-fyOff), 0x808080); // Btm Right
                SetPixel (hdcMem, (int)(xML-fxOff), (int)(yML-fyOff), 0x808080); // Base Lft
                SetPixel (hdcMem, (int)(xMR-fxOff), (int)(yMR-fyOff), 0x808080); // Base Rt
                */

                CircleBlt(hdc, margin, margin,
                    rectChar.right-rectChar.left, rectChar.bottom-rectChar.top,
                    hdcMem, 0, 0, (int)(fWidth), (int)(fHeight), SRCPAINT);

            } else {
                MessageBox (GetFocus(),
                    "Unable to perform TextOut", "DisplayGlyph", MB_OK);
            }
            SelectObject (hdcMem, hbitmapOld);
            SelectObject (hdcMem, hfontOld);
            DeleteObject (hbitmap);
        } else {
            MessageBox (GetFocus(), "Unable To create Bitmap", "DisplayGlyph", MB_OK);
        }
        DeleteDC (hdcMem);
    } else {
        MessageBox (GetFocus(), "Unable to create DC", "DisplayGlyph", MB_OK);
    }

    hfontTmp = CreateFont(14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, VARIABLE_PITCH | FF_SWISS, "");

    if (hfontTmp) {
        hfontOld = SelectObject (hdc, hfontTmp);
        x = rect.right + 10;
        GetTextMetrics (hdc, &tm);

        y = tm.tmHeight + tm.tmExternalLeading;

        y += tm.tmHeight + tm.tmExternalLeading;
        wsprintf (szText, "Character = '%c' %u (0x%X)", (char)glyph, (int)glyph, (int)glyph);
        TextOut (hdc, x, y, szText, lstrlen(szText));

        y += tm.tmHeight + tm.tmExternalLeading;
        wsprintf (szText, "CharWidth = %i", cWidth);
        TextOut (hdc, x, y, szText, lstrlen(szText));

        // ABC Width - Eventually, I want to 'draw' this into the character
        // cell, but for now, this at least makes the information easily
        // available:
#if defined (TT_AVAILABLE)
        y += tm.tmHeight + tm.tmExternalLeading;
        wsprintf (szText, "A|B|C = %i | %u | %i", abc.abcA, abc.abcB, abc.abcC);
        TextOut (hdc, x, y, szText, lstrlen(szText));
#endif

        y += tm.tmHeight + tm.tmExternalLeading;
        wsprintf (szText, "CharExtentW = %i", cExtentW);
        TextOut (hdc, x, y, szText, lstrlen(szText));

        y += tm.tmHeight + tm.tmExternalLeading;
        wsprintf (szText, "CharExtentH = %i", cExtentH);
        TextOut (hdc, x, y, szText, lstrlen(szText));

        // These are for some values I was using to debug the character
        // cell rotation calculation:

        //y += tm.tmHeight + tm.tmExternalLeading;
        //wsprintf (szText, "(%i,%i) : (%i,%i) [%i]", (int)xTL, (int)yTL, (int)xTR, (int)yTR,(int)(((double)lf.lfEscapement/10.0)*10));
        //TextOut (hdc, x, y, szText, lstrlen(szText));

        //y += tm.tmHeight + tm.tmExternalLeading;
        //wsprintf (szText, "(%i,%i) : (%i,%i)", (int)xML, (int)yML, (int)xMR, (int)yMR);
        //TextOut (hdc, x, y, szText, lstrlen(szText));

        //y += tm.tmHeight + tm.tmExternalLeading;
        //wsprintf (szText, "(%i,%i) : (%i,%i)", (int)xBL, (int)yBL, (int)xBR, (int)yBR);
        //TextOut (hdc, x, y, szText, lstrlen(szText));

        //y += tm.tmHeight + tm.tmExternalLeading;
        //wsprintf (szText, "fAng = %i", (int)(fAng));
        //TextOut (hdc, x, y, szText, lstrlen(szText));

        //y += tm.tmHeight + tm.tmExternalLeading;
        //wsprintf (szText, "fHa = %i", (int)(fHa));
        //TextOut (hdc, x, y, szText, lstrlen(szText));

        //y += tm.tmHeight + tm.tmExternalLeading;
        //wsprintf (szText, "fHb = %i", (int)(fHb));
        //TextOut (hdc, x, y, szText, lstrlen(szText));

        //y += tm.tmHeight + tm.tmExternalLeading;
        //wsprintf (szText, "fWa = %i", (int)(fWa));
        //TextOut (hdc, x, y, szText, lstrlen(szText));

        //y += tm.tmHeight + tm.tmExternalLeading;
        //wsprintf (szText, "fWb = %i", (int)(fWb));
        //TextOut (hdc, x, y, szText, lstrlen(szText));

        //y += tm.tmHeight + tm.tmExternalLeading;
        //wsprintf (szText, "fWd = %i", (int)(fWd));
        //TextOut (hdc, x, y, szText, lstrlen(szText));

        //y += tm.tmHeight + tm.tmExternalLeading;
        //wsprintf (szText, "fHd = %i", (int)(fHd));
        //TextOut (hdc, x, y, szText, lstrlen(szText));

        //y += tm.tmHeight + tm.tmExternalLeading;
        //wsprintf (szText, "fHeight = %i", (int)(fHeight));
        //TextOut (hdc, x, y, szText, lstrlen(szText));

        //y += tm.tmHeight + tm.tmExternalLeading;
        //wsprintf (szText, "fWidth = %i", (int)(fWidth));
        //TextOut (hdc, x, y, szText, lstrlen(szText));

        SelectObject (hdc, hfontOld);
        DeleteObject (hfontTmp);
    }
}
