/*
 * BEAMBALL.C
 *
 * Graphics Driver for SieFuzzy Beamball demo.
 *
 * Copyright (c) Siemens ZFE ST SN 42 1993.
 * All rights reserved.
 *
 * $Header:   P:/VCS/TSEX/BEAMBALL/BEAMBALL.C_V   1.1   10 Dec 1993 17:45:42   ror  $
 */

#include <windows.h>
#include <windowsx.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <fpl.h>
#include <ts.h>
#include "tssim.h"

// Local Macros.
//
#define TITLE           "Beamball"
#define AXISLEN         (200)
#define TORQUE_RADIUS   (0.05)
#define RADIUS_FACTOR   (10)


// Create the graphic output window.
//
// Returns NULL if initialization errors, otherwise non-NULL GLOBALHANDLE.
//
GLOBALHANDLE FAR PASCAL TSSimGraphicInit(LPGRAPHICINIT pInit)
{
    HCURSOR hCurOrg;
    MDICREATESTRUCT cs;

    hWndFrame = pInit->hWndFrame;

    if (hWndGraphic)
    {
	ShowWindow (hWndGraphic, SW_SHOW);
	return (GLOBALHANDLE) 1;
    }

    hCurOrg = SetCursor (LoadCursor (NULL, IDC_WAIT));

    // Load MDI create struct with sent data.
    //
    cs.szClass    = (LPSTR) CLASS;
    cs.szTitle    = (LPSTR) TITLE;
    cs.style      = CS_DBLCLKS | WS_CLIPCHILDREN;
    cs.x          = CW_USEDEFAULT;
    cs.y          = CW_USEDEFAULT;
    cs.cx         = CW_USEDEFAULT;
    cs.cy         = CW_USEDEFAULT;
    cs.hOwner     = hInst;
    cs.lParam     = 0L;

    hWndMDIClient = pInit->hWndMDIClient;
    hWndGraphic = (HWND) (UINT) SendMessage (pInit->hWndMDIClient, WM_MDICREATE, 0, (LPARAM) (LPMDICREATESTRUCT) &cs);

    // Error if no memory or mega problem
    //
    if (hWndGraphic == NULL)
    {
	SetCursor (hCurOrg);
	return NULL;
    }

    // If there is a window, show it.
    //
    else
    {
	ShowWindow (hWndGraphic, SW_SHOW);
	UpdateWindow (hWndGraphic);
    }

    SetCursor (hCurOrg);

    SetWindowText (hWndGraphic, (LPCSTR) TITLE);

    // This window doesn't require any allocated memory, but we must return
    // a non-NULL GLOBALHANDLE to SieFuzzy so it knows that initialization
    // was successful
    //
    return (GLOBALHANDLE) 1;
}


// Called when the user screen should be updated.
//
// Returns 0 if no errors, else non-zero.
//
int FAR PASCAL TSSimGraphic(GLOBALHANDLE hMem, LPSTR paramfmt, LPIOVALUE params)
{
    PWINDATA pWinData;

    NOTUSED(hMem);

    if (!hWndGraphic)
	return 0;

    // Verify parameters are 8 FLOAT values, otherwise, return a bad completion.
    //
    if (lstrcmp (paramfmt, "AAAAAAAA") != 0)
	return 1;

    // Get the input parameters
    //
    pWinData            = (PWINDATA) GetWinMem (hWndGraphic);
    pWinData->alpha = params++->float_value;
    pWinData->pos = params++->float_value;
    pWinData->torque    = params++->float_value;
    pWinData->radius    = params++->float_value;
    pWinData->length = params++->float_value;
    pWinData->BallMass = params++->float_value;
    pWinData->BeamMass = params++->float_value;
    pWinData->MaxTorque = params++->float_value;

    // Determine new locations.
    //
    pWinData->pBeam1.x = (int) (-0.5 * cos(pWinData->alpha) * pWinData->length * AXISLEN);
    pWinData->pBeam1.y = (int) (-0.5 * sin(pWinData->alpha) * pWinData->length * AXISLEN);
    pWinData->pBeam2.x = - pWinData->pBeam1.x;
    pWinData->pBeam2.y = - pWinData->pBeam1.y;
    pWinData->pBall.x = (int) ((pWinData->pos * cos(pWinData->alpha) + 
    pWinData->radius * RADIUS_FACTOR * sin(pWinData->alpha)/2) * AXISLEN);
    pWinData->pBall.y = (int) ((pWinData->pos * sin(pWinData->alpha) - 
    pWinData->radius * RADIUS_FACTOR * cos(pWinData->alpha)/2) * AXISLEN);
    pWinData->pTorque.x = (int) (-0.5 * cos(pWinData->alpha) * pWinData->length * AXISLEN * (pWinData->torque / pWinData->MaxTorque));
    pWinData->pTorque.y = (int) (-0.5 * sin(pWinData->alpha) * pWinData->length * AXISLEN * (pWinData->torque / pWinData->MaxTorque));

    InvalidateRect (pWinData->hWnd, NULL, TRUE);
    UpdateWindow (pWinData->hWnd);

    // Return a good completion
    //
    return 0;
}


// Called by SieFuzzy when the simulation is reset.
//
// Returns 0 if no errors, else non-zero.
//
int FAR PASCAL TSSimGraphicReset(GLOBALHANDLE hMem)
{
    NOTUSED(hMem);

    // Reset internal flags and memory structures.
    //

    // Return a good completion
    //
    return 0;
}


// Called by SieFuzzy when the simulation is terminated.
//
void FAR PASCAL TSSimGraphicClean(GLOBALHANDLE hMem)
{
    NOTUSED(hMem);

    // This window does not require allocated memory, so we don't
    // have to deallocate the hMem handle.
    //
    SendMessage (hWndGraphic, WM_CLOSE, 0, 0L);
}


// Initialize window data.
//
void SimGraphic_InitWindowData(PWINDATA pWinData)
{
    int cx;
    RECT r;

    pWinData->torque    = 0.0;
    pWinData->alpha = 25.0 * M_PI / 180;
    pWinData->pos = -0.2;
    pWinData->radius = 0.0127;
    pWinData->length = 1.02;
    pWinData->BallMass = 0.06591;
    pWinData->BeamMass = 0.387;
    pWinData->MaxTorque = 2.0;
    pWinData->pBeam1.x = (int) (-0.5 * cos(pWinData->alpha) * pWinData->length * AXISLEN);
    pWinData->pBeam1.y = (int) (-0.5 * sin(pWinData->alpha) * pWinData->length * AXISLEN);
    pWinData->pBeam2.x = - pWinData->pBeam1.x;
    pWinData->pBeam2.y = - pWinData->pBeam1.y;
    pWinData->pBall.x = (int) ((pWinData->pos * cos(pWinData->alpha) + 
    pWinData->radius * RADIUS_FACTOR * sin(pWinData->alpha)/2) * AXISLEN);
    pWinData->pBall.y = (int) ((pWinData->pos * sin(pWinData->alpha) - 
    pWinData->radius * RADIUS_FACTOR * cos(pWinData->alpha)/2) * AXISLEN);
    pWinData->pTorque.x = (int) (-0.5 * cos(pWinData->alpha) * pWinData->length * AXISLEN * (pWinData->torque / pWinData->MaxTorque));
    pWinData->pTorque.y = (int) (-0.5 * sin(pWinData->alpha) * pWinData->length * AXISLEN * (pWinData->torque / pWinData->MaxTorque));
    
    // Resize our window.
    //
    if (hWndMDIClient)
    {
	GetWindowRect (pWinData->hWnd, &r);
	ScreenToClient (hWndMDIClient, (POINT FAR *) &r);
	ScreenToClient (hWndMDIClient, (POINT FAR *) &r.right);
	cx = (r.bottom-r.top)/2;
	MoveWindow (pWinData->hWnd, r.left, r.top, cx, cx, TRUE);
    }
}


// Paint the window.
//
void SimGraphic_OnPaint(PWINDATA pWinData)
{
    RECT r;
    int cx;
    int cy;
    HDC hDC;
    RECT b1;
    FLOAT cradius;
    FLOAT cxBeam1;
    FLOAT cyBeam1;
    FLOAT cxBeam2;
    FLOAT cyBeam2;
    FLOAT cxBall;
    FLOAT cyBall;
    PAINTSTRUCT ps;
    HPEN hPenColor, hPenOrg;
    HBRUSH hBrushColor, hBrushOrg;

    if (!hWndGraphic)
	return;

    // Intialize hDC for painting.
    //
    hDC = BeginPaint (pWinData->hWnd, &ps);

    // Set up ISOTROPIC mapping mode for four quatrant system.
    //
    FillRect (hDC, (LPRECT) &ps.rcPaint, GetStockObject (WHITE_BRUSH));
    GetClientRect (pWinData->hWnd, &r);

    cx = r.right  - r.left;
    cy = r.bottom - r.top;
    SetMapMode (hDC, MM_ISOTROPIC);
    SetWindowExt (hDC, (int) (0.7 * AXISLEN), (int) (0.7 * AXISLEN));
    SetViewportExt (hDC, cx/2, cy/2);
    SetViewportOrg (hDC, cx/2, cy/2);

    SetRect (&pWinData->r, -AXISLEN, -AXISLEN, AXISLEN, AXISLEN);
    LPtoDP (hDC, (LPPOINT) &pWinData->r, 2);

    // Draw beam.
    //
    cxBeam1 = pWinData->pBeam1.x;
    cyBeam1 = pWinData->pBeam1.y;
    cxBeam2 = pWinData->pBeam2.x;
    cyBeam2 = pWinData->pBeam2.y;
    hPenColor = CreatePen (PS_SOLID, 5, RGB(255,0,0));
    hPenOrg   = SelectPen (hDC, hPenColor);
    MoveTo (hDC, (int) cxBeam1, (int) cyBeam1);
    LineTo (hDC, (int) cxBeam2, (int) cyBeam2);
    SelectPen (hDC, hPenOrg);
    DeleteObject (hPenColor);

    // Draw turning axis.
    //
    SelectPen (hDC, GetStockObject (NULL_PEN));
    hBrushColor = CreateSolidBrush (RGB(000, 128, 064));
    hBrushOrg   = SelectBrush (hDC, hBrushColor);

    SetRect (&r, 0, 0, 2, 2);
    DPtoLP  (hDC, (LPPOINT) &r, 2);
    cradius       = TORQUE_RADIUS * AXISLEN;  
    b1.left    = (int) (-cradius/2);
    b1.right   = (int) (cradius/2)+(r.right-r.left);
    b1.top     = (int) (cradius/2);
    b1.bottom  = (int) (-cradius/2)+(r.bottom-r.top);
    Ellipse (hDC, b1.left, b1.top, b1.right, b1.bottom);
    SelectBrush (hDC, hBrushOrg);
    DeleteObject (hBrushColor);

    // Draw ball.
    //
    SelectPen (hDC, GetStockObject (NULL_PEN));
    hBrushColor = CreateSolidBrush (RGB(000, 000, 255));
    hBrushOrg   = SelectBrush (hDC, hBrushColor);

    SetRect (&r, 0, 0, 2, 2);
    DPtoLP  (hDC, (LPPOINT) &r, 2);
    cxBall = pWinData->pBall.x;
    cyBall = pWinData->pBall.y;
    cradius       = pWinData->radius * RADIUS_FACTOR * AXISLEN;  
    b1.left    = (int) (cxBall-cradius/2);
    b1.right   = (int) (cxBall+cradius/2)+(r.right-r.left);
    b1.top     = (int) (cyBall-cradius/2);
    b1.bottom  = (int) (cyBall+cradius/2)+(r.bottom-r.top);
    Ellipse (hDC, b1.left, b1.top, b1.right, b1.bottom);

    SelectBrush (hDC, hBrushOrg);
    DeleteObject (hBrushColor);

    // Draw new torque.
    //
    cxBall = pWinData->pTorque.x;
    cyBall = pWinData->pTorque.y;
    SelectPen (hDC, GetStockObject (NULL_PEN));
    hBrushColor = CreateSolidBrush (RGB(0, 0, 0));
    hBrushOrg   = SelectBrush (hDC, hBrushColor);

    SetRect (&r, 0, 0, 2, 2);
    DPtoLP  (hDC, (LPPOINT) &r, 2);
    cradius       = TORQUE_RADIUS * AXISLEN;  
    b1.left    = (int) (cxBall-cradius/2);
    b1.right   = (int) (cxBall+cradius/2)+(r.right-r.left);
    b1.top     = (int) (cyBall+cradius/2);
    b1.bottom  = (int) (cyBall-cradius/2)+(r.bottom-r.top);
    Ellipse (hDC, b1.left, b1.top, b1.right, b1.bottom);
    SelectBrush (hDC, hBrushOrg);
    DeleteObject (hBrushColor);

    // Done Painting.
    //
    EndPaint (pWinData->hWnd, &ps);
}
