/*
 * Copyright (c) 2009-2022 Arm Limited. All rights reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the License); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*============================ INCLUDES ======================================*/

#define __NUMBER_LIST_IMPLEMENT__
#define __ARM_2D_HELPER_LIST_VIEW_INHERIT__

#include "arm_extra_controls.h"
#include "number_list.h"

#include "__common.h"
#include "arm_2d.h"
#include "arm_2d_helper.h"
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>

#if defined(__clang__)
#   pragma clang diagnostic push
#   pragma clang diagnostic ignored "-Wunknown-warning-option"
#   pragma clang diagnostic ignored "-Wreserved-identifier"
#   pragma clang diagnostic ignored "-Wdeclaration-after-statement"
#   pragma clang diagnostic ignored "-Wsign-conversion"
#   pragma clang diagnostic ignored "-Wpadded"
#   pragma clang diagnostic ignored "-Wcast-qual"
#   pragma clang diagnostic ignored "-Wcast-align"
#   pragma clang diagnostic ignored "-Wmissing-field-initializers"
#   pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
#   pragma clang diagnostic ignored "-Wmissing-braces"
#   pragma clang diagnostic ignored "-Wunused-const-variable"
#   pragma clang diagnostic ignored "-Wmissing-declarations"
#   pragma clang diagnostic ignored "-Wmissing-variable-declarations"
#   pragma clang diagnostic ignored "-Wformat-nonliteral"
#   pragma clang diagnostic ignored "-Wsign-compare"
#   pragma clang diagnostic ignored "-Wcovered-switch-default"
#endif

/*============================ MACROS ========================================*/

#if __GLCD_CFG_COLOUR_DEPTH__ == 8


#elif __GLCD_CFG_COLOUR_DEPTH__ == 16


#elif __GLCD_CFG_COLOUR_DEPTH__ == 32

#else
#   error Unsupported colour depth!
#endif


#undef this
#define this        (*ptThis)

/*============================ MACROFIED FUNCTIONS ===========================*/
/*============================ TYPES =========================================*/
/*============================ GLOBAL VARIABLES ==============================*/
/*============================ PROTOTYPES ====================================*/
/*============================ LOCAL VARIABLES ===============================*/
/*============================ IMPLEMENTATION ================================*/


static 
IMPL_ON_DRAW_EVT(__arm_2d_number_list_draw_background)
{
    ARM_2D_UNUSED(bIsNewFrame);
    
    number_list_t *ptThis = (number_list_t *)pTarget;


    if (!this.tNumListCFG.bIgnoreBackground) {
        arm_2d_fill_colour_with_opacity(
                            ptTile, 
                            NULL, 
                            (__arm_2d_color_t) {this.tNumListCFG.tBackgroundColour},
                            this.tNumListCFG.chOpacity);
    }
    
    arm_2d_op_wait_async(NULL);
    
    return arm_fsm_rt_cpl;
}


#if defined(__IS_COMPILER_IAR__) && __IS_COMPILER_IAR__
#define __va_list    va_list

#endif



static 
int __printf(number_list_t *ptThis, const arm_2d_region_t *ptRegion, const char *format, ...)
{
    int real_size;
    static char s_chBuffer[__LCD_PRINTF_CFG_TEXT_BUFFER_SIZE__ + 1];
    __va_list ap;
    va_start(ap, format);
        real_size = vsnprintf(s_chBuffer, sizeof(s_chBuffer)-1, format, ap);
    va_end(ap);
    real_size = MIN(sizeof(s_chBuffer)-1, real_size);
    s_chBuffer[real_size] = '\0';
    
    int16_t iWidth = this.tNumListCFG.ptFont->tCharSize.iWidth;
    int16_t iHeight = this.tNumListCFG.ptFont->tCharSize.iHeight;

    arm_2d_align_centre( *ptRegion, (int16_t)real_size * iWidth, iHeight) {
        arm_lcd_text_set_draw_region(&__centre_region);
        arm_lcd_puts(s_chBuffer);
        arm_lcd_text_set_draw_region(NULL);
    }

    return real_size;
}

static 
arm_fsm_rt_t __arm_2d_number_list_draw_list_core_item( 
                                      arm_2d_list_item_t *ptItem,
                                      const arm_2d_tile_t *ptTile,
                                      bool bIsNewFrame,
                                      arm_2d_list_item_param_t *ptParam)
{
    number_list_t *ptThis = (number_list_t *)ptItem->ptListView;
    ARM_2D_UNUSED(ptItem);
    ARM_2D_UNUSED(bIsNewFrame);
    ARM_2D_UNUSED(ptTile);
    ARM_2D_UNUSED(ptParam);

    arm_lcd_text_set_font(this.tNumListCFG.ptFont);

    arm_lcd_text_set_colour(this.tNumListCFG.tFontColour, this.tNumListCFG.tBackgroundColour);
    arm_lcd_text_set_display_mode(ARM_2D_DRW_PATN_MODE_COPY);
    
    arm_lcd_text_set_target_framebuffer((arm_2d_tile_t *)ptTile);
    
    arm_2d_canvas(ptTile, __top_container) {
        /* print numbers */
        __printf(ptThis,
                 &__top_container,
                 this.tNumListCFG.pchFormatString,
                 this.tNumListCFG.nStart + ptItem->hwID * this.tNumListCFG.iDelta);
    }
    arm_lcd_text_set_target_framebuffer(NULL);
    
    return arm_fsm_rt_cpl;
}

static arm_2d_list_item_t *__arm_2d_number_list_iterator(
                                        __arm_2d_list_core_t *ptListView,
                                        arm_2d_list_iterator_dir_t tDirection,
                                        uint_fast16_t hwID
                                    )
{
    number_list_t *ptThis = (number_list_t *)ptListView;
    int32_t nIterationIndex;
    switch (tDirection) {
        default:
        case __ARM_2D_LIST_GET_ITEM_WITH_ID_WITHOUT_MOVE_POINTER:
            nIterationIndex = hwID;
            nIterationIndex %= this.tNumListCFG.hwCount;
            break;

        case __ARM_2D_LIST_GET_ITEM_AND_MOVE_POINTER:
            this.nIterationIndex = hwID;
            this.nIterationIndex %= this.tNumListCFG.hwCount;
            nIterationIndex = this.nIterationIndex;
            break;

        case __ARM_2D_LIST_GET_PREVIOUS:
            if (this.nIterationIndex) {
                this.nIterationIndex--;
            } else {
                this.nIterationIndex = this.tNumListCFG.hwCount - 1;
            }
            nIterationIndex = this.nIterationIndex;
            break;

        case __ARM_2D_LIST_GET_NEXT:
            this.nIterationIndex++;
            this.nIterationIndex %= this.tNumListCFG.hwCount;
            
            nIterationIndex = this.nIterationIndex;
            break;

        case __ARM_2D_LIST_GET_FIRST_ITEM_WITHOUT_MOVE_POINTER:
            nIterationIndex = 0;
            break;

        case __ARM_2D_LIST_GET_FIRST_ITEM:
            this.nIterationIndex = 0;
            nIterationIndex = this.nIterationIndex;
            break;

        case __ARM_2D_LIST_GET_CURRENT:
            nIterationIndex = this.nIterationIndex;
            break;

        case __ARM_2D_LIST_GET_LAST_ITEM_WITHOUT_MOVE_POINTER:
            nIterationIndex = this.tNumListCFG.hwCount - 1;
            break;

        case __ARM_2D_LIST_GET_LAST_ITEM:
            this.nIterationIndex = this.tNumListCFG.hwCount - 1;
            nIterationIndex = this.nIterationIndex;
            break;
    }

    /* validate item size */
    if (this.tTempItem.tSize.iHeight <= 0) {
        this.tTempItem.tSize.iHeight = this.tNumListCFG.ptFont->tCharSize.iHeight;
    }
    if (this.tTempItem.tSize.iWidth <= 0) {
        this.tTempItem.tSize.iWidth 
            = this.use_as____arm_2d_list_core_t
                .Runtime.tileList.tRegion.tSize.iWidth;
    }

    nIterationIndex %= this.tNumListCFG.hwCount;

    /* update item id : pretend that this is a different list core item */
    this.tTempItem.hwID = (uint16_t)nIterationIndex;

    return &this.tTempItem;
}


ARM_NONNULL(1,2)
void number_list_init(  number_list_t *ptThis, 
                        number_list_cfg_t *ptCFG)
{
    assert(NULL != ptThis);
    assert(NULL != ptCFG);
    assert(ptCFG->hwCount > 0);

    static const char c_chDefaultFormatString[] = {"%d"};


    int16_t iItemHeight = ptCFG->chPrviousePadding + ptCFG->chNextPadding;

    if (NULL != ptCFG->ptFont) {
        iItemHeight += ptCFG->ptFont->tCharSize.iHeight;
    } else {
        iItemHeight += 8;
    }

    /* call base class contructor */
    do {
        __arm_2d_list_core_cfg_t tCFG = {
            .fnIterator = &__arm_2d_number_list_iterator,
            
            /* vertical list, centre aligned style */
            .fnCalculator = &ARM_2D_LIST_CALCULATOR_MIDDLE_ALIGNED_FIXED_SIZED_ITEM_NO_STATUS_CHECK_VERTICAL,
            .fnOnDrawListBackground = &__arm_2d_number_list_draw_background,
            //.fnOnDrawListItemBackground =       &__arm_2d_number_list_draw_list_core_item_background,
            .hwSwitchingPeriodInMs = ptCFG->hwSwitchingPeriodInMs,
            .hwItemCount = ptCFG->hwCount,
            .nTotalLength = ptCFG->hwCount * iItemHeight,
            .tListSize = ptCFG->tListSize,
        };

        /* you can override the default implementations */
        if (NULL != ptCFG->fnOnDrawListBackground) {
            tCFG.fnOnDrawListBackground = ptCFG->fnOnDrawListBackground;
        }
        if (NULL != ptCFG->fnOnDrawListCover) {
            tCFG.fnOnDrawListCover = ptCFG->fnOnDrawListCover;
        }
        if (NULL != ptCFG->fnOnDrawListItemBackground) {
            tCFG.fnOnDrawListItemBackground = ptCFG->fnOnDrawListItemBackground;
        }

        __arm_2d_list_core_init(&this.use_as____arm_2d_list_core_t, &tCFG);
    } while(0);

    this.tNumListCFG = *ptCFG;
    
    if (NULL == this.tNumListCFG.ptFont) {
        this.tNumListCFG.ptFont = (arm_2d_font_t *)&ARM_2D_FONT_6x8;
    }
    
    /* validation */
    if (!this.tNumListCFG.hwCount) {
        this.tNumListCFG.hwCount = 1;      /* at least one item */
    }

    if (NULL == this.tNumListCFG.pchFormatString) {
        /* use the default format string */
        this.tNumListCFG.pchFormatString = c_chDefaultFormatString;
    }
    
    if (0 == this.tNumListCFG.chOpacity) {
        this.tNumListCFG.chOpacity = 255;
    }
    
    /* update temp item */
    memset(&this.tTempItem, 0, sizeof(this.tTempItem));
    
    this.tTempItem.bIsEnabled = true;
    this.tTempItem.bIsVisible = true;
    this.tTempItem.u4Alignment = ARM_2D_ALIGN_CENTRE;
    
    this.tTempItem.Padding.chPrevious = ptCFG->chPrviousePadding;
    this.tTempItem.Padding.chNext = ptCFG->chNextPadding;
    this.tTempItem.tSize = ptCFG->tItemSize;

    this.tTempItem.fnOnDrawItem = &__arm_2d_number_list_draw_list_core_item;
    
    /* request updating StartOffset */
    //this.use_as____arm_2d_list_core_t.CalMidAligned.bListHeightChanged = true;
}

ARM_NONNULL(1,2)
arm_fsm_rt_t number_list_show(  number_list_t *ptThis,
                                const arm_2d_tile_t *ptTile, 
                                const arm_2d_region_t *ptRegion, 
                                bool bIsNewFrame)
{

    return __arm_2d_list_core_show( &this.use_as____arm_2d_list_core_t,
                                    ptTile,
                                    ptRegion,
                                    bIsNewFrame);
}


ARM_NONNULL(1)
void numer_list_move_selection( number_list_t *ptThis,
                                        int16_t iSteps,
                                        int32_t nFinishInMs)
{
    __arm_2d_list_core_move_request( &this.use_as____arm_2d_list_core_t,
                                             iSteps,
                                             nFinishInMs);
}

#if defined(__clang__)
#   pragma clang diagnostic pop
#endif