/*
 * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
 * 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
 *
 *     http://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.
 */

#include "components/ui_chart.h"
#include "engines/gfx/gfx_engine_manager.h"
#include "securec.h"

namespace OHOS {
UIChart::~UIChart()
{
    if (mixData_ != nullptr) {
        UIFree(mixData_);
        mixData_ = nullptr;
    }
    ClearDataSerial();
    Remove(&xAxis_);
    Remove(&yAxis_);
}

void UIChart::SetHeight(int16_t height)
{
    if (GetHeight() == height) {
        return;
    }

    if (height > 0) {
        needRefresh_ = true;
    }

    UIView::SetHeight(height);
    xAxis_.SetHeight(height);
    xAxis_.UpdateAxis();
    yAxis_.SetHeight(height);
    yAxis_.UpdateAxis();
}

void UIChart::SetWidth(int16_t width)
{
    UIView::SetWidth(width);
    xAxis_.SetWidth(width);
    yAxis_.SetWidth(width);
    xAxis_.UpdateAxis();
    yAxis_.UpdateAxis();
}

void UIChart::OnDraw(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
{
    UIViewGroup::OnDraw(gfxDstBuffer, invalidatedArea);
    DrawDataSerials(gfxDstBuffer, invalidatedArea);
}

bool UIChart::AddDataSerial(UIChartDataSerial* dataSerial)
{
    if (dataSerial == nullptr) {
        return false;
    }

    ListNode<UIChartDataSerial*>* serialNode = list_.Head();
    while (serialNode != list_.End()) {
        if (serialNode->data_ == dataSerial) {
            return false;
        }
        serialNode = serialNode->next_;
    }
    list_.PushBack(dataSerial);
    dataSerial->BindToChart(this);
    return true;
}

bool UIChart::DeleteDataSerial(UIChartDataSerial* dataSerial)
{
    if ((dataSerial == nullptr) || list_.IsEmpty()) {
        return false;
    }

    bool findSerial = false;
    ListNode<UIChartDataSerial*>* serialNode = list_.Head();
    while (serialNode != list_.End()) {
        if (serialNode->data_ == dataSerial) {
            dataSerial->BindToChart(nullptr);
            list_.Remove(serialNode);
            findSerial = true;
            break;
        }
        serialNode = serialNode->next_;
    }

    return findSerial;
}

void UIChart::ClearDataSerial()
{
    if (list_.IsEmpty()) {
        return;
    }

    ListNode<UIChartDataSerial*>* serialNode = list_.Head();
    while (serialNode != list_.End()) {
        serialNode->data_->BindToChart(nullptr);
        ListNode<UIChartDataSerial*>* tempNode = serialNode;
        serialNode = serialNode->next_;
        list_.Remove(tempNode);
    }
    list_.Clear();
}

UIChartDataSerial::UIChartDataSerial()
    : maxCount_(0),
      pointArray_(nullptr),
      serialColor_(Color::White()),
      fillColor_(Color::White()),
      dataCount_(0),
      peakPointIndex_(0),
      peakData_(0),
      valleyData_(0),
      valleyPointIndex_(0),
      lastPointIndex_(0),
      latestIndex_(0),
      hideIndex_(0),
      hideCount_(0),
      smooth_(false),
      enableGradient_(false),
      enableHeadPoint_(false),
      enableTopPoint_(false),
      enableBottomPoint_(false),
      chart_(nullptr),
      invalidateRect_(0, 0, 0, 0)
{
    PointStyle style;
    style.radius = DEFAULT_POINT_RADIUS;
    style.strokeWidth = 1;
    style.fillColor = Color::White();
    style.strokeColor = Color::White();
    topPointStyle_ = style;
    bottomPointStyle_ = style;
    headPointStyle_ = style;
}

bool UIChartDataSerial::SetMaxDataCount(uint16_t maxCount)
{
    if (maxCount > MAX_POINTS_COUNT) {
        maxCount = MAX_POINTS_COUNT;
    }

    if (maxCount == maxCount_) {
        return true;
    }

    if (pointArray_ != nullptr) {
        UIFree(pointArray_);
        pointArray_ = nullptr;
    }

    maxCount_ = maxCount;
    if (maxCount_ == 0) {
        return true;
    }

    pointArray_ = static_cast<Point*>(UIMalloc(sizeof(Point) * maxCount_));
    if (pointArray_ == nullptr) {
        maxCount_ = 0;
        return false;
    }
    return true;
}

bool UIChartDataSerial::ModifyPoint(uint16_t index, const Point& point)
{
    if ((index >= maxCount_) || (pointArray_ == nullptr)) {
        return false;
    }

    pointArray_[index].x = point.x;
    pointArray_[index].y = point.y;
    if (point.y > peakData_) {
        if (enableTopPoint_) {
            RefreshInvalidateRect(peakPointIndex_, topPointStyle_);
        }
        peakPointIndex_ = index;
        peakData_ = point.y;
    } else if (point.y < valleyData_) {
        if (enableBottomPoint_) {
            RefreshInvalidateRect(valleyPointIndex_, bottomPointStyle_);
        }
        valleyPointIndex_ = index;
        valleyData_ = point.y;
    } else if ((index == peakPointIndex_) || (index == valleyPointIndex_)) {
        UpdatePeakAndValley(0, dataCount_);
    }

    latestIndex_ = index;
    uint16_t startIndex = (index == 0) ? index : (index - 1);
    RefreshInvalidateRect(startIndex, index + 1);
    return true;
}

bool UIChartDataSerial::GetPoint(uint16_t index, Point& point)
{
    if ((index >= dataCount_) || (pointArray_ == nullptr)) {
        return false;
    }
    point = pointArray_[index];
    if (chart_ != nullptr) {
        chart_->GetXAxis().TranslateToPixel(point.x);
        chart_->GetYAxis().TranslateToPixel(point.y);
    }
    return true;
}

bool UIChartDataSerial::GetOriginalPoint(uint16_t index, Point& point)
{
    if ((index >= dataCount_) || (pointArray_ == nullptr)) {
        return false;
    }
    point = pointArray_[index];
    return true;
}

bool UIChartDataSerial::PointArrayDup(Point** pointArrayBack)
{
    *pointArrayBack = pointArray_;
    pointArray_ = static_cast<Point*>(UIMalloc(sizeof(Point) * maxCount_));
    return memcpy_s(pointArray_, dataCount_ * sizeof(Point), *pointArrayBack, dataCount_ * sizeof(Point)) != EOK;
}

void UIChartDataSerial::HidePoint(uint16_t index, uint16_t count)
{
    hideIndex_ = index;
    hideCount_ = count;
    RefreshInvalidateRect(hideIndex_, hideIndex_ + hideCount_);
}

void UIChartDataSerial::RefreshInvalidateRect(uint16_t pointIndex, const PointStyle& style)
{
    Point point;
    if (GetPoint(pointIndex, point)) {
        uint16_t width = style.radius + style.strokeWidth;
        Rect refresh(point.x - width, 0, point.x + width, 0);
        if ((invalidateRect_.GetLeft() == 0) && (invalidateRect_.GetRight() == 0)) {
            invalidateRect_ = refresh;
        } else {
            invalidateRect_.Join(invalidateRect_, refresh);
        }
    }
}

void UIChartDataSerial::RefreshInvalidateRect(uint16_t startIndex, uint16_t endIndex)
{
    Point start;
    GetPoint(startIndex, start);
    Point end;
    endIndex = (endIndex >= dataCount_) ? (dataCount_ - 1) : endIndex;
    GetPoint(endIndex, end);
    int16_t xMin = MATH_MIN(start.x, end.x);
    int16_t xMax = MATH_MAX(start.x, end.x);
    Rect refresh(xMin, 0, xMax, 0);
    if ((invalidateRect_.GetLeft() == 0) && (invalidateRect_.GetRight() == 0)) {
        invalidateRect_ = refresh;
        return;
    }
    invalidateRect_.Join(invalidateRect_, refresh);
}

bool UIChartDataSerial::UpdatePeakAndValley(uint16_t startPos, uint16_t endPos)
{
    if ((startPos >= endPos) || (endPos > dataCount_) || (pointArray_ == nullptr)) {
        return false;
    }

    if (startPos == 0) {
        peakData_ = pointArray_[startPos].y;
        valleyData_ = pointArray_[startPos].y;
    }

    for (uint16_t i = startPos; i < endPos; i++) {
        if (pointArray_[i].y > peakData_) {
            if (enableTopPoint_) {
                RefreshInvalidateRect(peakPointIndex_, topPointStyle_);
                RefreshInvalidateRect(i, topPointStyle_);
            }
            peakPointIndex_ = i;
            peakData_ = pointArray_[i].y;
        }

        if (pointArray_[i].y < valleyData_) {
            if (enableBottomPoint_) {
                RefreshInvalidateRect(valleyPointIndex_, bottomPointStyle_);
                RefreshInvalidateRect(i, bottomPointStyle_);
            }
            valleyPointIndex_ = i;
            valleyData_ = pointArray_[i].y;
        }
    }
    return true;
}

bool UIChartDataSerial::AddPoints(const Point* data, uint16_t count)
{
    if ((maxCount_ <= dataCount_) || (count == 0) || (pointArray_ == nullptr) || (data == nullptr)) {
        return false;
    }

    if (count > (maxCount_ - dataCount_)) {
        count = maxCount_ - dataCount_;
    }

    Point* current = pointArray_ + dataCount_;
    if (memcpy_s(current, (maxCount_ - dataCount_) * sizeof(Point), data, count * sizeof(Point)) != EOK) {
        return false;
    }
    uint16_t i = dataCount_;
    dataCount_ += count;
    UpdatePeakAndValley(i, dataCount_);
    latestIndex_ = dataCount_ - 1;
    uint16_t startIndex = (i == 0) ? i : (i - 1);
    RefreshInvalidateRect(startIndex, latestIndex_);
    return true;
}

void UIChartDataSerial::ClearData()
{
    RefreshInvalidateRect(0, dataCount_ - 1);
    if (pointArray_ != nullptr) {
        if (memset_s(pointArray_, maxCount_ * sizeof(Point), 0, maxCount_ * sizeof(Point)) != EOK) {
            return;
        }
    }
    dataCount_ = 0;
    valleyPointIndex_ = 0;
    peakPointIndex_ = 0;
    latestIndex_ = 0;
}

void UIChartDataSerial::DoDrawPoint(BufferInfo& gfxDstBuffer,
                                    const Point& center,
                                    const PointStyle& style,
                                    const Rect& mask)
{
    Style drawStyle = StyleDefault::GetDefaultStyle();
    drawStyle.lineOpa_ = OPA_OPAQUE;
    drawStyle.lineColor_ = style.fillColor;

    ArcInfo arcinfo = {{0}};
    arcinfo.center = center;
    arcinfo.imgPos = Point{0, 0};
    arcinfo.radius = style.radius + style.strokeWidth;
    arcinfo.startAngle = 0;
    arcinfo.endAngle = CIRCLE_IN_DEGREE;
    BaseGfxEngine* baseGfxEngine = BaseGfxEngine::GetInstance();
    if (style.fillColor.full == style.strokeColor.full) {
        drawStyle.lineWidth_ = style.radius + style.strokeWidth;
        baseGfxEngine->DrawArc(gfxDstBuffer, arcinfo, mask, drawStyle, OPA_OPAQUE, CapType::CAP_NONE);
        return;
    }
    drawStyle.lineWidth_ = style.radius;
    arcinfo.radius = style.radius;
    baseGfxEngine->DrawArc(gfxDstBuffer, arcinfo, mask, drawStyle, OPA_OPAQUE, CapType::CAP_NONE);

    drawStyle.lineWidth_ = style.strokeWidth;
    drawStyle.lineColor_ = style.strokeColor;
    arcinfo.radius = style.radius + style.strokeWidth;
    baseGfxEngine->DrawArc(gfxDstBuffer, arcinfo, mask, drawStyle, OPA_OPAQUE, CapType::CAP_NONE);
}

void UIChartDataSerial::DrawPoint(BufferInfo& gfxDstBuffer, const Rect& mask)
{
    Point center;
    if (enableTopPoint_) {
        if (GetPoint(peakPointIndex_, center)) {
            DoDrawPoint(gfxDstBuffer, center, topPointStyle_, mask);
        }
    }

    if (enableBottomPoint_) {
        if (GetPoint(valleyPointIndex_, center)) {
            DoDrawPoint(gfxDstBuffer, center, bottomPointStyle_, mask);
        }
    }

    if (enableHeadPoint_) {
        if (GetPoint(latestIndex_, center)) {
            DoDrawPoint(gfxDstBuffer, center, headPointStyle_, mask);
            lastPointIndex_ = latestIndex_;
        }
    }
}

void UIChartDataSerial::Refresh()
{
    if (chart_ != nullptr) {
        Rect refresh = chart_->GetContentRect();
        refresh.SetLeft(invalidateRect_.GetLeft() - headPointStyle_.radius - headPointStyle_.strokeWidth);
        refresh.SetRight(invalidateRect_.GetRight() + headPointStyle_.radius + headPointStyle_.strokeWidth);
        invalidateRect_.SetRect(0, 0, 0, 0);
        chart_->InvalidateRect(refresh);

        if (enableHeadPoint_ && (lastPointIndex_ != latestIndex_)) {
            RefreshInvalidateRect(lastPointIndex_, headPointStyle_);
            refresh.SetLeft(invalidateRect_.GetLeft());
            refresh.SetRight(invalidateRect_.GetRight());
            chart_->InvalidateRect(refresh);
            invalidateRect_.SetRect(0, 0, 0, 0);
        }
    }
}

void UIChartPillar::RefreshChart()
{
    ListNode<UIChartDataSerial*>* iter = list_.Begin();
    Rect rect = GetContentRect();
    for (; iter != list_.End(); iter = iter->next_) {
        UIChartDataSerial* data = iter->data_;
        if (data == nullptr) {
            break;
        }
        uint16_t dataCount = data->GetDataCount();
        if (dataCount <= 1) {
            break;
        }

        uint16_t index = data->GetLastPointIndex();
        if (index >= dataCount) {
            break;
        }

        Point current;
        data->GetPoint(index, current);
        Point last;
        data->GetPoint(dataCount - 1, last);
        Rect refresh(current.x, rect.GetTop(), last.x, rect.GetBottom());
        InvalidateRect(refresh);
        data->SetLastPointIndex(dataCount - 1);
    }
}

void UIChartPillar::DrawDataSerials(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
{
    xAxis_.UpdateAxisPoints();
    yAxis_.UpdateAxisPoints();
    uint16_t minXStep = static_cast<uint16_t>(xAxis_.GetMarkInterval());
    Point xStart = xAxis_.GetStartPoint();
    uint16_t dataSerialCount = list_.Size();
    if (dataSerialCount == 0) {
        return;
    }
    uint16_t width = minXStep / dataSerialCount;
    uint8_t dataSerialIndex = 0;
    uint16_t barWidth = static_cast<uint16_t>(width - DEFAULT_MARK_PERCENTAGE * (width << 1));

    for (ListNode<UIChartDataSerial*>* iter = list_.Begin(); iter != list_.End(); iter = iter->next_) {
        UIChartDataSerial* data = iter->data_;
        uint16_t dataSerialWidth = width * dataSerialIndex;
        int16_t x = dataSerialWidth + (width >> 1);
        for (uint16_t index = 0; index < data->GetDataCount(); index++) {
            Point current;
            data->GetPoint(index, current);
            if (current.y == xStart.y) {
                continue;
            }
            current.x += x;
            xStart.x = current.x;
            BaseGfxEngine::GetInstance()->DrawLine(gfxDstBuffer, current, xStart, invalidatedArea, barWidth,
                                                   data->GetFillColor(), style_->lineOpa_);
        }
        dataSerialIndex++;
    }
}

void UIChartPolyline::RefreshChart()
{
    ListNode<UIChartDataSerial*>* iter = list_.Begin();
    for (; iter != list_.End(); iter = iter->next_) {
        UIChartDataSerial* data = iter->data_;
        uint16_t dataCount = data->GetDataCount();
        if (dataCount == 1) {
            break;
        }
        data->Refresh();
    }
}

void UIChartPolyline::ReMeasure()
{
    if (!needRefresh_) {
        return;
    }
    needRefresh_ = false;
    int16_t height = GetHeight();
    if (mixData_ != nullptr) {
        UIFree(mixData_);
        mixData_ = nullptr;
    }
    if (height <= 0) {
        return;
    }
    if (height > COORD_MAX) {
        height = COORD_MAX;
    }
    mixData_ = static_cast<uint8_t*>(UIMalloc(height));
    if (mixData_ == nullptr) {
        return;
    }
    int16_t opa = maxOpa_ - minOpa_;
    for (int16_t y = 0; y < height; y++) {
        mixData_[y] = static_cast<uint8_t>(y * opa / height + minOpa_);
    }
}

void UIChartPolyline::DrawDataSerials(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
{
    xAxis_.UpdateAxisPoints();
    yAxis_.UpdateAxisPoints();
    ListNode<UIChartDataSerial*>* iter = list_.Begin();
    for (; iter != list_.End(); iter = iter->next_) {
        UIChartDataSerial* data = iter->data_;
        uint16_t dataCount = data->GetDataCount();
        if (dataCount <= 1) {
            continue;
        }
        if (data->IsGradient()) {
            if (data->IsSmooth()) {
                Point* pointArrayBack = nullptr;
                data->PointArrayDup(&pointArrayBack);
                GetDataBySmooth(0, dataCount - 1, data);
                GradientColor(gfxDstBuffer, invalidatedArea, data);
                data->ClearData();
                data->AddPoints(pointArrayBack, dataCount);
                UIFree(pointArrayBack);
            } else {
                GradientColor(gfxDstBuffer, invalidatedArea, data);
            }
        }
        if (data->GetHideCount() != 0) {
            uint16_t hideIndex = data->GetHideIndex();
            DrawPolyLine(gfxDstBuffer, 0, hideIndex, invalidatedArea, data);
            DrawPolyLine(gfxDstBuffer, hideIndex + data->GetHideCount(), dataCount - 1, invalidatedArea, data);
        } else {
            DrawPolyLine(gfxDstBuffer, 0, dataCount - 1, invalidatedArea, data);
        }

        data->DrawPoint(gfxDstBuffer, invalidatedArea);
    }
}

bool UIChartPolyline::Smooth(uint16_t startPos,
                             Point& start,
                             Point& end,
                             Point& current,
                             UIChartDataSerial* data,
                             uint16_t& slope,
                             uint16_t& preSlope)
{
    data->GetPoint(startPos + 1, current);
    if (((end.y - start.y <= 0) && (current.y - end.y <= 0)) || ((end.y - start.y >= 0) && (current.y - end.y >= 0))) {
        slope = (current.x == start.x) ? QUARTER_IN_DEGREE : FastAtan2(current.x - start.x, current.y - start.y);
        if (MATH_ABS(slope - preSlope) < SMOOTH_SLOPE_ANGLE) {
            end = current;
            return false;
        }
    }
    preSlope = (current.x == end.x) ? QUARTER_IN_DEGREE : FastAtan2(current.x - end.x, current.y - end.y);
    return true;
}

void UIChartPolyline::GetDataBySmooth(uint16_t startIndex, uint16_t endIndex, UIChartDataSerial* data)
{
    if (data == nullptr) {
        return;
    }
    Point* pointArray = static_cast<Point*>(UIMalloc(sizeof(Point) * data->GetDataCount()));
    Point start;
    Point end;

    uint16_t slope;
    data->GetPoint(startIndex, start);
    data->GetOriginalPoint(startIndex, pointArray[0]);
    int count = 1;
    data->GetPoint(startIndex + 1, end);
    uint16_t preSlope = (start.x == end.x) ? QUARTER_IN_DEGREE : FastAtan2(end.x - start.x, end.y - start.y);
    Point current;
    for (uint16_t i = startIndex; i < endIndex; i++) {
        if (!Smooth(i, start, end, current, data, slope, preSlope)) {
            continue;
        }
        data->GetOriginalPoint(i, pointArray[count++]);
        start = end;
        end = current;
    }
    data->GetOriginalPoint(endIndex, pointArray[count++]);
    data->ClearData();
    data->AddPoints(pointArray, count);
    UIFree(pointArray);
}

void UIChartPolyline::DrawSmoothPolyLine(BufferInfo& gfxDstBuffer,
                                         uint16_t startIndex,
                                         uint16_t endIndex,
                                         const Rect& invalidatedArea,
                                         UIChartDataSerial* data)
{
    if (data == nullptr) {
        return;
    }
    Point start;
    Point end;
    ColorType color = data->GetLineColor();
    Style style = *style_;
    style.lineColor_ = color;
    style.lineOpa_ = OPA_OPAQUE;
    uint16_t slope;
    data->GetPoint(startIndex, start);
    data->GetPoint(startIndex + 1, end);
    uint16_t preSlope = (start.x == end.x) ? QUARTER_IN_DEGREE : FastAtan2(end.x - start.x, end.y - start.y);
    Point current;
    BaseGfxEngine* baseGfxEngine = BaseGfxEngine::GetInstance();
    for (uint16_t i = startIndex; i < endIndex; i++) {
        if (!Smooth(i, start, end, current, data, slope, preSlope)) {
            continue;
        }
        Rect rect;
        rect.SetLeft(MATH_MIN(start.x, end.x) - style_->lineWidth_);
        rect.SetRight(MATH_MAX(start.x, end.x) + style_->lineWidth_);
        rect.SetTop(MATH_MIN(start.y, end.y) - style_->lineWidth_);
        rect.SetBottom(MATH_MAX(start.y, end.y) + style_->lineWidth_);
        if (!invalidatedArea.IsIntersect(rect)) {
            start = end;
            end = current;
            continue;
        }
        baseGfxEngine->DrawLine(gfxDstBuffer, start, end, invalidatedArea, style_->lineWidth_, color, OPA_OPAQUE);
        ArcInfo arcinfo = {{0}};
        arcinfo.center = end;
        arcinfo.imgPos = Point{0, 0};
        arcinfo.radius = (style_->lineWidth_ + 1) >> 1;
        arcinfo.startAngle = 0;
        arcinfo.endAngle = CIRCLE_IN_DEGREE;

        baseGfxEngine->DrawArc(gfxDstBuffer, arcinfo, invalidatedArea, style, OPA_OPAQUE, CapType::CAP_NONE);
        start = end;
        end = current;
    }
    baseGfxEngine->DrawLine(gfxDstBuffer, start, end, invalidatedArea, style_->lineWidth_, color, OPA_OPAQUE);
}

void UIChartPolyline::DrawPolyLine(BufferInfo& gfxDstBuffer,
                                   uint16_t startIndex,
                                   uint16_t endIndex,
                                   const Rect& invalidatedArea,
                                   UIChartDataSerial* data)
{
    if ((startIndex >= endIndex) || (data == nullptr)) {
        return;
    }

    if (data->IsSmooth()) {
        DrawSmoothPolyLine(gfxDstBuffer, startIndex, endIndex, invalidatedArea, data);
        return;
    }
    Point start;
    Point end;
    ColorType color = data->GetLineColor();
    Style style = *style_;
    style.lineColor_ = color;
    style.lineOpa_ = OPA_OPAQUE;
    ArcInfo arcinfo = {{0}};
    arcinfo.imgPos = Point{0, 0};
    arcinfo.radius = (style_->lineWidth_ + 1) >> 1;
    arcinfo.startAngle = 0;
    arcinfo.endAngle = CIRCLE_IN_DEGREE;
    BaseGfxEngine* baseGfxEngine = BaseGfxEngine::GetInstance();
    for (uint16_t i = startIndex; i < endIndex - 1; i++) {
        data->GetPoint(i, start);
        data->GetPoint(i + 1, end);
        Rect rect;
        rect.SetLeft(MATH_MIN(start.x, end.x) - style_->lineWidth_);
        rect.SetRight(MATH_MAX(start.x, end.x) + style_->lineWidth_);
        rect.SetTop(MATH_MIN(start.y, end.y) - style_->lineWidth_);
        rect.SetBottom(MATH_MAX(start.y, end.y) + style_->lineWidth_);
        if (!invalidatedArea.IsIntersect(rect)) {
            continue;
        }

        baseGfxEngine->DrawLine(gfxDstBuffer, start, end, invalidatedArea, style_->lineWidth_, color, OPA_OPAQUE);
        if (style_->lineWidth_ >= LINE_JOIN_WIDTH) {
            arcinfo.center = end;
            baseGfxEngine->DrawArc(gfxDstBuffer, arcinfo, invalidatedArea, style, OPA_OPAQUE, CapType::CAP_NONE);
        }
    }
    data->GetPoint(endIndex - 1, start);
    data->GetPoint(endIndex, end);
    baseGfxEngine->DrawLine(gfxDstBuffer, start, end, invalidatedArea, style_->lineWidth_, color, OPA_OPAQUE);
}

bool UIChartPolyline::GetLineCrossPoint(const Point& p1,
                                        const Point& p2,
                                        const Point& p3,
                                        const Point& p4,
                                        Point& cross)
{
    /* Rectangular ranges of line segments must intersect. */
    if ((MATH_MIN(p1.x, p2.x) <= MATH_MAX(p3.x, p4.x)) && (MATH_MIN(p3.x, p4.x) <= MATH_MAX(p1.x, p2.x)) &&
        (MATH_MIN(p1.y, p2.y) <= MATH_MAX(p3.y, p4.y)) && (MATH_MIN(p3.y, p4.y) <= MATH_MAX(p1.y, p2.y))) {
        /* Check whether the lines are parallel. If the lines are collinear, there is no intersection point. */
        if ((p4.y - p3.y) * (p2.x - p1.x) - (p4.x - p3.x) * (p2.y - p1.y) != 0) {
            /*
             * (y1 - y2)x + (x2 - x1)y = x2y1 - x1y2  ->  ax + by = c
             * (y3 - y4)x + (x4 - x3)y = x4y3 - x3y4  ->  dx + ey = f
             */
            int64_t a = p1.y - p2.y;
            int64_t b = p2.x - p1.x;
            int64_t c = p2.x * p1.y - p1.x * p2.y;
            int64_t d = p3.y - p4.y;
            int64_t e = p4.x - p3.x;
            int64_t f = p4.x * p3.y - p3.x * p4.y;
            int64_t left = a * e - b * d;
            int64_t right = c * e - b * f;
            if (left == 0) {
                return false;
            }
            cross.x = static_cast<int16_t>(right / left);
            left = b * d - a * e;
            right = c * d - a * f;
            if (left == 0) {
                return false;
            }
            cross.y = static_cast<int16_t>(right / left);
            if ((cross.x >= MATH_MIN(p1.x, p2.x)) && (cross.x <= MATH_MAX(p1.x, p2.x)) &&
                (cross.x >= MATH_MIN(p3.x, p4.x)) && (cross.x <= MATH_MAX(p3.x, p4.x))) {
                return true;
            }
        }
    }
    if ((MATH_MIN(p1.x, p2.x) <= MATH_MAX(p3.x, p4.x)) && (MATH_MIN(p3.x, p4.x) <= MATH_MAX(p1.x, p2.x)) &&
        (MATH_MIN(p1.y, p2.y) >= MATH_MAX(p3.y, p4.y)) && (MATH_MIN(p3.y, p4.y) <= MATH_MAX(p1.y, p2.y))) {
        return enableReverse_ ? true : false;
    }
    return false;
}

void UIChartPolyline::FindCrossPoints(const ChartLine& line, const ChartLine& polyLine, CrossPointSet& cross)
{
    if (GetLineCrossPoint(line.start, line.end, polyLine.start, polyLine.end, cross.nextFirst)) {
        if (enableReverse_ && (MATH_MIN(line.start.y, line.end.y) >= MATH_MAX(polyLine.start.y, polyLine.end.y))) {
            if (!cross.firstFind) {
                if (polyLine.start.y < polyLine.end.y) {
                    cross.first = cross.nextFirst;
                    cross.firstFind = false;
                }
            } else if (!cross.secondFind) {
                if ((cross.first.x != cross.nextFirst.x) || (cross.first.y != cross.nextFirst.y)) {
                    cross.second = cross.nextFirst;
                    cross.secondFind = true;
                    return;
                }
                if (polyLine.start.y > polyLine.end.y) {
                    cross.firstFind = true;
                }
            }
            return;
        }
        if (!cross.firstFind) {
            /* first corss must on the line like "/" */
            if (polyLine.start.y < polyLine.end.y) {
                cross.first = cross.nextFirst;
                cross.firstFind = true;
            }
        } else if (!cross.secondFind) {
            /* second corss can't be same with first cross. */
            if ((cross.first.x != cross.nextFirst.x) || (cross.first.y != cross.nextFirst.y)) {
                cross.second = cross.nextFirst;
                cross.secondFind = true;
                return;
            }
            /* second corss must on the line like "\", otherwise skip those crosss. */
            if (polyLine.start.y > polyLine.end.y) {
                cross.firstFind = false;
            }
        }
    }
}

void UIChartPolyline::DrawGradientColor(BufferInfo& gfxDstBuffer,
                                        const Rect& invalidatedArea,
                                        UIChartDataSerial* data,
                                        const ChartLine& linePoints,
                                        const ChartLine& limitPoints,
                                        int16_t startY)
{
    if (data == nullptr) {
        return;
    }
    Rect currentRect = GetContentRect();
    CrossPointSet cross = {{0}};
    ChartLine polyLine = {{0}};
    uint16_t pointCount = data->GetDataCount() - 1;
    int16_t y = enableReverse_ ? (linePoints.start.y + startY) : (startY - linePoints.start.y);
    int16_t mixScale = !enableReverse_ ? (currentRect.GetBottom() - y) : (y - currentRect.GetTop());
    if ((mixScale < 0) || (mixScale >= currentRect.GetHeight())) {
        return;
    }
    bool onVerticalLine = enableReverse_ ? (y <= limitPoints.start.y) : (y >= limitPoints.start.y);
    if (onVerticalLine) {
        cross.first.x = limitPoints.start.x;
        cross.first.y = enableReverse_ ? (y - startY) : (startY - y);
        cross.firstFind = true;
    }
    Point start;
    Point end;
    BaseGfxEngine* baseGfxEngine = BaseGfxEngine::GetInstance();
    for (uint16_t i = 0; i < pointCount; i++) {
        data->GetPoint(i, start);
        data->GetPoint(i + 1, end);
        if (start.y == end.y) {
            int16_t tmpY = enableReverse_ ? (start.y + startY) : (startY - start.y);
            if (tmpY == linePoints.start.y) {
                cross.firstFind = false;
                cross.secondFind = false;
            }
            continue;
        }
        start.y = enableReverse_ ? (start.y - startY) : (startY - start.y);
        end.y = enableReverse_ ? (end.y - startY) : (startY - end.y);
        polyLine = {start, end};
        FindCrossPoints(linePoints, polyLine, cross);
        SetDrawLineCross(gfxDstBuffer, invalidatedArea, data, cross, baseGfxEngine, startY, mixScale);
    }
    if (cross.firstFind && !cross.secondFind) {
        cross.second = {limitPoints.end.x, y};
        cross.first.y = y;
        baseGfxEngine->DrawLine(gfxDstBuffer, cross.first, cross.second, invalidatedArea, 1, data->GetFillColor(),
                                mixData_[mixScale]);
    }
}

void UIChartPolyline::SetDrawLineCross(BufferInfo& gfxDstBuffer,
                                       const Rect& invalidatedArea,
                                       UIChartDataSerial* data,
                                       CrossPointSet& cross,
                                       BaseGfxEngine* baseGfxEngine,
                                       int16_t startY,
                                       int16_t mixScale)
{
    if (cross.firstFind && cross.secondFind) {
        cross.first.y = enableReverse_ ? (cross.first.y + startY) : (startY - cross.first.y);
        cross.second.y = enableReverse_ ? (cross.second.y + startY) : (startY - cross.second.y);
        baseGfxEngine->DrawLine(gfxDstBuffer, cross.first, cross.second, invalidatedArea, 1, data->GetFillColor(),
                                mixData_[mixScale]);
        cross.firstFind = false;
        cross.secondFind = false;
    }
}

void UIChartPolyline::CalcVerticalInfo(int16_t top,
                                       int16_t bottom,
                                       int16_t start,
                                       int16_t end,
                                       int16_t& y,
                                       int16_t& yHeight)
{
    if ((top < start) && (bottom > start)) {
        y = start;
        yHeight = top;
    } else if ((bottom <= start) && (top >= end)) {
        y = bottom;
        yHeight = top;
    } else if ((top < end) && (bottom > end)) {
        y = bottom;
        yHeight = end;
    }
}

void UIChartPolyline::GradientColor(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea, UIChartDataSerial* data)
{
    if (data == nullptr) {
        return;
    }
    int16_t bottom = invalidatedArea.GetBottom();
    int16_t top = invalidatedArea.GetTop();
    Point yStart = yAxis_.GetStartPoint();
    yStart.y = enableReverse_ ? (yStart.y + gradientBottom_) : (yStart.y - gradientBottom_);
    int16_t topY = enableReverse_ ? data->GetValleyData() : data->GetPeakData();
    int16_t bottomY = enableReverse_ ? data->GetPeakData() : data->GetValleyData();
    yAxis_.TranslateToPixel(topY);
    yAxis_.TranslateToPixel(bottomY);
    int16_t valleyY = enableReverse_ ? topY : bottomY;
    int16_t startY = enableReverse_ ? topY : yStart.y;
    int16_t endY = enableReverse_ ? yStart.y : topY;
    if ((bottom < endY) || (top > startY)) {
        return;
    }

    int16_t y = 0;
    int16_t yHeight = 0;
    CalcVerticalInfo(top, bottom, startY, endY, y, yHeight);

    ChartLine limitPoints = {{0}};
    data->GetPoint(0, limitPoints.start);
    data->GetPoint(data->GetDataCount() - 1, limitPoints.end);
    ChartLine linePoints = {{0}};
    linePoints.start.x = limitPoints.start.x;
    linePoints.end.x = limitPoints.end.x;
    Rect currentRect = GetContentRect();
    BaseGfxEngine* baseGfxEngine = BaseGfxEngine::GetInstance();
    while (y >= yHeight) {
        linePoints.start.y = enableReverse_ ? (y - endY) : (startY - y);
        linePoints.end.y = linePoints.start.y;
        if (y <= valleyY) {
            int16_t baseY = enableReverse_ ? endY : startY;
            DrawGradientColor(gfxDstBuffer, invalidatedArea, data, linePoints, limitPoints, baseY);
        } else {
            int16_t mixScale = enableReverse_ ? (linePoints.start.y + endY - currentRect.GetTop()) :
                                                (currentRect.GetBottom() - (startY - linePoints.start.y));
            if ((mixScale < 0) || (mixScale >= currentRect.GetHeight())) {
                y--;
                continue;
            }
            Point start = {limitPoints.start.x, y};
            Point end = {limitPoints.end.x, y};
            baseGfxEngine->DrawLine(gfxDstBuffer, start, end, invalidatedArea, 1, data->GetFillColor(),
                                    mixData_[mixScale]);
        }
        y--;
    }
}
} // namespace OHOS