/*-----------------------------------------------------------------------------+
| TRACKMAP.C                                                                   |
|                                                                              |
| This file contains the code that implements the "MPlayerTrackMap" control.   |
| The control displays the list of tracks contained in the current medium, or  |
| a time scale appropriate to the length of the medium, in such a way as to    |
| serve as a scale for the scrollbar.                                          |
|                                                                              |
| (C) Copyright Microsoft Corporation 1991.  All rights reserved.              |
|                                                                              |
| Revision History                                                             |
|    Oct-1992 MikeTri Ported to WIN32 / WIN16 common code                      |
|                                                                              |
+-----------------------------------------------------------------------------*/

/* include files */

#include <windows.h>
#include <mmsystem.h>
#include "mplayer.h"
#include "toolbar.h"

typedef struct tagScale {
    DWORD   dwInterval;
    UINT    wScale;
} SCALE;

STATICDT SCALE aScale[] =
{
    { 1, SCALE_SECONDS },
    { 2, SCALE_SECONDS },
    { 5, SCALE_SECONDS },
    { 10, SCALE_SECONDS },
    { 25, SCALE_SECONDS },
    { 50, SCALE_SECONDS },
    { 100, SCALE_SECONDS },
    { 250, SCALE_SECONDS },
    { 500, SCALE_SECONDS },
    { 1000, SCALE_SECONDS },
    { 2000, SCALE_SECONDS },
    { 5000, SCALE_SECONDS },
    { 10000, SCALE_SECONDS },
    { 15000, SCALE_MINUTES },
    { 30000, SCALE_MINUTES },
    { 60000, SCALE_MINUTES },
    { 120000, SCALE_MINUTES },
    { 300000, SCALE_MINUTES },
    { 600000, SCALE_HOURS },
    { 1800000, SCALE_HOURS },
    { 3600000, SCALE_HOURS },
    { 7200000, SCALE_HOURS },
    { 18000000, SCALE_HOURS },
    { 36000000, SCALE_HOURS },
    { 72000000, SCALE_HOURS }
};

STATICDT SZCODE   aszNULL[] = TEXT("");
STATICDT SZCODE   aszOneDigit[] = TEXT("0");
STATICDT SZCODE   aszTwoDigits[] = TEXT("00");
STATICDT SZCODE   aszPositionFormat[] = TEXT("%0d");
STATICDT SZCODE   aszMSecFormat[] = TEXT("%d");
STATICDT SZCODE   aszHourFormat[] = TEXT("%d%c");
STATICDT SZCODE   aszMinuteFormat[] = TEXT("%d%c");
STATICDT SZCODE   aszSecondFormat[] = TEXT("%d%c");
STATICDT SZCODE   aszSecondFormatNoLzero[] = TEXT("%c");
STATICDT SZCODE   aszDecimalFormat[] = TEXT("%02d");
/*
 * fnMPlayerTrackMap()
 *
 * This is the window procedure for windows of class "MPlayerTrackMap".
 * This window shows the position of the start of each track of the
 * current medium or a time scale, displayed above the scrollbar which shows
 * the current play position within the medium.
 *
 */

void FAR PASCAL CalcTicsOfDoom(void);

extern UINT gwCurScale;  /* The current scale in which to draw the track map*/
extern BOOL gfCurrentCDNotAudio;/* TRUE when we have a CD that we can't play */

LRESULT FAR PASCAL fnMPlayerTrackMap(

HWND     hwnd,                 /*handle to a MPlayerTrackMap window*/
UINT     wMsg,                 /* message number                   */
WPARAM   wParam,               /* message-dependent parameter      */
LPARAM   lParam)               /* message-dependent parameter      */

{
    PAINTSTRUCT    ps;            /* paint structure for the window   */
    RECT           rc, rcSB;      /* dimensions of the windows        */
    POINT          ptExtent;      /* extent of the track marks        */
    TCHAR          szLabel[20];   /* string holding the current label */
    TCHAR          szLabel2[20];  /* string holding the current label */
    UINT           wNumTics,
                   wTicNo,
                   wTemp,
                   wHour,
                   wMin,
                   wSec,
                   wMsec;
    int            iOldPosition = -1000;
    int            iNewPosition;
    UINT           wScale;
    DWORD          dwMarkValue;
    int            iLargeMarkSize, iFit, iLastPos, iLen;
    BOOL           fForceTextDraw = FALSE;
    HBRUSH         hbr;

    switch (wMsg) {

        case WM_PAINT:

            BeginPaint(hwnd, &ps);

            GetClientRect(ghwndTrackbar, &rcSB);
            GetClientRect(hwnd, &rc);

            /* Set background and text colours */

            (VOID)SendMessage(ghwndApp, WM_CTLCOLORSTATIC,
                              (WPARAM)ps.hdc, (LONG_PTR)hwnd);

            /* Get the length of the scrollbar we're putting tics under */
            /* Use these numbers for size and position calculations     */
            GetClientRect(ghwndMap, &rc);

            /*
             * Check to see if we actually have a valid device loaded up;
             * if not, don't display anything
             *
             */

            if (gwDeviceID == 0
                    || gwStatus == MCI_MODE_OPEN
                    || gwStatus == MCI_MODE_NOT_READY || gdwMediaLength == 0
                    || !gfValidMediaInfo
                    || gfCurrentCDNotAudio) {
                EndPaint(hwnd,&ps);
                //VIJR-SBSetWindowText(ghwndStatic, aszNULL);
                WriteStatusMessage(ghwndStatic, (LPTSTR)aszNULL);
                return 0L;
            }

            /* Select the font to use */

            if (ghfontMap != NULL)
                SelectObject(ps.hdc, ghfontMap);

            /*
             * Because the scrollbar thumb takes up space in the inner part
             * of the scrollbar, compute its width so that we can compensate
             * for it while displaying the trackmap.
             *
             */

            /*
             * Get the child window rectangle and reduce it such that
             * it is the same width as the inner part of the scrollbar.
             *
             */
            //rc.left;  //!!! GetSystemMetrics(SM_CXHSCROLL);
            //rc.right; //!!!(GetSystemMetrics(SM_CXHSCROLL));

            /* Now, Put text underneath the TICS */
            if (gwCurScale == ID_TRACKS) {

                SIZE Size;

                GetTextExtentPoint32( ps.hdc, aszTwoDigits, 2, &Size );

                ptExtent.x = Size.cx;
                ptExtent.y = Size.cy;

                /*
                 * Based on the width of the child window, compute the positions
                 * to place the track markers.
                 *
                 */

                wNumTics = (UINT)SendMessage(ghwndTrackbar, TBM_GETNUMTICS, 0, 0L);

                /*
                 * TBM_GETNUMTICS returns the number of visible tics
                 * which includes the first and last tics not created
                 * by media player.  Subtract 2 to account for the
                 * the first and last tics.
                 *
                 */

                if (wNumTics >= 2)
                    wNumTics = wNumTics - 2;

                for(wTicNo = 0; wTicNo < wNumTics; wTicNo++) {

                    /* Get the position of the next tic */
                    iNewPosition = (int)SendMessage(ghwndTrackbar, TBM_GETTICPOS,
                                                    (WPARAM)wTicNo, 0L);
                    /* Centre it above the marker. */
                    iNewPosition -= ptExtent.x / 4;

                    /*
                     * Check to make sure that we are not overwriting the
                     * text from the previous marker.
                     *
                     */

                    if (iNewPosition > iOldPosition) {

                        wsprintf(szLabel, aszPositionFormat, wTicNo + gwFirstTrack);
                        TextOut(ps.hdc,
                                iNewPosition + rc.left,
                                0, szLabel,
                                (wTicNo + gwFirstTrack < 10) ? 1 : 2 );
                        /* Finish the end of the text string we just printed */
                        iOldPosition = iNewPosition +
                                       ((wTicNo + gwFirstTrack < 10)
                                       ? ptExtent.x / 2 : ptExtent.x);
                    }
                }
            } else {

                #define ONE_HOUR    (60ul*60ul*1000ul)
                #define ONE_MINUTE  (60ul*1000ul)
                #define ONE_SECOND  (1000ul)

                /*
                 * The scale is set to display time - find out what units
                 * (msec, sec, min, or hour) are most appropriate, for the
                 * scale. This requires us to look at both the overall length
                 * of the medium and the distance between markers (or
                 * granularity).
                 *
                 */

                /*
                 * Find the maximum number of markers that we can draw without
                 * cluttering the display too badly, and find the granularity
                 * between these markers.
                 *
                 */

                SIZE Size;

                GetTextExtentPoint32( ps.hdc, aszOneDigit, 1, &Size );

                ptExtent.x = Size.cx;
                ptExtent.y = Size.cy;

                if (gdwMediaLength < 10)
                    iLargeMarkSize = 1;
                else if (gdwMediaLength < 100)
                    iLargeMarkSize = 2;
                else if (gdwMediaLength < 1000)
                    iLargeMarkSize = 3;
                else if (gdwMediaLength < 10000)
                    iLargeMarkSize = 4;
                else
                    iLargeMarkSize = 5;

                wNumTics = (UINT)SendMessage(ghwndTrackbar, TBM_GETNUMTICS,
								0, 0L);

                /*
                 * TBM_GETNUMTICS returns the number of visible tics
                 * which includes the first and last tics not created
                 * by media player.  Subtract 2 to account for the
                 * the first and last tics.
                 *
                 */

                if (wNumTics >= 2)
                    wNumTics = wNumTics - 2;

                /* Where the text for the last mark will begin */
		if (wNumTics > 1) {
                    iLastPos = (int)SendMessage(ghwndTrackbar,
			TBM_GETTICPOS, (WPARAM)wNumTics - 1, 0L);
                    iLastPos -= ptExtent.x  / 2;    // centre 1st numeral
		}

                /* What scale do we use?  Hours, minutes, or seconds? */
                /* NOTE:  THIS MUST AGREE WITH WHAT FormatTime() does */
                /* in mplayer.c !!!                                   */
                if (gwCurScale == ID_FRAMES)
                    wScale = SCALE_FRAMES;
                else {
                    if (gdwMediaLength > ONE_HOUR)
                        wScale = SCALE_HOURS;
                    else if (gdwMediaLength > ONE_MINUTE)
                        wScale = SCALE_MINUTES;
                    else
                        wScale = SCALE_SECONDS;
                }

                for (wTicNo = 0; wTicNo < wNumTics; wTicNo++) {

                    /* The text for the last tic is always drawn */
                    if (wTicNo == wNumTics - 1)
                        fForceTextDraw = TRUE;

                    dwMarkValue = (DWORD)SendMessage(ghwndTrackbar, TBM_GETTIC,
                                          (WPARAM)wTicNo, 0L);
                    iNewPosition = (int)SendMessage(ghwndTrackbar, TBM_GETTICPOS,
                                                (WPARAM)wTicNo, 0L);


                    /*
                     * Get the text ready for printing and centre it above the
                     * marker.
                     *
                     */

                    switch ( wScale ) {

                        case SCALE_FRAMES:
                        case SCALE_MSEC:
                            wsprintf(szLabel, aszMSecFormat, dwMarkValue);
                            break;

                        case SCALE_HOURS:

                            wHour = (WORD)(dwMarkValue / 3600000);
                            wMin = (WORD)((dwMarkValue % 3600000) / 60000);
                            wsprintf(szLabel2,aszDecimalFormat,wMin);
                            wsprintf(szLabel,aszHourFormat,wHour, chTime);
                            lstrcat(szLabel,szLabel2);
                            break;

                        case SCALE_MINUTES :

                            wMin = (WORD)(dwMarkValue / 60000);
                            wSec = (WORD)((dwMarkValue % 60000) / 1000);
                            wsprintf(szLabel2,aszDecimalFormat,wSec);
                            wsprintf(szLabel,aszMinuteFormat,wMin,chTime);
                            lstrcat(szLabel,szLabel2);
                            break;

                        case SCALE_SECONDS :

                            wSec = (WORD)((dwMarkValue + 5) / 1000);
                            wMsec = (WORD)(((dwMarkValue + 5) % 1000) / 10);
                            wsprintf(szLabel2,aszDecimalFormat,wMsec);
                            if (!wSec && chLzero == TEXT('0'))
                                wsprintf(szLabel, aszSecondFormatNoLzero,  chDecimal);
                            else
                                wsprintf(szLabel, aszSecondFormat, wSec, chDecimal);
                            lstrcat(szLabel,szLabel2);
                            break;

                    }

                    wTemp = STRLEN(szLabel);
                    iNewPosition -= ptExtent.x  / 2;    // centre 1st numeral

                    /* The position after which text will be cut off the */
                    /* right edge of the window                          */
                    iFit = rc.right - rc.left - (ptExtent.x * iLargeMarkSize);

                    /* Calculate the length of the text we just printed. */
                    /* Leave a little space at the end, too.             */
                    iLen = (ptExtent.x * wTemp) + ptExtent.x / 2;

                    /* Display the mark if we can without overlapping either
                     * the previous mark or the final mark or going off the
                     * edge of the window. */
                    if (fForceTextDraw ||
                        (iNewPosition >= iOldPosition &&
                         iNewPosition <= iFit &&
                         iNewPosition + iLen <= iLastPos)) {
                        TextOut(ps.hdc, iNewPosition + rc.left, 0,
                                szLabel, wTemp );
                        /* Calculate the end pos of the text we just printed. */
                        iOldPosition = iNewPosition + iLen;

                    } else {

                        DPF("Didn't display mark: iNew = %d; iOld = %d; iFit = %d; iLen = %d, iLast = %d\n", iNewPosition, iOldPosition, iFit, iLen, iLastPos);
                    }
                }
            }
            EndPaint(hwnd, &ps);
            return 0L;

        case WM_ERASEBKGND:

            GetClientRect(hwnd, &rc);

            hbr = (HBRUSH)SendMessage(ghwndApp, WM_CTLCOLORSTATIC,
                                      wParam, (LONG_PTR)hwnd);

            if (hbr != NULL)
                FillRect((HDC)wParam, &rc, hbr);

            return TRUE;
    }

    /* Let DefWindowProc() process all other window messages */

    return DefWindowProc(hwnd, wMsg, wParam, lParam);

}

/* Gee thanks for the helpful spec for this routine! */

void FAR PASCAL CalcTicsOfDoom(void)
{
    UINT        wMarkNo;
    int         iTableIndex;
    DWORD       dwMarkValue,
                dwNewPosition;
    BOOL        fDidLastMark = FALSE;

    if (gfPlayOnly && !gfOle2IPEditing)
        return;

    DPF2("CalcTicsOfDoom\n");
    SendMessage(ghwndTrackbar, TBM_CLEARTICS, (WPARAM)FALSE, 0L);

    if (gwCurScale == ID_TRACKS) {

        /*
         * Based on the width of the child window, compute the positions
         * to place the track marker tics.
         *
         */

        for (wMarkNo = 0; wMarkNo < gwNumTracks; wMarkNo++) {

            /* If zero length, don't mark it, unless it is the end */
            if ((wMarkNo < gwNumTracks - 1) &&
                (gadwTrackStart[wMarkNo] == gadwTrackStart[wMarkNo + 1]))
                continue;

            /* Compute the centre point and place a marker there */

            if (gdwMediaLength == 0)
                dwNewPosition = 0;
            else
                dwNewPosition = gadwTrackStart[wMarkNo];

            SendMessage(ghwndTrackbar,
                        TBM_SETTIC,
                        (WPARAM)FALSE,
                        (LPARAM)dwNewPosition);

        }
    } else {

        /*
         * The scale is set to display time - find out what units
         * (msec, sec, min, or hour) are most appropriate, for the
         * scale. This requires us to look at both the overall length
         * of the medium and the distance between markers (or
         * granularity).
         *
         */

        /*
         * Find the maximum number of markers that we can draw without
         * cluttering the display too badly, and find the granularity
         * between these markers.
         *
         */

        UINT    wNumTicks;
        RECT    rc;

        if(!GetClientRect(ghwndMap, &rc)) {
            DPF0("GetClientRect failed in CalcTicsOfDoom: Error %d\n", GetLastError());
        }

        wNumTicks = rc.right / 60;

        if (0 == gdwMediaLength) {
            iTableIndex = 0;
        } else {

            DPF4("Checking the scale for media length = %d, tick count = %d\n", gdwMediaLength, wNumTicks);

            for (iTableIndex = (sizeof(aScale) / sizeof(SCALE)) -1;
                (int)iTableIndex >= 0;
                iTableIndex--) {

                DPF4("Index %02d: %d\n", aScale[iTableIndex].dwInterval * wNumTicks);

                if ((aScale[iTableIndex].dwInterval * wNumTicks)
                    <= gdwMediaLength)
                    break;
            }
        }
#ifdef DEBUG
        if ((int)iTableIndex == -1) {
            DPF("BAD TABLEINDEX\n");
            DebugBreak();
        }
#endif
        // We have enough room to show every tick.  Don't let our index wrap
        // around, or we won't see ANY ticks which would look odd.
        if (iTableIndex <0)
            iTableIndex = 0;

        dwMarkValue = gdwMediaStart;

        do {

            /* Compute the centre point and place a marker there */

            if (gdwMediaLength == 0)
                dwNewPosition = 0;
            else
                dwNewPosition = dwMarkValue; // HACK!! - gdwMediaStart;

            SendMessage(ghwndTrackbar,
                        TBM_SETTIC,
                        (WPARAM)FALSE,
                        (LPARAM)dwNewPosition);

            /* If this is the first mark, adjust so it's going
            /* by the right interval. */
            if (dwMarkValue == gdwMediaStart) {
                dwMarkValue += aScale[iTableIndex].dwInterval
                - (dwMarkValue % aScale[iTableIndex].dwInterval);
            } else {
                dwMarkValue += aScale[iTableIndex].dwInterval;
            }

            /* If we're almost done, do the final mark. */
            if ((dwMarkValue >= (gdwMediaLength + gdwMediaStart))
                && !(fDidLastMark)) {
                fDidLastMark = TRUE;
                dwMarkValue = gdwMediaLength + gdwMediaStart;
            }
        } while (dwMarkValue <= gdwMediaStart + gdwMediaLength);
    }

    InvalidateRect(ghwndTrackbar, NULL, FALSE);
    InvalidateRect(ghwndMap, NULL, TRUE);
}