/*
 * 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_scroll_view.h"

#include "components/ui_abstract_scroll_bar.h"
#include "dock/focus_manager.h"
#include "dock/vibrator_manager.h"
#include "draw/draw_rect.h"
#include "gfx_utils/graphic_log.h"

namespace OHOS {
UIScrollView::UIScrollView() : scrollListener_(nullptr)
{
#if defined(ENABLE_ROTATE_INPUT) && ENABLE_ROTATE_INPUT
    rotateFactor_ = DEFAULT_SCROLL_VIEW_ROTATE_FACTOR;
    rotateThrowthreshold_ = SCROLLVIEW_ROTATE_THROW_THRESHOLD;
    rotateAccCoefficient_ = SCROLLVIEW_ROTATE_DISTANCE_COEFF;
#endif
#if defined(ENABLE_VIBRATOR) && ENABLE_VIBRATOR
    totalRotateLen_ = 0;
    lastVibratorRotateLen_ = 0;
#endif
#if defined(ENABLE_FOCUS_MANAGER) && ENABLE_FOCUS_MANAGER
    focusable_ = true;
#endif
    direction_ = HORIZONTAL_AND_VERTICAL;
}

bool UIScrollView::OnDragEvent(const DragEvent& event)
{
    if (scrollAnimator_.GetState() != Animator::STOP) {
        UIAbstractScroll::StopAnimator();
    }
    Drag(event);
    return UIView::OnDragEvent(event);
}

bool UIScrollView::OnDragEndEvent(const DragEvent& event)
{
    Point last = event.GetPreLastPoint();
    Point current = event.GetLastPoint();
    if ((last.x == current.x) && (last.y == current.y)) {
        last = current;
        current = event.GetCurrentPos();
    }

    if (!DragThrowAnimator(current, last, event.GetDragDirection())) {
        if (scrollListener_ && (scrollListener_->GetScrollState() == OnScrollListener::SCROLL_STATE_MOVE)) {
            scrollListener_->OnScrollEnd();
            scrollListener_->SetScrollState(OnScrollListener::SCROLL_STATE_STOP);
        }
    }
    return UIView::OnDragEndEvent(event);
}

void UIScrollView::Drag(const DragEvent& event)
{
    int16_t xDistance = event.GetDeltaX();
    int16_t yDistance = event.GetDeltaY();

    if ((direction_ == HORIZONTAL || direction_ == HORIZONTAL_AND_VERTICAL) && xDistance != 0) {
        DragXInner(xDistance);
    }
    if ((direction_ == VERTICAL || direction_ == HORIZONTAL_AND_VERTICAL) && yDistance != 0) {
        RefreshDelta(yDistance);
        DragYInner(yDistance);
    }
}

bool UIScrollView::OnPressEvent(const PressEvent& event)
{
    StopAnimator();
    return UIView::OnPressEvent(event);
}

#if defined(ENABLE_ROTATE_INPUT) && ENABLE_ROTATE_INPUT
bool UIScrollView::OnRotateEvent(const RotateEvent& event)
{
    if (direction_ == HORIZONTAL_NOR_VERTICAL) {
        return UIView::OnRotateEvent(event);
    }
    int16_t rotateLen = static_cast<int16_t>(event.GetRotate() * rotateFactor_);
#if defined(ENABLE_VIBRATOR) && ENABLE_VIBRATOR
    bool lastIsEdge = false;
    Rect childRect = GetAllChildRelativeRect();
    SetIsEdge(lastIsEdge, childRect);
#endif
    RefreshRotate(rotateLen);
    if (direction_ == HORIZONTAL) {
        DragXInner(rotateLen);
    } else {
        DragYInner(rotateLen);
    }
#if defined(ENABLE_VIBRATOR) && ENABLE_VIBRATOR
    totalRotateLen_ += rotateLen;
    childRect = GetAllChildRelativeRect();
    bool isEdge = false;
    if (direction_ == HORIZONTAL) {
        if (childRect.GetLeft() - scrollBlankSize_ >= 0 || childRect.GetRight() + scrollBlankSize_ <= GetWidth()) {
            isEdge = true;
        }
    } else {
        if (childRect.GetTop() - scrollBlankSize_ >= 0 || childRect.GetBottom() + scrollBlankSize_ <= GetHeight()) {
            isEdge = true;
        }
    }
    VibratorFunc vibratorFunc = VibratorManager::GetInstance()->GetVibratorFunc();
    if (vibratorFunc != nullptr && !isEdge) {
        rotateLen = MATH_ABS(totalRotateLen_ - lastVibratorRotateLen_);
        if (rotateLen > DEFAULT_SCROLL_VIEW_VIBRATION_LEN) {
            uint16_t vibrationCnt = rotateLen / DEFAULT_SCROLL_VIEW_VIBRATION_LEN;
            for (uint16_t i = 0; i < vibrationCnt; i++) {
                GRAPHIC_LOGI("UIScrollView::OnRotateEvent calls TYPE_ONE vibrator");
                vibratorFunc(VibratorType::VIBRATOR_TYPE_ONE);
            }
            lastVibratorRotateLen_ = totalRotateLen_;
        }
    }
    if (vibratorFunc != nullptr && (!lastIsEdge && isEdge)) {
        GRAPHIC_LOGI("UIScrollView::OnRotateEvent calls TYPE_THREE vibrator");
        vibratorFunc(VibratorType::VIBRATOR_TYPE_THREE);
    }
#endif
    return UIView::OnRotateEvent(event);
}

bool UIScrollView::OnRotateEndEvent(const RotateEvent& event)
{
    if (direction_ == HORIZONTAL_NOR_VERTICAL) {
        return UIView::OnRotateEndEvent(event);
    }
    return UIAbstractScroll::OnRotateEndEvent(event);
}

#if defined(ENABLE_VIBRATOR) && ENABLE_VIBRATOR
void UIScrollView::SetIsEdge(bool& lastIsEdge, Rect childRect)
{
    if (direction_ == HORIZONTAL) {
        if (childRect.GetLeft() - scrollBlankSize_ >= 0 || childRect.GetRight() + scrollBlankSize_ <= GetWidth()) {
            lastIsEdge = true;
        }
    } else {
        if (childRect.GetTop() - scrollBlankSize_ >= 0 || childRect.GetBottom() + scrollBlankSize_ <= GetHeight()) {
            lastIsEdge = true;
        }
    }
}
#endif
#endif

void UIScrollView::ScrollBy(int16_t xDistance, int16_t yDistance)
{
    if ((direction_ == HORIZONTAL || direction_ == HORIZONTAL_AND_VERTICAL) && xDistance != 0) {
        DragXInner(xDistance);
    }
    if ((direction_ == VERTICAL || direction_ == HORIZONTAL_AND_VERTICAL) && yDistance != 0) {
        DragYInner(yDistance);
    }
    if ((scrollListener_ != nullptr) && (scrollListener_->GetScrollState() == OnScrollListener::SCROLL_STATE_MOVE)) {
        scrollListener_->OnScrollEnd();
        scrollListener_->SetScrollState(OnScrollListener::SCROLL_STATE_STOP);
    }
}

bool UIScrollView::DragXInner(int16_t distance)
{
    Rect childRect = GetAllChildRelativeRect();
    int16_t reboundSize = reboundSize_;
    if (scrollAnimator_.GetState() != Animator::STOP) {
        reboundSize = 0;
    }

    if (childRect.GetWidth() <= (GetWidth() - (scrollBlankSize_ << 1)) ||
        !(direction_ == HORIZONTAL || direction_ == HORIZONTAL_AND_VERTICAL)) {
        return false;
    }

    if (distance > 0) {
        if (childRect.GetLeft() > scrollBlankSize_ + reboundSize) {
            distance = 0;
        } else if ((childRect.GetLeft() + distance) > scrollBlankSize_ + reboundSize) {
            distance = scrollBlankSize_ - childRect.GetLeft() + reboundSize;
        }
    } else {
        int16_t childRight = childRect.GetRight();
        int16_t scrollWidth = GetWidth();
        if (childRight < scrollWidth - (scrollBlankSize_ + reboundSize)) {
            distance = 0;
        } else if (childRight + distance < scrollWidth - (scrollBlankSize_ + reboundSize)) {
            distance = scrollWidth - (scrollBlankSize_ + reboundSize) - childRight - 1;
        }
    }

    return MoveOffset(distance, 0);
}

bool UIScrollView::DragYInner(int16_t distance)
{
    Rect childRect = GetAllChildRelativeRect();
    int16_t reboundSize = reboundSize_;
    if (scrollAnimator_.GetState() != Animator::STOP) {
        reboundSize = 0;
    }

    if (childRect.GetHeight() <= (GetHeight() - (scrollBlankSize_ << 1)) ||
        !(direction_ == VERTICAL || direction_ == HORIZONTAL_AND_VERTICAL)) {
        return false;
    }

    if (distance > 0) {
        if (childRect.GetTop() > scrollBlankSize_ + reboundSize) {
            distance = 0;
        } else if ((childRect.GetTop() + distance) > scrollBlankSize_ + reboundSize) {
            distance = scrollBlankSize_ - childRect.GetTop() + reboundSize;
        }
    } else {
        int16_t childBottom = childRect.GetBottom();
        int16_t scrollHeight = GetHeight();
        if (childBottom < scrollHeight - (scrollBlankSize_ + reboundSize)) {
            distance = 0;
        } else if (childBottom + distance < scrollHeight - (scrollBlankSize_ + reboundSize)) {
            distance = scrollHeight - (scrollBlankSize_ + reboundSize) - childBottom - 1;
        }
    }

    return MoveOffset(0, distance);
}

bool UIScrollView::MoveOffset(int16_t offsetX, int16_t offsetY)
{
    if ((offsetX != 0) || (offsetY != 0)) {
        if ((scrollListener_ != nullptr) &&
            (scrollListener_->GetScrollState() == OnScrollListener::SCROLL_STATE_STOP)) {
            scrollListener_->OnScrollStart();
            scrollListener_->SetScrollState(OnScrollListener::SCROLL_STATE_MOVE);
        }
        UIAbstractScroll::MoveChildByOffset(offsetX, offsetY);
        if (xScrollBarVisible_ || yScrollBarVisible_) {
            RefreshScrollBar();
        }
        Invalidate();
        return true;
    }
    return false;
}

void UIScrollView::RefreshScrollBar()
{
    Rect childrenRect = GetAllChildRelativeRect();
    /* calculate scrollBar's the proportion of foreground */
    int16_t totalLen = childrenRect.GetHeight() + 2 * scrollBlankSize_; // 2: two blank space on both sizes
    int16_t len = GetHeight();
    if (yScrollBarVisible_) {
        yScrollBar_->SetForegroundProportion(static_cast<float>(len) / totalLen);
        /* calculate scrolling progress */
        yScrollBar_->SetScrollProgress(static_cast<float>(scrollBlankSize_ - childrenRect.GetTop()) / (totalLen - len));
    }
    if (xScrollBarVisible_) {
        /* so do x-bar */
        totalLen = childrenRect.GetWidth() + 2 * scrollBlankSize_; // 2: two blank space on both sizes
        len = GetWidth();
        xScrollBar_->SetForegroundProportion(static_cast<float>(len) / totalLen);
        xScrollBar_->SetScrollProgress(static_cast<float>(scrollBlankSize_ - childrenRect.GetLeft()) /
                                       (totalLen - len));
    }
    RefreshAnimator();
}

void UIScrollView::CalculateReboundDistance(int16_t& dragDistanceX, int16_t& dragDistanceY)
{
    Rect rect = GetAllChildRelativeRect();
    int16_t top = rect.GetTop();
    int16_t bottom = rect.GetBottom();
    int16_t scrollHeight = GetHeight();
    int16_t left = rect.GetLeft();
    int16_t right = rect.GetRight();
    int16_t scrollWidth = GetWidth();
    if (scrollBlankSize_ < top) {
        dragDistanceY = scrollBlankSize_ - top;
    } else if (bottom < scrollHeight - 1) {
        dragDistanceY = scrollHeight - scrollBlankSize_ - bottom - 1;
    }

    if (scrollBlankSize_ < left) {
        dragDistanceX = scrollBlankSize_ - left;
    } else if (right < scrollWidth - 1) {
        dragDistanceX = scrollWidth - scrollBlankSize_ - right - 1;
    }
}

void UIScrollView::StopAnimator()
{
    if ((scrollListener_ != nullptr) && (scrollListener_->GetScrollState() == OnScrollListener::SCROLL_STATE_MOVE)) {
        scrollListener_->OnScrollEnd();
        scrollListener_->SetScrollState(OnScrollListener::SCROLL_STATE_STOP);
    }
    UIAbstractScroll::StopAnimator();
}
} // namespace OHOS