// Copyright 2013 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.content.browser;

/**
 * Cached copy of all positions and scales (CSS-to-DIP-to-physical pixels)
 * reported from the renderer.
 * Provides wrappers and a utility class to help with coordinate transforms on the client side.
 * Provides the internally-visible set of update methods (called from ContentViewCore).
 *
 * Unless stated otherwise, all coordinates are in CSS (document) coordinate space.
 */
public class RenderCoordinates {
    // Scroll offset from the native in CSS.
    private float mScrollXCss;
    private float mScrollYCss;

    // Content size from native in CSS.
    private float mContentWidthCss;
    private float mContentHeightCss;

    // Last-frame render-reported viewport size in CSS.
    private float mLastFrameViewportWidthCss;
    private float mLastFrameViewportHeightCss;

    // Cached page scale factor from native.
    private float mPageScaleFactor = 1.0f;
    private float mMinPageScaleFactor = 1.0f;
    private float mMaxPageScaleFactor = 1.0f;

    // Cached device density.
    private float mDeviceScaleFactor;

    private float mContentOffsetYPix;

    // Internally-visible set of update methods (used by ContentViewCore).
    void reset() {
        mScrollXCss = mScrollYCss = 0;
        mPageScaleFactor = 1.0f;
    }

    void updateContentSizeCss(float contentWidthCss, float contentHeightCss) {
        mContentWidthCss = contentWidthCss;
        mContentHeightCss = contentHeightCss;
    }

    void setDeviceScaleFactor(float deviceScaleFactor) {
        mDeviceScaleFactor = deviceScaleFactor;
    }

    void updateFrameInfo(
            float scrollXCss, float scrollYCss,
            float contentWidthCss, float contentHeightCss,
            float viewportWidthCss, float viewportHeightCss,
            float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor,
            float contentOffsetYPix) {
        mScrollXCss = scrollXCss;
        mScrollYCss = scrollYCss;
        mPageScaleFactor = pageScaleFactor;
        mMinPageScaleFactor = minPageScaleFactor;
        mMaxPageScaleFactor = maxPageScaleFactor;
        mContentOffsetYPix = contentOffsetYPix;

        updateContentSizeCss(contentWidthCss, contentHeightCss);
        mLastFrameViewportWidthCss = viewportWidthCss;
        mLastFrameViewportHeightCss = viewportHeightCss;
    }

    /**
     * Handles conversion of a point from window-relative-local-dip or screen-pix
     * to document-absolute-CSS space and vice versa.
     */
    public class NormalizedPoint {
        private float mXAbsoluteCss, mYAbsoluteCss;

        private NormalizedPoint() {
        }

        /**
         * @return Absolute CSS (document) X coordinate of the point.
         */
        public float getXAbsoluteCss() { return mXAbsoluteCss; }

        /**
         * @return Absolute CSS (document) Y coordinate of the point.
         */
        public float getYAbsoluteCss() { return mYAbsoluteCss; }

        /**
         * @return Local device-scale-unadjusted X coordinate of the point.
         */
        public float getXLocalDip() { return (mXAbsoluteCss - mScrollXCss) * mPageScaleFactor; }

        /**
         * @return Local device-scale-unadjusted Y coordinate of the point.
         */
        public float getYLocalDip() { return (mYAbsoluteCss - mScrollYCss) * mPageScaleFactor; }

        /**
         * @return Physical (screen) X coordinate of the point.
         */
        public float getXPix() { return getXLocalDip() * mDeviceScaleFactor; }

        /**
         * @return Physical (screen) Y coordinate of the point.
         */
        public float getYPix() { return getYLocalDip() * mDeviceScaleFactor + mContentOffsetYPix; }

        /**
         * Sets the point to the given absolute CSS (document) coordinates.
         */
        public void setAbsoluteCss(float xCss, float yCss) {
            mXAbsoluteCss = xCss;
            mYAbsoluteCss = yCss;
        }

        /**
         * Sets the point to the given local device-scale-unadjusted coordinates.
         */
        public void setLocalDip(float xDip, float yDip) {
            setAbsoluteCss(
                    xDip / mPageScaleFactor + mScrollXCss,
                    yDip / mPageScaleFactor + mScrollYCss);
        }

        /**
         * Sets the point to the given physical (screen) coordinates.
         */
        public void setScreen(float xPix, float yPix) {
            setLocalDip(xPix / mDeviceScaleFactor, yPix / mDeviceScaleFactor);
        }
    }

    /**
     * @return A helper to convert a point between between absolute CSS and local DIP spaces.
     */
    public NormalizedPoint createNormalizedPoint() {
        return new NormalizedPoint();
    }

    /**
     * @return Horizontal scroll offset in CSS pixels.
     */
    public float getScrollX() { return mScrollXCss; }

    /**
     * @return Vertical scroll offset in CSS pixels.
     */
    public float getScrollY() { return mScrollYCss; }

    /**
     * @return Horizontal scroll offset in physical pixels.
     */
    public float getScrollXPix() { return fromLocalCssToPix(mScrollXCss); }

    /**
     * @return Vertical scroll offset in physical pixels.
     */
    public float getScrollYPix() { return fromLocalCssToPix(mScrollYCss); }

    /**
     * @return Horizontal scroll offset in physical pixels (approx, integer).
     */
    public int getScrollXPixInt() { return (int) Math.floor(getScrollXPix()); }

    /**
     * @return Vertical scroll offset in physical pixels (approx, integer).
     */
    public int getScrollYPixInt() { return (int) Math.floor(getScrollYPix()); }

    /**
     * @return Width of the content in CSS pixels.
     */
    public float getContentWidthCss() { return mContentWidthCss; }

    /**
     * @return Height of the content in CSS pixels.
     */
    public float getContentHeightCss() { return mContentHeightCss; }

    /**
     * @return Approximate width of the content in physical pixels.
     */
    public float getContentWidthPix() { return fromLocalCssToPix(mContentWidthCss); }

    /**
     * @return Approximate height of the content in physical pixels.
     */
    public float getContentHeightPix() { return fromLocalCssToPix(mContentHeightCss); }

    /**
     * @return Approximate width of the content in physical pixels (integer).
     */
    public int getContentWidthPixInt() { return (int) Math.ceil(getContentWidthPix()); }

    /**
     * @return Approximate height of the content in physical pixels (integer).
     */
    public int getContentHeightPixInt() { return (int) Math.ceil(getContentHeightPix()); }

    /**
     * @return Render-reported width of the viewport in CSS pixels.
     */
    public float getLastFrameViewportWidthCss() { return mLastFrameViewportWidthCss; }

    /**
     * @return Render-reported height of the viewport in CSS pixels.
     */
    public float getLastFrameViewportHeightCss() { return mLastFrameViewportHeightCss; }

    /**
     * @return Render-reported width of the viewport in physical pixels (approximate).
     */
    public float getLastFrameViewportWidthPix() {
        return fromLocalCssToPix(mLastFrameViewportWidthCss);
    }

    /**
     * @return Render-reported height of the viewport in physical pixels (approximate).
     */
    public float getLastFrameViewportHeightPix() {
        return fromLocalCssToPix(mLastFrameViewportHeightCss);
    }

    /**
     * @return Render-reported width of the viewport in physical pixels (approx, integer).
     */
    public int getLastFrameViewportWidthPixInt() {
        return (int) Math.ceil(getLastFrameViewportWidthPix());
    }

    /**
     * @return Render-reported height of the viewport in physical pixels (approx, integer).
     */
    public int getLastFrameViewportHeightPixInt() {
        return (int) Math.ceil(getLastFrameViewportHeightPix());
    }

    /**
     * @return The Physical on-screen Y offset amount below the top controls.
     */
    public float getContentOffsetYPix() {
        return mContentOffsetYPix;
    }

    /**
     * @return Current page scale factor (maps CSS pixels to DIP pixels).
     */
    public float getPageScaleFactor() { return mPageScaleFactor; }

    /**
     * @return Minimum page scale factor to be used with the content.
     */
    public float getMinPageScaleFactor() { return mMinPageScaleFactor; }

    /**
     * @return Maximum page scale factor to be used with the content.
     */
    public float getMaxPageScaleFactor() { return mMaxPageScaleFactor; }

    /**
     * @return Current device scale factor (maps DIP pixels to physical pixels).
     */
    public float getDeviceScaleFactor() { return mDeviceScaleFactor; }

    /**
     * @return True if the page doesn't allow zoom-in/zoom-out.
     */
    public boolean hasFixedPageScale() { return mMinPageScaleFactor == mMaxPageScaleFactor; }

    /**
     * @return True if the page has a width=device-width or narrower viewport.
     */
    public boolean hasMobileViewport() {
        float windowWidthDip = mPageScaleFactor * mLastFrameViewportWidthCss;
        return mContentWidthCss <= windowWidthDip;
    }

    /**
     * @return Maximum possible horizontal scroll in physical pixels.
     */
    public float getMaxHorizontalScrollPix() {
        return getContentWidthPix() - getLastFrameViewportWidthPix();
    }

    /**
     * @return Maximum possible vertical scroll in physical pixels.
     */
    public float getMaxVerticalScrollPix() {
        return getContentHeightPix() - getLastFrameViewportHeightPix();
    }

    /**
     * @return Maximum possible horizontal scroll in physical pixels (approx, integer).
     */
    public int getMaxHorizontalScrollPixInt() {
        return (int) Math.floor(getMaxHorizontalScrollPix());
    }

    /**
     * @return Maximum possible vertical scroll in physical pixels (approx, integer).
     */
    public int getMaxVerticalScrollPixInt() {
        return (int) Math.floor(getMaxVerticalScrollPix());
    }

    /**
     * @return Physical on-screen coordinate converted to local DIP.
     */
    public float fromPixToDip(float pix) {
        return pix / mDeviceScaleFactor;
    }

    /**
     * @return Local DIP converted to physical coordinates.
     */
    public float fromDipToPix(float dip) {
        return dip * mDeviceScaleFactor;
    }

    /**
     * @return Physical coordinate converted to local CSS.
     */
    public float fromPixToLocalCss(float pix) {
        return pix / (mDeviceScaleFactor * mPageScaleFactor);
    }

    /**
     * @return Local CSS converted to physical coordinates.
     */
    public float fromLocalCssToPix(float css) {
        return css * mPageScaleFactor * mDeviceScaleFactor;
    }
}
