/*
 * Copyright (c) 2023 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.
 */

#ifndef INPUT_METHOD_PANEL_H
#define INPUT_METHOD_PANEL_H

#include <cstdint>
#include <functional>
#include <map>
#include <string>

#include "calling_window_info.h"
#include "display_manager.h"
#include "input_method_utils.h"
#include "input_status_info.h"
#include "input_window_info.h"
#include "native_engine/native_engine.h"
#include "panel_common.h"
#include "panel_info.h"
#include "window_change_listener_impl.h"
#include "wm_common.h"

namespace OHOS {
namespace MiscServices {
constexpr int FOLD_TOP = 0;
constexpr int FOLD_LEFT = 0;
constexpr int FOLD_RIGHT = 0;
constexpr int FOLD_BOTTOM = 606;
constexpr int UNFOLD_TOP = 0;
constexpr int UNFOLD_LEFT = 0;
constexpr int UNFOLD_RIGHT = 0;
constexpr int UNFOLD_BOTTOM = 822;
constexpr int COMMON_BOTTOM = 809;

class InputMethodPanel {
public:
    static constexpr uint32_t INVALID_WINDOW_ID = 0;
    using CallbackFunc = std::function<void(uint32_t, PanelFlag)>;
    InputMethodPanel() = default;
    ~InputMethodPanel();
    int32_t SetUiContent(const std::string &contentInfo, napi_env env, std::shared_ptr<NativeReference> contentStorage);
    int32_t SetUiContentAni(const std::string &contentInfo, ani_env* env, ani_object storage);
    int32_t CreatePanel(const std::shared_ptr<AbilityRuntime::Context> &context, const PanelInfo &panelInfo);
    int32_t DestroyPanel();

    int32_t Resize(uint32_t width, uint32_t height);
    int32_t MoveTo(int32_t x, int32_t y);
    int32_t StartMoving();
    int32_t GetDisplayId(uint64_t &displayId);
    int32_t AdjustKeyboard();
    int32_t AdjustPanelRect(const PanelFlag panelFlag, const LayoutParams &layoutParams, bool needUpdateRegion,
        bool needConfig, bool isColdStartRequest);
    int32_t AdjustPanelRect(PanelFlag panelFlag, EnhancedLayoutParams params, HotAreas hotAreas,
        bool isColdStartRequest);
    int32_t UpdateRegion(std::vector<Rosen::Rect> region);
    std::tuple<std::vector<std::string>, std::vector<std::string>> GetScreenStatus(const PanelFlag panelFlag);
    int32_t ChangePanelFlag(PanelFlag panelFlag);
    PanelType GetPanelType();
    PanelFlag GetPanelFlag();
    int32_t ShowPanel(Trigger trigger, uint32_t windowId = 0);
    int32_t HidePanel();
    int32_t SizeChange(const WindowSize &size);
    WindowSize GetKeyboardSize();
    bool SetPanelStatusListener(std::shared_ptr<PanelStatusListener> statusListener, const std::string &type);
    void ClearPanelListener(const std::string &type);
    int32_t SetCallingWindow(uint32_t windowId);
    int32_t GetCallingWindowInfo(CallingWindowInfo &windowInfo);
    int32_t SetPrivacyMode(bool isPrivacyMode);
    bool IsShowing();
    bool IsDisplayPortrait();
    int32_t SetTextFieldAvoidInfo(double positionY, double height);
    void SetPanelHeightCallback(CallbackFunc heightCallback);
    int32_t IsEnhancedParamValid(PanelFlag panelFlag, EnhancedLayoutParams &params);
    int32_t SetImmersiveMode(ImmersiveMode mode);
    ImmersiveMode GetImmersiveMode();
    bool IsInMainDisplay();
    int32_t SetImmersiveEffect(const ImmersiveEffect &effect);
    ImmersiveEffect LoadImmersiveEffect();
    int32_t SetKeepScreenOn(bool isKeepScreenOn);
    int32_t SetSystemPanelButtonColor(const std::string &fillColor, const std::string &backgroundColor);
    int32_t GetSystemPanelCurrentInsets(uint64_t displayId, SystemPanelInsets &systemPanelInsets);
    int32_t SetShadow(const Shadow &shadow);
    int32_t CovertSetShadowResult(const Rosen::WMError ret, const SetShadowOperation operation);
    bool IsKeyboardBottomElevated(PanelFlag flag);
    bool IsFunctionButtonVisible(bool isKeyboardElevated);
    uint32_t windowId_ = INVALID_WINDOW_ID;

private:
    class KeyboardPanelInfoChangeListener : public Rosen::IKeyboardPanelInfoChangeListener {
    public:
        using ChangeHandler = std::function<void(const Rosen::KeyboardPanelInfo &keyboardPanelInfo)>;
        explicit KeyboardPanelInfoChangeListener(ChangeHandler handler) : handler_(std::move(handler)) { }
        ~KeyboardPanelInfoChangeListener() = default;
        void OnKeyboardPanelInfoChanged(const Rosen::KeyboardPanelInfo &keyboardPanelInfo) override
        {
            if (handler_ == nullptr) {
                return;
            }
            handler_(keyboardPanelInfo);
        }

    private:
        ChangeHandler handler_ = nullptr;
    };
    void RegisterKeyboardPanelInfoChangeListener();
    void UnregisterKeyboardPanelInfoChangeListener();
    void OnKeyboardPanelInfoChange(const Rosen::KeyboardPanelInfo &keyboardPanelInfo);
    void OnPanelHeightChange(const Rosen::KeyboardPanelInfo &keyboardPanelInfo);
    void HandleKbPanelInfoChange(const Rosen::KeyboardPanelInfo &keyboardPanelInfo);
    sptr<Rosen::IKeyboardPanelInfoChangeListener> kbPanelInfoListener_ { nullptr };

private:
    class VisibilityChangeListener : public Rosen::IOcclusionStateChangedListener {
    public:
        using ChangeHandler = std::function<void(Rosen::WindowVisibilityState)>;
        explicit VisibilityChangeListener(ChangeHandler handler) : handler_(std::move(handler)) { }
        ~VisibilityChangeListener() = default;
        void OnOcclusionStateChanged(const Rosen::WindowVisibilityState state) override
        {
            if (handler_ != nullptr) {
                handler_(state);
            }
        }

    private:
        ChangeHandler handler_ = nullptr;
    };
    int32_t RegisterVisibilityChangeListener();
    int32_t UnregisterVisibilityChangeListener();
    void OnVisibilityChange(const Rosen::WindowVisibilityState state);
    std::atomic<bool> isVisible_{ false };
    sptr<Rosen::IOcclusionStateChangedListener> visibilityChangeListener_{ nullptr };

private:
    bool IsHidden();
    int32_t SetPanelProperties();
    std::string GeneratePanelName();
    void PanelStatusChange(const InputWindowStatus &status);
    void PanelStatusChangeToImc(
        const InputWindowStatus &status, const Rosen::Rect &rect, bool triggeredBySwitchCandidate = false);
    bool MarkListener(const std::string &type, bool isRegister);
    bool SetPanelSizeChangeListener(std::shared_ptr<PanelStatusListener> statusListener);
    std::shared_ptr<PanelStatusListener> GetPanelListener();
    static uint32_t GenerateSequenceId();
    bool IsSizeValid(uint32_t width, uint32_t height);
    bool IsSizeValid(PanelFlag panelFlag, uint32_t width, uint32_t height, int32_t displayWidth, int32_t displayHeight);
    bool IsRectValid(const Rosen::Rect &rect, const WindowSize &displaySize);
    bool IsRectValid(PanelFlag panelFlag, const Rosen::Rect &rect, const WindowSize &displaySize);
    int32_t GetResizeParams(Rosen::Rect &portrait, Rosen::Rect &landscape, uint32_t width, uint32_t height);

    int32_t ParseEnhancedParams(
        PanelFlag panelFlag, const FullPanelAdjustInfo &adjustInfo, EnhancedLayoutParams &params);
    int32_t RectifyRect(bool isFullScreen, EnhancedLayoutParam &layoutParam, const WindowSize &displaySize,
        PanelFlag panelFlag, const PanelAdjustInfo &adjustInfo);
    int32_t CalculateAvoidHeight(EnhancedLayoutParam &layoutParam, const WindowSize &displaySize, PanelFlag panelFlag,
        const PanelAdjustInfo &adjustInfo);

    int32_t ParseParams(PanelFlag panelFlag, const LayoutParams &input, Rosen::KeyboardLayoutParams &output,
        bool needConfig = true);
    void ParseParam(PanelFlag panelFlag, const PanelAdjustInfo &adjustInfo, const WindowSize &displaySize,
        const Rosen::Rect &inputRect, EnhancedLayoutParam &outputParam);

    void CalculateHotAreas(const EnhancedLayoutParams &enhancedParams, const Rosen::KeyboardLayoutParams &params,
        const FullPanelAdjustInfo &adjustInfo, HotAreas &hotAreas);
    void CalculateDefaultHotArea(const Rosen::Rect &keyboard, const Rosen::Rect &panel,
        const PanelAdjustInfo &adjustInfo, HotArea &hotArea, uint32_t changeY);
    void CalculateHotArea(const Rosen::Rect &keyboard, const Rosen::Rect &panel, const PanelAdjustInfo &adjustInfo,
        HotArea &hotArea, uint32_t changeY);
    void CalculateEnhancedHotArea(
        const EnhancedLayoutParam &layout, const PanelAdjustInfo &adjustInfo, HotArea &hotArea, uint32_t changeY);
    void RectifyAreas(const std::vector<Rosen::Rect> &availableAreas, std::vector<Rosen::Rect> &areas);
    Rosen::Rect GetRectIntersection(Rosen::Rect a, Rosen::Rect b);
    uint32_t SafeSubtract(uint32_t minuend, uint32_t subtrahend);

    int32_t ResizePanel(uint32_t width, uint32_t height);
    int32_t ResizeWithoutAdjust(uint32_t width, uint32_t height);
    int32_t ResizeEnhancedPanel(uint32_t width, uint32_t height);
    void UpdateRectParams(
        Rosen::Rect &portrait, Rosen::Rect &landscape, uint32_t width, uint32_t height, const LayoutParams &currParams);
    void RectifyResizeParams(LayoutParams &params, const DisplaySize &displaySize);
    int32_t MovePanelRect(int32_t x, int32_t y);
    int32_t MoveEnhancedPanelRect(int32_t x, int32_t y);

    bool IsDisplayUnfolded();
    int32_t GetDisplaySize(DisplaySize &size);
    int32_t GetDensityDpi(float &densityDpi);

    int32_t InitAdjustInfo();
    int32_t GetAdjustInfo(PanelFlag panelFlag, FullPanelAdjustInfo &fullPanelAdjustInfo);
    void UpdateResizeParams();
    void UpdateHotAreas();
    void UpdateLayoutInfo(PanelFlag panelFlag, const LayoutParams &params, const EnhancedLayoutParams &enhancedParams,
        const Rosen::KeyboardLayoutParams &wmsParams, bool isEnhanced);
    Rosen::KeyboardLayoutParams ConvertToWMSParam(PanelFlag panelFlag, const EnhancedLayoutParams &layoutParams);
    Rosen::KeyboardTouchHotAreas ConvertToWMSHotArea(const HotAreas &hotAreas);
    int32_t GetKeyboardArea(PanelFlag panelFlag, const WindowSize &size, PanelAdjustInfo &keyboardArea);
    int32_t GetWindowOrientation(PanelFlag panelFlag, uint32_t windowWidth, bool &isPortrait);
    int32_t GetInputWindowAvoidArea(PanelFlag panelFlag, Rosen::Rect &windowRect);

    sptr<Rosen::Display> GetCurDisplay();
    uint64_t GetCurDisplayId();
    bool IsNeedConfig(bool ignoreIsMainDisplay = false);
    int32_t IsValidParam(const ImmersiveEffect &effect, const Rosen::KeyboardLayoutParams &layoutParams);
    int32_t AdjustLayout(const Rosen::KeyboardLayoutParams &param, bool isColdStartRequest);
    int32_t AdjustLayout(const Rosen::KeyboardLayoutParams &param, const ImmersiveEffect &effect,
        bool isColdStartRequest);
    int32_t ExecuteAdjustLayout(const Rosen::KeyboardLayoutParams &param);
    int32_t AdjustLayoutWithoutScb(const Rosen::KeyboardLayoutParams &param);
    int32_t FullScreenPrepare(Rosen::KeyboardLayoutParams &param, const ImmersiveEffect &effect);
    int32_t NormalImePrepare(Rosen::KeyboardLayoutParams &param, const ImmersiveEffect &effect);
    int32_t PrepareAdjustLayout(Rosen::KeyboardLayoutParams &param, const ImmersiveEffect &effect);
    bool IsImmersiveEffectSupported();
    Rosen::KeyboardEffectOption ConvertToWmEffect(ImmersiveMode mode, const ImmersiveEffect &effect);
    void SetImmersiveEffectToNone();
    void UpdateImmersiveHotArea();
    bool IsValidGradientHeight(uint32_t gradientHeight);
    int32_t HandleImmersiveEffectWhenShowing(const ImmersiveEffect &targetEffect);

    // Locked read and write functions for concurrent protection
    Rosen::KeyboardLayoutParams GetKeyboardLayoutParams();
    void SetKeyboardLayoutParams(Rosen::KeyboardLayoutParams params);
    EnhancedLayoutParams GetEnhancedLayoutParams();
    void SetEnhancedLayoutParams(EnhancedLayoutParams params);
    std::vector<int32_t> GetIgnoreAdjustInputTypes();
    void SetIgnoreAdjustInputTypes(const std::vector<int32_t> &inputTypes);
    void StoreImmersiveEffect(ImmersiveEffect effect);
    HotAreas GetHotAreas();
    void SetHotAreas(const HotAreas &hotAreas);
    ChangeY GetChangeY();
    void SetChangeY(ChangeY changeY);

    void StoreImmersiveMode(ImmersiveMode mode);
    CallbackFunc GetPanelHeightCallback();
    bool IsVectorsEqual(const std::vector<std::string>& vec1, const std::vector<std::string>& vec2);
    int32_t AreaInsets(SystemPanelInsets &systemPanelInsets, sptr<Rosen::Display> displayPtr);
    int32_t SetWindowShadow(const Shadow &shadow);
    bool IsKeyboardRectAtBottom();
    bool IsNeedNotify(PanelFlag panelFlag);
    void WaitSetUIContent();
    int32_t ShowKeyboardToWms(uint32_t windowId);
    void UpdatePanelFlag(PanelFlag newPanelFlag);
    void NotifySoftKeyBoardInfoChanged(PanelFlag panelFlag, InputWindowStatus status);
    int32_t SetHotAreasOnAdjust(HotAreas hotAreas);
    int32_t UpdatePanelFLagToWindow(PanelFlag panelFlag);
    bool NeedAdjustPanelRect();
    void AddAdjustPanelRect();

    sptr<OHOS::Rosen::Window> window_ = nullptr;
    sptr<OHOS::Rosen::WindowOption> winOption_ = nullptr;
    PanelType panelType_ = PanelType::SOFT_KEYBOARD;
    PanelFlag panelFlag_ = PanelFlag::FLG_FIXED;
    bool showRegistered_ = false;
    bool hideRegistered_ = false;
    bool sizeChangeRegistered_ = false;
    bool sizeUpdateRegistered_ = false;
    uint32_t invalidGravityPercent = 0;
    uint32_t callingWindowId_ = 0;
    std::shared_ptr<PanelStatusListener> panelStatusListener_ = nullptr;

    static std::atomic<uint32_t> sequenceId_;
    bool isScbEnable_ { false };

    std::mutex panelAdjustLock_;
    std::map<std::vector<std::string>, PanelAdjustInfo> panelAdjust_;
    std::mutex adjustInfoInitLock_;
    uint64_t adjustInfoDisplayId_ = 0;
    std::atomic<bool> isAdjustInfoInitialized_{ false };
    std::atomic<bool> isIgnorePanelAdjustInitialized_{ false };
    std::mutex ignoreAdjustInputTypeLock_;
    std::vector<int32_t> ignoreAdjustInputTypes_;

    std::mutex hotAreasLock_;
    HotAreas hotAreas_;
    std::mutex enhancedLayoutParamMutex_;
    EnhancedLayoutParams enhancedLayoutParams_;
    std::mutex keyboardLayoutParamsMutex_;
    Rosen::KeyboardLayoutParams keyboardLayoutParams_;

    std::mutex keyboardSizeLock_;
    WindowSize keyboardSize_ { 0, 0 };
    std::mutex windowListenerLock_;
    sptr<Rosen::IWindowChangeListener> windowChangedListener_ = nullptr;
    std::mutex heightCallbackMutex_;
    CallbackFunc panelHeightCallback_ = nullptr;

    std::mutex foldResizeParamMutex_;
    LayoutParams resizePanelFoldParams_ { // FoldDefaultValue
        { FOLD_TOP, FOLD_LEFT, FOLD_RIGHT, FOLD_BOTTOM },
        { FOLD_TOP, FOLD_LEFT, FOLD_RIGHT, COMMON_BOTTOM }
    };

    std::mutex unfoldResizeParamMutex_;
    LayoutParams resizePanelUnfoldParams_ { // UnfoldDefaultValue
        { UNFOLD_TOP, UNFOLD_LEFT, UNFOLD_RIGHT, UNFOLD_BOTTOM },
        { UNFOLD_TOP, UNFOLD_LEFT, UNFOLD_RIGHT, COMMON_BOTTOM }
    };
    std::atomic<bool> isWaitSetUiContent_ { true };
    std::atomic<bool> isInEnhancedAdjust_{ false };

    std::mutex immersiveModeMutex_;
    ImmersiveMode immersiveMode_ { ImmersiveMode::NONE_IMMERSIVE };
    std::mutex immersiveEffectMutex_;
    ImmersiveEffect immersiveEffect_ { 0, GradientMode::NONE, FluidLightMode::NONE };
    std::mutex changeYMutex_;
    ChangeY changeY_;

    std::mutex getInsetsMutex_;
    std::mutex parseParamsMutex_;
    std::mutex panelStatusChangeMutex_;

    std::atomic<bool> hasSetSize_ { false };
    std::atomic<bool> isExternalAdjusting_ { false };

    bool isPortrait_ { true };
    std::atomic<bool> hasAdjustWithoutScb_ { false };

    std::mutex bindImeInfoLock_;
    BoundImeInfo bindImeInfo_;

    std::mutex adjustLayoutMutex_;
    std::chrono::steady_clock::time_point adjustLayoutTime_;
    std::atomic<bool> hasImfAdjust_ { false };
    std::atomic<bool> hasJsAdjust_ { false };

    std::atomic<bool> parseAdjustSuccess_ { true };

    friend class JsPanel;
};
} // namespace MiscServices
} // namespace OHOS

#endif // INPUT_METHOD_PANEL_H