/*
 * 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 "dock/focus_manager.h"
#if ENABLE_FOCUS_MANAGER
#include "components/root_view.h"
#include "gfx_utils/graphic_math.h"

#include "common/input_method_manager.h"

namespace OHOS {
FocusManager* FocusManager::GetInstance()
{
    static FocusManager instance;
    return &instance;
}

bool FocusManager::RequestFocus(UIView* view)
{
    if (view == nullptr || view == focusView_ || !view->IsFocusable() ||
        !view->IsVisible()) {
        return false;
    }
    lastFocusView_ = focusView_;
    focusView_ = view;

    UIViewGroup* rootView = RootView::GetInstance();
    if (RootView::FindSubView(*rootView, lastFocusView_)) {
        lastFocusView_->Blur();
    }
    focusView_->Focus();

    UIViewType viewType = focusView_->GetViewType();
    // show keyboard if the view is edittable
    if (viewType == UI_EDIT_TEXT) {
        InputMethodManager::GetInstance().ShowInputMethod(focusView_);
    }

    return true;
}

bool FocusManager::ClearFocus()
{
    if (focusView_ == nullptr) {
        return false;
    }
    lastFocusView_ = focusView_;

    UIViewGroup* rootView = RootView::GetInstance();
    if (RootView::FindSubView(*rootView, focusView_)) {
        focusView_->Blur();
    }

    focusView_ = nullptr;
    return true;
}

bool FocusManager::RequestFocusByDirection(uint8_t direction)
{
    if (focusView_ != nullptr) {
        UIView* candidate = nullptr;
        if (GetNextFocus(focusView_, candidate, direction)) {
            return RequestFocus(candidate);
        }
    }
    return false;
}

bool FocusManager::CompareCandidates(UIView* focusedView, UIView*& candidate, UIView* current, uint8_t direction)
{
    if (current == focusedView) {
        return false;
    }
    bool res = false;
    switch (direction) {
        case FOCUS_DIRECTION_UP: {
            res = CompareCandidatesByUp(focusedView, candidate, current);
            break;
        }
        case FOCUS_DIRECTION_DOWN: {
            res = CompareCandidatesByDown(focusedView, candidate, current);
            break;
        }
        case FOCUS_DIRECTION_LEFT: {
            res = CompareCandidatesByLeft(focusedView, candidate, current);
            break;
        }
        case FOCUS_DIRECTION_RIGHT: {
            res = CompareCandidatesByRight(focusedView, candidate, current);
            break;
        }
        default:
            return false;
    }
    return res;
}

bool FocusManager::IsAtSameCol(const Rect& rect1, const Rect& rect2)
{
    return ((rect1.GetLeft() < rect2.GetRight()) && (rect1.GetRight() > rect2.GetLeft()));
}

bool FocusManager::IsAtSameRow(const Rect& rect1, const Rect& rect2)
{
    return ((rect1.GetBottom() < rect2.GetTop()) && (rect1.GetTop() > rect2.GetBottom()));
}

bool FocusManager::CompareCandidatesByUp(UIView* focusedView, UIView*& candidate, UIView* current)
{
    Rect focusedViewRect = focusedView->GetRect();
    Rect currentRect = current->GetRect();
    if (currentRect.GetBottom() >= focusedViewRect.GetBottom()) {
        return false;
    }
    if (candidate != nullptr) {
        Rect candidateRect = candidate->GetRect();
        if (IsAtSameCol(focusedViewRect, currentRect) && IsAtSameCol(focusedViewRect, candidateRect)) {
            return currentRect.GetBottom() > candidateRect.GetBottom();
        }
        return CompareCandidatesDistance(focusedViewRect, candidateRect, currentRect);
    }
    return true;
}

bool FocusManager::CompareCandidatesByDown(UIView* focusedView, UIView*& candidate, UIView* current)
{
    Rect focusedViewRect = focusedView->GetRect();
    Rect currentRect = current->GetRect();
    if (currentRect.GetTop() <= focusedViewRect.GetTop()) {
        return false;
    }
    if (candidate != nullptr) {
        Rect candidateRect = candidate->GetRect();
        if (IsAtSameCol(focusedViewRect, currentRect) && IsAtSameCol(focusedViewRect, candidateRect)) {
            return currentRect.GetTop() < candidateRect.GetTop();
        }
        return CompareCandidatesDistance(focusedViewRect, candidateRect, currentRect);
    }
    return true;
}

bool FocusManager::CompareCandidatesByLeft(UIView* focusedView, UIView*& candidate, UIView* current)
{
    Rect focusedViewRect = focusedView->GetRect();
    Rect currentRect = current->GetRect();
    if (currentRect.GetRight() >= focusedViewRect.GetRight()) {
        return false;
    }
    if (candidate != nullptr) {
        Rect candidateRect = candidate->GetRect();
        if (IsAtSameRow(focusedViewRect, currentRect) && IsAtSameRow(focusedViewRect, candidateRect)) {
            return currentRect.GetRight() > candidateRect.GetRight();
        }
        return CompareCandidatesDistance(focusedViewRect, candidateRect, currentRect);
    }
    return true;
}

bool FocusManager::CompareCandidatesByRight(UIView* focusedView, UIView*& candidate, UIView* current)
{
    Rect focusedViewRect = focusedView->GetRect();
    Rect currentRect = current->GetRect();
    if (currentRect.GetLeft() <= focusedViewRect.GetLeft()) {
        return false;
    }
    if (candidate != nullptr) {
        Rect candidateRect = candidate->GetRect();
        if (IsAtSameRow(focusedViewRect, currentRect) && IsAtSameRow(focusedViewRect, candidateRect)) {
            return currentRect.GetLeft() < candidateRect.GetLeft();
        }
        return CompareCandidatesDistance(focusedViewRect, candidateRect, currentRect);
    }
    return true;
}

bool FocusManager::CompareCandidatesDistance(const Rect& focused, const Rect& candidate, const Rect& current)
{
    uint64_t xDiff1 = focused.GetX() - candidate.GetX();
    uint64_t yDiff1 = focused.GetY() - candidate.GetY();
    uint64_t xDiff2 = focused.GetX() - current.GetX();
    uint64_t yDiff2 = focused.GetY() - current.GetY();
    uint64_t distance1 = xDiff1 * xDiff1 + yDiff1 * yDiff1;
    uint64_t distance2 = xDiff2 * xDiff2 + yDiff2 * yDiff2;
    return distance2 < distance1;
}

bool FocusManager::GetNextFocus(UIView* focusedView, UIView*& candidate, uint8_t direction)
{
    UIView* parent = focusedView->GetParent();
    if (parent == nullptr) {
        return false;
    }
    UIView* child = nullptr;
    bool isFoundBestCandidate = false;
    UIView* current = focusedView;
    while (parent != nullptr) {
        if (isFoundBestCandidate) {
            return true;
        }
        child = static_cast<UIViewGroup*>(parent)->GetChildrenHead();
        while (child != nullptr) {
            if (child == current) {
                child = child->GetNextSibling();
                continue;
            }
            if (child->IsViewGroup()) {
                if (GetNextFocus(focusedView, candidate, static_cast<UIViewGroup*>(child), direction)) {
                    isFoundBestCandidate = true;
                }
            } else {
                if (GetNextFocus(focusedView, candidate, child, direction)) {
                    isFoundBestCandidate = true;
                }
            }
            child = child->GetNextSibling();
        }
        if (isFoundBestCandidate) {
            return true;
        } else {
            current = parent;
        }
        parent = parent->GetParent();
    }
    return false;
}

bool FocusManager::GetNextFocus(UIView* focusedView, UIView*& candidate, UIView* view, uint8_t direction)
{
    UIView* current = view;
    if (!current->IsVisible() || !current->IsFocusable()) {
        return false;
    }
    if (CompareCandidates(focusedView, candidate, current, direction)) {
        candidate = current;
        return true;
    }
    return false;
}

bool FocusManager::GetNextFocus(UIView* focusedView, UIView*& candidate, UIViewGroup* viewGroup, uint8_t direction)
{
    UIViewGroup* current = viewGroup;
    if (!current->IsVisible() || !current->IsFocusable()) {
        return false;
    }
    if (current->IsInterceptFocus()) {
        return GetNextFocus(focusedView, candidate, static_cast<UIView*>(current), direction);
    }

    UIView* child = current->GetChildrenHead();
    bool childFocusable = false;
    while (child != nullptr) {
        if (child == focusedView) {
            child = child->GetNextSibling();
            continue;
        }
        if (child->IsViewGroup()) {
            if (GetNextFocus(focusedView, candidate, static_cast<UIViewGroup*>(child), direction)) {
                childFocusable = true;
            }
        } else if (GetNextFocus(focusedView, candidate, child, direction)) {
            childFocusable = true;
        }
        child = child->GetNextSibling();
    }
    return childFocusable;
}
}
#endif