/*

 * Copyright (c) 2025-2026 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.

 */



package ohos.ace.adapter;



import android.app.Activity;

import android.content.pm.PackageManager;

import android.os.Build;



import androidx.core.util.Consumer;

import androidx.window.java.layout.WindowInfoTrackerCallbackAdapter;

import androidx.window.layout.DisplayFeature;

import androidx.window.layout.FoldingFeature;

import androidx.window.layout.WindowInfoTracker;

import androidx.window.layout.WindowLayoutInfo;



import ohos.ace.adapter.ALog;



import java.lang.reflect.Method;

import java.lang.reflect.InvocationTargetException;

import java.util.List;

import java.util.concurrent.atomic.AtomicReference;



/** 

 * fold manager that get fold status and fold display mode.

 *

 * @since 2025-05-22

 */

public class FoldManager {



    private static final String TAG = "FoldManager";



    private static final int ANDROID_API_31 = 31;



    private static final String FEATURE_SENSOR_HINGE_ANGLE = "android.hardware.sensor.hinge_angle";



    private static boolean foldAble = false;



    private static int foldStatus = FoldInfo.FOLD_STATUS_UNKNOWN;



    private FoldManager() {

    }



    /**

     * get is foldable of the device.

     *

     * @param activity the activity

     * @return true if foldable

     */

    public static boolean isFoldable(Activity activity) {

        if (Build.VERSION.SDK_INT < ANDROID_API_31) {

            return false;

        }

        FoldingFeature foldingFeature = null;

        if (isOpenFoldListener(activity)) {

            foldingFeature = getFoldingFeatureInvoke(activity);

        } else {

            foldingFeature = getFoldingFeature(activity);

        }

        if (foldingFeature != null) {

            foldAble = true;

        }

        return foldingFeature != null;

    }



    /**

     * get fold status

     *

     * @param activity the activity

     * @return fold status

     */

    public static int getFoldStatus(Activity activity) {

        if (Build.VERSION.SDK_INT < ANDROID_API_31) {

            return FoldInfo.FOLD_STATUS_UNKNOWN;

        }

        FoldingFeature foldingFeature = null;

        if (isOpenFoldListener(activity)) {

            foldingFeature = getFoldingFeatureInvoke(activity);

        } else {

            foldingFeature = getFoldingFeature(activity);

        }

        int currentStatus = getStatusFromFeature(activity, foldingFeature);

        foldStatus = currentStatus;

        return currentStatus;

    }



    /** 

     * get fold status from the folding feature

     *

     * @param activity the activity

     * @param foldingFeature the folding feature

     * @return fold status

     */

    public static int getStatusFromFeature(Activity activity, FoldingFeature foldingFeature) {

        if (foldingFeature == null) {

            if (hasHingeAngleSensor(activity)) {

                foldAble = true;

                ALog.i(TAG, "foldingFeature is null, hinge sensor detected, use folded status fallback.");

                return FoldInfo.FOLD_STATUS_FOLDED;

            }

            if (foldAble) {

                ALog.i(TAG, "foldingFeature is null, use folded status fallback.");

                return FoldInfo.FOLD_STATUS_FOLDED;

            }

            if (foldStatus != FoldInfo.FOLD_STATUS_UNKNOWN) {

                ALog.i(TAG, "foldingFeature is null, use cached foldStatus.");

                return foldStatus;

            }

            ALog.e(TAG, "foldingFeature is null.");

            return FoldInfo.FOLD_STATUS_UNKNOWN;

        }

        if (foldingFeature.getState() == FoldingFeature.State.FLAT) {

            return FoldInfo.FOLD_STATUS_EXPANDED;

        } else if (foldingFeature.getState() == FoldingFeature.State.HALF_OPENED) {

            return FoldInfo.FOLD_STATUS_HALF_FOLDED;

        } else {

            return FoldInfo.FOLD_STATUS_UNKNOWN;

        }

    }



    /**

     * get fold feature

     *

     * @param activity the activity

     * @return FoldingFeature

     */

    public static FoldingFeature getFoldingFeature(Activity activity) {

        if (activity == null) {

            return null;

        }

        try {

            AtomicReference<FoldingFeature> foldingFeature = new AtomicReference<>();



            WindowInfoTracker windowInfoTracker = WindowInfoTracker.getOrCreate(activity);

            WindowInfoTrackerCallbackAdapter windowInfoTrackerCallbackAdapter = new WindowInfoTrackerCallbackAdapter(

                windowInfoTracker);

            Consumer<WindowLayoutInfo> consumerWindowLayout = getConsumerWindowLayout(activity, foldingFeature);

            windowInfoTrackerCallbackAdapter.addWindowLayoutInfoListener(activity, Runnable::run, consumerWindowLayout);

            windowInfoTrackerCallbackAdapter.removeWindowLayoutInfoListener(consumerWindowLayout);

            return foldingFeature.get();

        } catch (NoClassDefFoundError e) {

            ALog.e(TAG, "androidx.window is not support!\n" + e.toString());

            return null;

        }

    }



    /**

     * get status of fold motoring in activity

     *

     * @param activity the activity

     * @return open or not

     */

    public static boolean isOpenFoldListener(Activity activity) {

        try {

            Class<?> clazz = activity.getClass();

            Method getFoldMethod = clazz.getMethod("getFoldWindowInfoCallback");

            Object result = getFoldMethod.invoke(activity);

            return result != null;

        } catch (InvocationTargetException e) {

            ALog.e(TAG, "invoke WindowInfoCallback fail:" + e.getMessage());

            return false;

        } catch (NoSuchMethodException e) {

            ALog.e(TAG, "invoke WindowInfoCallback fail:" + e.getMessage());

            return false;

        } catch (IllegalAccessException e) {

            ALog.e(TAG, "invoke WindowInfoCallback fail:" + e.getMessage());

            return false;

        }

    }



    /** 

     * get FoldingFeature of fold motoring in activity

     *

     * @param activity the activity

     * @return FoldingFeature

     */

    public static FoldingFeature getFoldingFeatureInvoke(Activity activity) {

        try {

            Class<?> clazz = activity.getClass();

            Method getFoldMethod = clazz.getMethod("getFoldingFeatureFromListener");

            Object result = getFoldMethod.invoke(activity);

            if (result == null) {

                return null;

            }

            if (result instanceof FoldingFeature) {

                return (FoldingFeature) result;

            }

            return null;

        } catch (InvocationTargetException e) {

            ALog.e(TAG, "invoke getFoldingFeature fail:" + e.getMessage());

            return null;

        } catch (NoSuchMethodException e) {

            ALog.e(TAG, "invoke getFoldingFeature fail:" + e.getMessage());

            return null;

        } catch (IllegalAccessException e) {

            ALog.e(TAG, "invoke getFoldingFeature fail:" + e.getMessage());

            return null;

        }

    }



    /**

     * set foldable from activity

     *

     * @param foldable is true or false

     */

    public static void setFoldable(boolean foldable) {

        foldAble = foldable;

    }



    /**

     * set fold status from activity

     *

     * @param newFoldStatus fold status

     */

    public static void setFoldStatus(int newFoldStatus) {

        foldStatus = newFoldStatus;

    }



    /**

     * get an instance of windowLayoutInfo

     *

     * @param activity the activity

     * @param foldingFeature the folding feature

     * @return Consumer<WindowLayoutInfo>

     */

    public static Consumer<WindowLayoutInfo> getConsumerWindowLayout(Activity activity,

        AtomicReference<FoldingFeature> foldingFeature) {

        return windowLayoutInfo -> {

                activity.runOnUiThread(() -> {

                    handleFoldStatusChange(windowLayoutInfo, foldingFeature);

                });

            };

    }



    private static void handleFoldStatusChange(WindowLayoutInfo windowLayoutInfo,

        AtomicReference<FoldingFeature> foldingFeature) {

        windowLayoutInfo.getDisplayFeatures().stream()

            .filter(FoldingFeature.class::isInstance)

            .map(FoldingFeature.class::cast)

            .findFirst()

            .ifPresent(feature -> {

                foldingFeature.set((FoldingFeature) feature);

            });

    }



    private static boolean hasHingeAngleSensor(Activity activity) {

        if (activity == null) {

            return false;

        }

        PackageManager packageManager = activity.getPackageManager();

        if (packageManager == null) {

            return false;

        }

        return packageManager.hasSystemFeature(FEATURE_SENSOR_HINGE_ANGLE);

    }

}