/*
 * PEND.C
 *
 * Graphics Driver for SieFuzzy Pendulum demo.
 *
 * Copyright (c) Siemens AG 1993.
 * All rights reserved.
 *
 * $Header:   P:/VCS/TSEX/PEND/PEND.C_V   1.1   10 Dec 1993 17:47:22   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		"Single Inverted Pendulum"
#define SHIFT90RADS	(((2.0*M_PI)/360.0)*90.0)
#define AXISLEN 	(200)
#define MAXBOB		(AXISLEN/3.0)
#define MAXMOTOR	(AXISLEN/5.0)
#define MAXSTICK	(110.0)

// 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);
    }

    SetWindowText (hWndGraphic, (LPCSTR) TITLE);

    SetCursor (hCurOrg);

    // 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)
{
    int xNew;
    int yNew;
    PWINDATA pWinData;

    NOTUSED(hMem);

    if (!hWndGraphic)
	return 0;

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

    // Get the input parameters
    //
    pWinData		= (PWINDATA) GetWinMem (hWndGraphic);
    pWinData->Theta	= params++->float_value;
    pWinData->StickSize = params++->float_value;
    pWinData->MotorSize = params++->float_value;
    pWinData->BobMass	= params++->float_value;

    // Determine new bob location.
    //
    xNew = (int) (pWinData->StickSize * MAXSTICK * -cos (pWinData->Theta + SHIFT90RADS));
    yNew = (int) (pWinData->StickSize * MAXSTICK *  sin (pWinData->Theta + SHIFT90RADS));

    // If the bob location has changed, update.
    //
    if (xNew != pWinData->pBob.x || yNew != pWinData->pBob.y)
    {
	pWinData->pBob.x = xNew;
	pWinData->pBob.y = yNew;
	InvalidateRect (pWinData->hWnd, &pWinData->r, 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->Theta	= 0.0;
    pWinData->StickSize = 4.0;
    pWinData->MotorSize = 120.0;
    pWinData->BobMass	= 3.0;
    pWinData->pBob.x	= (int) (pWinData->StickSize * MAXSTICK * -cos (pWinData->Theta + SHIFT90RADS));
    pWinData->pBob.y	= (int) (pWinData->StickSize * MAXSTICK *  sin (pWinData->Theta + SHIFT90RADS));

    // 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 cbob;
    FLOAT cxBob1;
    FLOAT cyBob1;
    PAINTSTRUCT ps;
    HPEN hPenColor, hPenOrg;
    HBRUSH hBrushColor, hBrushOrg;
    POINT poly[3];

    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, 2*AXISLEN, 2*AXISLEN);
    SetViewportExt (hDC, cx/2, -cy/2);
    SetViewportOrg (hDC, cx/2, r.top+cy*4/5);

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

    // Draw axis.
    //
    SelectPen (hDC, GetStockObject (BLACK_PEN));
    SetRect (&r, -AXISLEN, 0, AXISLEN, -20);
    SelectBrush (hDC, GetStockObject (GRAY_BRUSH));
    Rectangle (hDC, r.left, r.top, r.right, r.bottom);

    // Draw stick.
    //
    cxBob1 = pWinData->pBob.x;
    cyBob1 = pWinData->pBob.y;
    hPenColor = CreatePen (PS_SOLID, 1, RGB(255,0,0));
    hPenOrg   = SelectPen (hDC, hPenColor);
    MoveTo (hDC, 0, 20);
    LineTo (hDC, (int) cxBob1, (int) cyBob1);
    SelectPen (hDC, hPenOrg);
    DeleteObject (hPenColor);

    // Draw new pendulum bob.
    //
    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);
    cbob       = pWinData->BobMass * 20.0 / 100.0 * MAXBOB;  // 0.0 to MAXBOB
    b1.left    = (int) (cxBob1-cbob/2);
    b1.right   = (int) (cxBob1+cbob/2)+(r.right-r.left);
    b1.top     = (int) (cyBob1+cbob/2);
    b1.bottom  = (int) (cyBob1-cbob/2)+(r.bottom-r.top);
    Ellipse (hDC, b1.left, b1.top, b1.right, b1.bottom);

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

    // Draw new motorsize.
    //
    hBrushColor = CreateSolidBrush (RGB(128, 128, 128));
    hBrushOrg	= SelectBrush (hDC, hBrushColor);
    SelectPen (hDC, GetStockObject (BLACK_PEN));

    poly[0].x = -30;
    poly[0].y = -1;
    poly[1].x = 0;
    poly[1].y = 20;
    poly[2].x = 30;
    poly[2].y = -1;
    Polygon (hDC, (LPPOINT) &poly, 3);

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

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