// Copyright (c) 2025 Robin E. R. Davies
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

import ResizeResponsiveComponent from './ResizeResponsiveComponent';
import { Theme } from '@mui/material/styles';
import WithStyles from './WithStyles';
import { withStyles } from "tss-react/mui";
import { createStyles } from './WithStyles';
import FullscreenIcon from '@mui/icons-material/Fullscreen';
import IconButtonEx from './IconButtonEx';
import { isDarkMode } from './DarkMode';
import Backdrop from '@mui/material/Backdrop';
import CloseIcon from '@mui/icons-material/Close';
import Rect from './Rect';

import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';

const styles = (theme: Theme) => createStyles({
    pgraph: {
        paddingBottom: 16
    }
});


export interface AutoZoomProps extends WithStyles<typeof styles> {
    leftPad: number,
    rightPad: number,
    children?: React.ReactNode,
    contentReady: boolean,
    showZoomed: boolean,
    setShowZoomed: (value: boolean) => void;
}

export interface AutoZoomState {
    screenWidth: number,
    screenHeight: number,
}



const AutoZoom = withStyles(
    class extends ResizeResponsiveComponent<AutoZoomProps, AutoZoomState> {
        private model: PiPedalModel;
        constructor(props: AutoZoomProps) {
            super(props);

            this.model = PiPedalModelFactory.getInstance();
            void this.model;  // suppress unused variable warning

            this.state = {
                screenWidth: this.windowSize.width,
                screenHeight: this.windowSize.height,
            };
        }
        mounted: boolean = false;



        onWindowSizeChanged(width: number, height: number): void {
            this.setState({ screenWidth: width, screenHeight: height });
        }


        componentDidMount() {
            super.componentDidMount();
            this.mounted = true;
            if (this.normalRef) {
                this.updateZoom(this.normalRef);
            }
        }
        componentWillUnmount() {
            this.mounted = false;
            this.cancelZoomUpdate();
            this.cancelMaximizedZoomUpdate();
            super.componentWillUnmount();
        }


        componentDidUpdate(prevProps: AutoZoomProps) {
            if (prevProps.contentReady !== this.props.contentReady) {
                if (this.normalRef) {
                    this.updateZoom(this.normalRef);
                }
                if (this.maximizedRef) {
                    this.updateMaximizedZoom();
                }
            }
        }

        resizeObserver: ResizeObserver | null = null;


        calculateZoomedClientRect(
            clientRect: Rect,
            contentWidth: number,
            contentHeight: number,
            buttonPadding: number
        ): Rect {

            console.log("Zoom: " + clientRect.toString() + " content: " + contentWidth + "x" + contentHeight + " buttonPadding: " + buttonPadding);
            let rightButtonZoom = Math.min((clientRect.width - buttonPadding) / contentWidth, clientRect.height / contentHeight);
            let topButtonZoom = Math.min((clientRect.width - buttonPadding) / contentWidth, (clientRect.height - buttonPadding) / contentHeight);

            let effectiveClientRect = clientRect.copy();
            if (rightButtonZoom > topButtonZoom) {
                effectiveClientRect.width -= buttonPadding;
            } else {
                effectiveClientRect.y += buttonPadding;
                effectiveClientRect.height -= buttonPadding;
            }

            let zoom = Math.min(effectiveClientRect.width / contentWidth, effectiveClientRect.height / contentHeight);
            if (zoom > 1) {
                zoom = 1;
            }
            let leftPad = (effectiveClientRect.width - contentWidth * zoom) / 2;
            if (leftPad < 0) {
                leftPad = 0;
            }
            let topPad = (effectiveClientRect.height - contentHeight * zoom) / 4;
            if (topPad < 0) {
                topPad = 0;
            }
            return new Rect(effectiveClientRect.x + leftPad, effectiveClientRect.y + topPad, contentWidth * zoom, contentHeight * zoom);
        }

        updateZoom(ref: HTMLDivElement) {
            if (!this.mounted || !ref) {
                return;
            }
            let content = ref.firstElementChild as HTMLElement | null;

            if (content) {
                const { leftPad, rightPad } = this.props;
                let bounds = new Rect(leftPad, 0, ref.clientWidth - leftPad - rightPad, ref.clientHeight - 8);
                let zoomedClientRect = this.calculateZoomedClientRect(
                    bounds, content.clientWidth, content.clientHeight, 48);
                content.style.left = "0px";
                content.style.top = "0px";
                content.style.transformOrigin = "0 0"; // top-left corner
                content.style.transform =
                    `translate(${zoomedClientRect.x}px, ${zoomedClientRect.y}px) scale(${zoomedClientRect.width / content.clientWidth}, ${zoomedClientRect.height / content.clientHeight})`;

                if (this.zoomButton) {
                    this.zoomButton.style.display = zoomedClientRect.width < content.clientWidth ? "block" : "none";
                    let rightSizeSpace = ref.clientWidth - zoomedClientRect.right;
                    if (rightSizeSpace - rightPad >= 48) {
                        this.zoomButton.style.top = `${zoomedClientRect.y + 24}px`;
                        this.zoomButton.style.left = `${zoomedClientRect.right + 4}px`;
                    } else {
                        this.zoomButton.style.top = `${zoomedClientRect.y - 48}px`;
                        this.zoomButton.style.left = `${zoomedClientRect.right - 64 - 12}px`;
                    }
                }

                // save the position of the Mod UI in page coordinates for later zooming.
                this.zoomStartRect = zoomedClientRect.copy();
                let refBounds = ref.getBoundingClientRect();
                this.zoomStartRect.x += refBounds.left;
                this.zoomStartRect.y += refBounds.top;
            }
        }

        updateMaximizedZoom() {
            let ref = this.maximizedRef;
            if (!this.mounted || !ref) {
                return;
            }
            let content = ref.firstElementChild as HTMLElement | null;

            if (content) {
                let leftPad = 16; let rightPad = 16;
                let topPad = 16, bottomPad = 16;
                let bounds: Rect;
                bounds = new Rect(leftPad, topPad, ref.clientWidth - leftPad - rightPad, ref.clientHeight - topPad - bottomPad);

                let zoomedClientRect = this.calculateZoomedClientRect(
                    bounds, content.clientWidth, content.clientHeight, 48);
                content.style.left = "0px";
                content.style.top = "0px";
                content.style.transformOrigin = "0 0"; // top-left corner
                content.style.transform = `translate(${zoomedClientRect.x}px, ${zoomedClientRect.y}px) scale(${zoomedClientRect.width / content.clientWidth}, ${zoomedClientRect.height / content.clientHeight})`;
                //console.log("Zoomed to: ", zoomedClientRect.toString());

            }
        }


        private animateFrameHandle: number | null = null;
        cancelZoomUpdate() {
            if (this.animateFrameHandle) {
                window.cancelAnimationFrame(this.animateFrameHandle);
                this.animateFrameHandle = null;
            }
        }
        requestZoomUpdate() {
            if (this.animateFrameHandle) {
                return; // already scheduled
            }
            this.animateFrameHandle = window.requestAnimationFrame(() => {
                this.animateFrameHandle = null;
                if (this.normalRef) {
                    this.updateZoom(this.normalRef);
                }
            });
        }
        private maximizedAnimateFrameHandle: number | null = null;

        cancelMaximizedZoomUpdate() {
            if (this.maximizedAnimateFrameHandle) {
                window.cancelAnimationFrame(this.maximizedAnimateFrameHandle);
                this.maximizedAnimateFrameHandle = null;
            }
        }
        requestMaximizedZoomUpdate() {
            if (this.maximizedAnimateFrameHandle) {
                return; // already scheduled
            }
            this.maximizedAnimateFrameHandle = window.requestAnimationFrame(() => {
                this.maximizedAnimateFrameHandle = null;
                if (this.maximizedRef) {
                    this.updateMaximizedZoom();
                }
            });
        }


        private zoomButton: HTMLElement | null = null;
        private normalRef: HTMLDivElement | null = null;

        setNormalRef(ref: HTMLDivElement | null) {
            this.normalRef = ref;
            if (ref) {
                this.zoomButton = ref.querySelector("#maximize-button") as HTMLDivElement | null;
                this.resizeObserver = new ResizeObserver(() => {
                    this.requestZoomUpdate();
                });
                this.resizeObserver.observe(ref);
                let child = ref.firstElementChild as HTMLElement | null;
                if (child) {
                    this.resizeObserver.observe(child);
                }
                this.requestZoomUpdate();
            } else {
                if (this.resizeObserver) {
                    this.resizeObserver.disconnect();
                    this.resizeObserver = null;
                    this.zoomButton = null;
                }
                this.zoomStartRect = null;
            }
        }


        private maximizedRef: HTMLDivElement | null = null;
        private maximedResizeObserver: ResizeObserver | null = null;

        setMaximizedRef(ref: HTMLDivElement | null) {
            this.maximizedRef = ref;
            if (ref) {
                this.maximedResizeObserver = new ResizeObserver(() => {
                    this.requestMaximizedZoomUpdate();
                });
                this.maximedResizeObserver.observe(ref);
                let child = ref.firstElementChild as HTMLElement | null;
                if (child) {
                    this.maximedResizeObserver.observe(child);
                }
                this.requestMaximizedZoomUpdate();
            } else {
                if (this.maximedResizeObserver) {
                    this.maximedResizeObserver.disconnect();
                    this.maximedResizeObserver = null;
                }
            }
        }

        private zoomStartRect: Rect | null = null;

        startZoom() {
            if (!this.zoomStartRect) {
                return;
            }
            this.props.setShowZoomed(true);
        }

        render() {
            const classes = withStyles.getClasses(this.props);
            void classes; // suppress unused variable warning
            const landscape = this.state.screenWidth > this.state.screenHeight;
            return (<div style={{ overflow: "hidden", height: "100%", width: "100%" }} ref={(ref) => { this.setNormalRef(ref); }}>
                <div style={{
                    display: "inline-block",
                    position: "absolute",
                    visibility: this.props.contentReady ? "visible" : "hidden",

                }}>
                    {!this.props.showZoomed && this.props.children}
                </div>
                <div id="maximize-button" style={{
                    display: "none", position: "absolute", left: 0, top: 0, width: 52, height: 64, zIndex: 1101,
                    borderRadius: "0px 26px 26px 0px",

                }}
                >
                    <IconButtonEx tooltip="Fullscreen view"
                        style={{ margin: 2, background: isDarkMode() ? "#444" : "#eee" }}
                        onClick={() => {
                            this.startZoom();
                        }}
                    >
                        <FullscreenIcon style={{ color: "white" }} />
                    </IconButtonEx>
                </div>
                {this.props.showZoomed && (
                    <Backdrop id="modGuiZoomBackdrop"
                        sx={(theme) => ({
                            zIndex: this.props.showZoomed ? 1101 : -1,
                            overflow: "clip",
                            background: theme.palette.mode === 'dark' ? 'rgba(0, 0, 0, 0.9)' : 'rgba(255, 255, 255, 0.9)'
                        })}
                        open={this.props.showZoomed}
                    >
                        <div ref={(ref) => { this.setMaximizedRef(ref); }}
                            style={{
                                position: "absolute", left: 0, top: 0, right: 0, bottom: 0, overflow: "hidden",
                                visibility: this.props.contentReady ? "visible" : "hidden"
                            }}>

                            <div style={{ display: "inline-block", position: "relative" }}>
                                {
                                    this.props.showZoomed && this.props.children
                                }
                            </div>

                            <IconButtonEx tooltip="Exit fullscreen"
                                style={{
                                    position: "absolute", 
                                        right: landscape? 80: 16, // avoid potential cutout in landscape mode
                                    top: 16, zIndex: 1104,

                                }}
                                onClick={(e) => {
                                    e.stopPropagation();
                                    this.props.setShowZoomed(false);
                                }}
                            >
                                <CloseIcon />
                            </IconButtonEx>
                        </div>
                    </Backdrop>
                )}
            </div>);
        }
    },
    styles);

export default AutoZoom;