/*********************************************************************
*                 SEGGER Software GmbH                               *
*        Solutions for real time microcontroller applications        *
**********************************************************************
*                                                                    *
*        (c) 1996 - 2018  SEGGER Microcontroller GmbH                *
*                                                                    *
*        Internet: www.segger.com    Support:  support@segger.com    *
*                                                                    *
**********************************************************************

** emWin V5.46 - Graphical user interface for embedded applications **
All  Intellectual Property rights in the Software belongs to  SEGGER.
emWin is protected by  international copyright laws.  Knowledge of the
source code may not be used to write a similar product. This file may
only be used in accordance with the following terms:

The  software has  been licensed by SEGGER Software GmbH to Nuvoton Technology Corporation
at the address: No. 4, Creation Rd. III, Hsinchu Science Park, Taiwan
for the purposes  of  creating  libraries  for its
Arm Cortex-M and  Arm9 32-bit microcontrollers, commercialized and distributed by Nuvoton Technology Corporation
under  the terms and conditions  of  an  End  User
License  Agreement  supplied  with  the libraries.
Full source code is available at: www.segger.com

We appreciate your understanding and fairness.
----------------------------------------------------------------------
Licensing information
Licensor:                 SEGGER Software GmbH
Licensed to:              Nuvoton Technology Corporation, No. 4, Creation Rd. III, Hsinchu Science Park, 30077 Hsinchu City, Taiwan
Licensed SEGGER software: emWin
License number:           GUI-00735
License model:            emWin License Agreement, signed February 27, 2018
Licensed platform:        Cortex-M and ARM9 32-bit series microcontroller designed and manufactured by or for Nuvoton Technology Corporation
----------------------------------------------------------------------
Support and Update Agreement (SUA)
SUA period:               2018-03-26 - 2019-03-27
Contact to extend SUA:    sales@segger.com
----------------------------------------------------------------------
File        : GUIDEMO_Graph.c
Purpose     : Demonstration of the GRAPH widget.
----------------------------------------------------------------------
*/

#include "GUIDEMO.h"

#if (SHOW_GUIDEMO_GRAPH && GUI_WINSUPPORT && GUI_SUPPORT_MEMDEV)

/*********************************************************************
*
*       Configuration defines
*
**********************************************************************
*/
#define MAX_GRAPH_SIZE_X  440
#define MAX_GRAPH_SIZE_Y  ((MAX_GRAPH_SIZE_X * 11) / 20)

#define DIST_TO_BORDER    10
#define DIST_TO_WIN       5

#define BORDER_TOP        0
#define BORDER_BOTTOM     11
#define BORDER_LEFT       19
#define BORDER_RIGHT      0

#define COLOR_BK          GUI_DARKGRAY
#define COLOR_BORDER      BK_COLOR_1
#define COLOR_FRAME       GUI_BLACK
#define COLOR_GRID        GUI_GRAY

#define SCALE_H_HEIGHT    4

#define TICK_DIST_H       25
#define TICK_DIST_V       20

#define TIME_RUN          5000
#define TIME_STEP         15

#define MAX_NUM_DATA_OBJ  3

#define GRAPH_DIV         9   // (2^9 = 512) If this value is changed _aWaves[] need to be changed too!
#define GRID_DIST_X       25
#define GRID_DIST_Y       10

/*********************************************************************
*
*       Typedef / Data
*
**********************************************************************
*/
typedef struct
{
    char  * pName;
    int     ScaleVOff;
    int     DataVOff;
    int     GridVOff;
    void (* pfAddData)(GRAPH_DATA_Handle hData, int DataID);
    int     NumWaves;
} GRAPH_WAVE;

static int _HeartBeat[] =
{
    2,   4,   6,   8,  10,   6,   2,   0,   0,   0,
    -8,  16,  40,  64,  88,  58,  28,  -2, -32, -30,
    -20, -10,   0,   2,   2,   4,   4,   6,   6,   8,
    8,  10,  12,  14,  16,  18,  20,  16,  12,   8,
    4,   2,   2,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0
};

static GUI_COLOR _aColorData[MAX_NUM_DATA_OBJ] =
{
    GUI_MAKE_COLOR(0x50C0FF),
    GUI_MAKE_COLOR(0xFFC050),
    GUI_MAKE_COLOR(0x50FFC0),
};

static GRAPH_SCALE_Handle _hScaleH;
static GRAPH_SCALE_Handle _hScaleV;
static int                _DataAdjust;

/*********************************************************************
*
*       Static functions
*
**********************************************************************
*/
/*********************************************************************
*
*       _AddData_Sine
*/
static void _AddData_Sine(GRAPH_DATA_Handle hData, int DataID)
{
    static int x1000[MAX_NUM_DATA_OBJ];
    I32        SinHQ;
    int        Multi;
    int        Step;

    switch(DataID)
    {
        case 0:
            Multi = 70;
            Step  = 3;
            break;
        case 1:
            Multi = 50;
            Step  = 1;
            break;
        case 2:
            Multi = 30;
            Step  = 7;
            break;
        default:
            return;
    }
    SinHQ          = GUI__SinHQ(x1000[DataID]);
    x1000[DataID] += 1000 * Step;
    GRAPH_DATA_YT_AddValue(hData, SHIFT_RIGHT_16(SinHQ * Multi) + _DataAdjust);
}

/*********************************************************************
*
*       _AddData_Heartbeat
*/
static void _AddData_Heartbeat(GRAPH_DATA_Handle hData, int DataID)
{
    static int Index;

    GUI_USE_PARA(DataID);
    GRAPH_DATA_YT_AddValue(hData, (_HeartBeat[Index]) + _DataAdjust);
    if(++Index == GUI_COUNTOF(_HeartBeat))
    {
        Index = 0;
    }
}

/*********************************************************************
*
*       DATA _aWave - Keep below _AddData-functions
*/
GRAPH_WAVE _aWave[] =
{
    {
        "Heartbeat",        // Name
        157,                // Vertical scale offset in relation to GRAPH_DIV
        152,                // Vertical data  offset in relation to GRAPH_DIV
        21,                 // Vertical grid  offset in relation to GRAPH_DIV
        _AddData_Heartbeat, // Pointer to specific AddData function
        1                   // Number of waves
    },
    {
        "Sine Waves",
        265,
        253,
        23,
        _AddData_Sine,
        3
    }
};

/*********************************************************************
*
*       _ShowGraph
*/
static void _ShowGraph(GRAPH_Handle hGraph, GRAPH_DATA_Handle hData[], int DataCount, void (* pfAddData)(GRAPH_DATA_Handle hData, int DataID))
{
    int Data_xSize;
    int NextState;
    int TimeStart;
    int TimeDiff;
    int TimeStep;
    int Count;
    int xSize;
    int i;

    xSize      = LCD_GetXSize();
    Data_xSize = xSize - (DIST_TO_BORDER << 1) - (BORDER_LEFT + BORDER_RIGHT);
    Count      = 0;
    //
    // Attach data objects
    //
    for(i = 0; i < DataCount; i++)
    {
        GRAPH_AttachData(hGraph, hData[i]);
    }
    //
    // Add values before GRAPH is displayed
    //
    while(Count < Data_xSize)
    {
        for(i = 0; i < DataCount; i++)
        {
            pfAddData(hData[i], i);
        }
        Count++;
    }
    //
    // Add values depending on time
    //

    TimeStart = GUIDEMO_GetTime();
    do
    {
        TimeDiff = GUIDEMO_GetTime() - TimeStart;
        for(i = 0; i < DataCount; i++)
        {
            pfAddData(hData[i], i);
        }
        NextState = GUIDEMO_CheckCancel();
        TimeStep  = GUIDEMO_GetTime() - TimeStart;
        if((TimeStep - TimeDiff) < TIME_STEP)
        {
            GUI_Delay(TIME_STEP - (TimeStep - TimeDiff));
        }
    }
    while((TimeDiff < TIME_RUN) && (NextState == 0));
    for(i = 0; i < DataCount; i++)
    {
        GRAPH_DetachData(hGraph, hData[i]);
    }
    GUIDEMO_NotifyStartNext();
}

/*********************************************************************
*
*       _cbBk
*/
static void _cbBk(WM_MESSAGE * pMsg)
{
    switch(pMsg->MsgId)
    {
        case WM_PAINT:
            GUIDEMO_DrawBk();
            break;
        default:
            WM_DefaultProc(pMsg);
    }
}

/*********************************************************************
*
*       _GraphDemo
*/
static void _GraphDemo(void)
{
    const WIDGET_EFFECT * pEffectOld;
    GRAPH_DATA_Handle     hData[MAX_NUM_DATA_OBJ];
    GRAPH_Handle          hGraph;
    unsigned              i;
    int                   Graph_xSize;
    int                   Graph_ySize;
    int                   Graph_xPos;
    int                   Graph_yPos;
    int                   Data_ySize;
    int                   xSize;
    int                   ySize;

    xSize      = LCD_GetXSize();
    ySize      = LCD_GetYSize();
    pEffectOld = WIDGET_SetDefaultEffect(&WIDGET_Effect_Simple);
    //
    // Set Callback function for background window
    //
    WM_SetCallback(WM_HBKWIN, _cbBk);
    //
    // Determine size of GRAPH
    //
    Graph_xSize = xSize - (DIST_TO_BORDER << 1);            // xsize = Screen size subtracting twice the distance to the border of the screen
    Graph_ySize = ySize - INFO_SIZE_Y - (DIST_TO_WIN << 1); // ysize = Screen size subtracting the window sizes and twice the distance to the windows
    if(Graph_ySize > MAX_GRAPH_SIZE_Y)
    {
        Graph_ySize = MAX_GRAPH_SIZE_Y;
        if(Graph_xSize > (Graph_ySize * 20) / 11)
        {
            Graph_xSize = (Graph_ySize * 20) / 11;
        }
    }
    //
    // Create and configure GRAPH_WIDGET
    //
    Graph_xPos = (xSize - Graph_xSize) / 2;
    Graph_yPos = (ySize - Graph_ySize) / 2;
    if(Graph_yPos < INFO_SIZE_Y + DIST_TO_WIN)
    {
        Graph_yPos = INFO_SIZE_Y + DIST_TO_WIN;
    }
    hGraph = GRAPH_CreateEx(Graph_xPos, Graph_yPos, Graph_xSize, Graph_ySize, WM_HBKWIN, WM_CF_SHOW | WM_CF_CONST_OUTLINE, 0, 0);
    GRAPH_SetBorder(hGraph, BORDER_LEFT, BORDER_TOP, BORDER_RIGHT, BORDER_BOTTOM);
    WM_SetHasTrans(hGraph);
    GRAPH_SetColor(hGraph, COLOR_BK,     GRAPH_CI_BK);
    GRAPH_SetColor(hGraph, COLOR_BORDER, GRAPH_CI_BORDER);
    GRAPH_SetColor(hGraph, COLOR_FRAME,  GRAPH_CI_FRAME);
    GRAPH_SetColor(hGraph, COLOR_GRID,   GRAPH_CI_GRID);
    //
    // Adjust grid
    //
    GRAPH_SetGridVis(hGraph, 1);
    GRAPH_SetGridDistX(hGraph, GRID_DIST_X);
    GRAPH_SetGridDistY(hGraph, GRID_DIST_Y);
    WM_BringToBottom(hGraph);
    //
    // Create and configure GRAPH_DATA_YT object
    //
    for(i = 0; i < MAX_NUM_DATA_OBJ; i++)
    {
        hData[i] = GRAPH_DATA_YT_Create(_aColorData[i], xSize - (DIST_TO_BORDER << 1) - BORDER_LEFT, 0, 0);
    }
    Data_ySize = Graph_ySize - BORDER_BOTTOM;
    //
    // Create and configure GRAPH_SCALE objects
    //
    _hScaleH = GRAPH_SCALE_Create(BORDER_BOTTOM >> 1, GUI_TA_VCENTER, GRAPH_SCALE_CF_HORIZONTAL, TICK_DIST_H);
    _hScaleV = GRAPH_SCALE_Create(BORDER_LEFT   >> 1, GUI_TA_HCENTER, GRAPH_SCALE_CF_VERTICAL,   TICK_DIST_V);
    GRAPH_AttachScale(hGraph, _hScaleH);
    GRAPH_AttachScale(hGraph, _hScaleV);
    GRAPH_SCALE_SetPos(_hScaleH, Graph_ySize - SCALE_H_HEIGHT);
    GRAPH_SCALE_SetOff(_hScaleH, -5);
    //
    // Show some graphs
    //
    for(i = 0; i < GUI_COUNTOF(_aWave); i++)
    {
        GUIDEMO_SetInfoText(_aWave[i].pName);
        _DataAdjust = GUIDEMO_ShiftRight(Data_ySize * _aWave[i].DataVOff, GRAPH_DIV);
        GRAPH_SetGridOffY(hGraph, GUIDEMO_ShiftRight(Data_ySize * _aWave[i].GridVOff, GRAPH_DIV));
        GRAPH_SCALE_SetOff(_hScaleV, GUIDEMO_ShiftRight((Data_ySize - BORDER_BOTTOM) * _aWave[i].ScaleVOff, GRAPH_DIV));
        _ShowGraph(hGraph, hData, _aWave[i].NumWaves, _aWave[i].pfAddData);
    }
    //
    // Clean up
    //
    GRAPH_DetachScale(hGraph, _hScaleH);
    GRAPH_DetachScale(hGraph, _hScaleV);
    GRAPH_SCALE_Delete(_hScaleH);
    GRAPH_SCALE_Delete(_hScaleV);
    for(i = 0; i < MAX_NUM_DATA_OBJ; i++)
    {
        GRAPH_DATA_YT_Delete(hData[i]);
    }
    WM_DeleteWindow(hGraph);
    WIDGET_SetDefaultEffect(pEffectOld);
}

/*********************************************************************
*
*       Public functions
*
**********************************************************************
*/
void GUIDEMO_Graph(void)
{
    GUIDEMO_ConfigureDemo("Drawing a graph", "Optimized drawing routine\nfor drawing graph data", GUIDEMO_SHOW_CURSOR | GUIDEMO_SHOW_INFO | GUIDEMO_SHOW_CONTROL);
    _GraphDemo();
    GUIDEMO_NotifyStartNext();
}

#else

void GUIDEMO_Graph_C(void);
void GUIDEMO_Graph_C(void) {}

#endif  // SHOW_GUIDEMO_GRAPH && GUI_WINSUPPORT && GUI_SUPPORT_MEMDEV

/*************************** End of file ****************************/
