// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.ui.gfx;

import android.content.ComponentCallbacks;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.os.Build;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.WindowManager;

import org.chromium.base.CalledByNative;
import org.chromium.base.JNINamespace;

/**
 * This class facilitates access to android information typically only
 * available using the Java SDK, including {@link Display} properties.
 *
 * Currently the information consists of very raw display information (height, width, DPI scale)
 * regarding the main display.
 */
@JNINamespace("gfx")
public class DeviceDisplayInfo {


    private final Context mAppContext;
    private final WindowManager mWinManager;
    private Point mTempPoint = new Point();

    private DeviceDisplayInfo(Context context) {
        mAppContext = context.getApplicationContext();
        mWinManager = (WindowManager) mAppContext.getSystemService(Context.WINDOW_SERVICE);
    }

    /**
     * @return Display height in physical pixels.
     */
    @CalledByNative
    public int getDisplayHeight() {
        return getMetrics().heightPixels;
    }

    /**
     * @return Display width in physical pixels.
     */
    @CalledByNative
    public int getDisplayWidth() {
        return getMetrics().widthPixels;
    }

    /**
     * @return Real physical display height in physical pixels.
     */
    @CalledByNative
    public int getPhysicalDisplayHeight() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
            return 0;
        }
        getDisplay().getRealSize(mTempPoint);
        return mTempPoint.y;
    }

    /**
     * @return Real physical display width in physical pixels.
     */
    @CalledByNative
    public int getPhysicalDisplayWidth() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
            return 0;
        }
        getDisplay().getRealSize(mTempPoint);
        return mTempPoint.x;
    }

    @SuppressWarnings("deprecation")
    private int getPixelFormat() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
            return getDisplay().getPixelFormat();
        }
        // JellyBean MR1 and later always uses RGBA_8888.
        return PixelFormat.RGBA_8888;
    }

    /**
     * @return Bits per pixel.
     */
    @CalledByNative
    public int getBitsPerPixel() {
        int format = getPixelFormat();
        PixelFormat info = new PixelFormat();
        PixelFormat.getPixelFormatInfo(format, info);
        return info.bitsPerPixel;
    }

    /**
     * @return Bits per component.
     */
    @SuppressWarnings("deprecation")
    @CalledByNative
    public int getBitsPerComponent() {
        int format = getPixelFormat();
        switch (format) {
            case PixelFormat.RGBA_4444:
                return 4;

            case PixelFormat.RGBA_5551:
                return 5;

            case PixelFormat.RGBA_8888:
            case PixelFormat.RGBX_8888:
            case PixelFormat.RGB_888:
                return 8;

            case PixelFormat.RGB_332:
                return 2;

            case PixelFormat.RGB_565:
                return 5;

            // Non-RGB formats.
            case PixelFormat.A_8:
            case PixelFormat.LA_88:
            case PixelFormat.L_8:
                return 0;

            // Unknown format. Use 8 as a sensible default.
            default:
                return 8;
        }
    }

    /**
     * @return A scaling factor for the Density Independent Pixel unit. 1.0 is
     *         160dpi, 0.75 is 120dpi, 2.0 is 320dpi.
     */
    @CalledByNative
    public double getDIPScale() {
        return getMetrics().density;
    }

    /**
     * @return Smallest screen size in density-independent pixels that the
     *         application will see, regardless of orientation.
     */
    @CalledByNative
    private int getSmallestDIPWidth() {
        return mAppContext.getResources().getConfiguration().smallestScreenWidthDp;
    }

    private void registerListener() {
        mAppContext.registerComponentCallbacks(
                new ComponentCallbacks() {
                    @Override
                    public void onConfigurationChanged(Configuration configuration) {
                        updateNativeSharedDisplayInfo();
                    }

                    @Override
                    public void onLowMemory() {
                    }
                });
    }

    private void updateNativeSharedDisplayInfo() {
        nativeUpdateSharedDeviceDisplayInfo(
                getDisplayHeight(), getDisplayWidth(),
                getPhysicalDisplayHeight(), getPhysicalDisplayWidth(),
                getBitsPerPixel(), getBitsPerComponent(),
                getDIPScale(), getSmallestDIPWidth());
    }

    private Display getDisplay() {
        return mWinManager.getDefaultDisplay();
    }

    private DisplayMetrics getMetrics() {
        return mAppContext.getResources().getDisplayMetrics();
    }

    /**
     * Creates DeviceDisplayInfo for a given Context.
     *
     * @param context A context to use.
     * @return DeviceDisplayInfo associated with a given Context.
     */
    public static DeviceDisplayInfo create(Context context) {
        return new DeviceDisplayInfo(context);
    }

    @CalledByNative
    private static DeviceDisplayInfo createWithListener(Context context) {
        DeviceDisplayInfo deviceDisplayInfo = new DeviceDisplayInfo(context);
        deviceDisplayInfo.registerListener();
        return deviceDisplayInfo;
    }

    private native void nativeUpdateSharedDeviceDisplayInfo(
            int displayHeight, int displayWidth,
            int physicalDisplayHeight, int physicalDisplayWidth,
            int bitsPerPixel, int bitsPerComponent, double dipScale,
            int smallestDIPWidth);

}
