import { Component, ReactNode } from "react";
import { Icon } from "@fluentui/react";
import { IMixinStatusProps, useStatusWithEvent } from "@Context/Status";
import { IMixinSettingProps, useSettingWithEvent } from "@Context/Setting";
import { BackgroundLevel, FontLevel, getClassList, Theme } from "@Component/Theme/Theme";
import { Popup as PopupModel, ResizeDragDirection } from "@Context/Popups";
import "./Popup.scss";

interface IPopupProps {}

@useSettingWithEvent("themes")
@useStatusWithEvent("popupChange")
class Popup extends Component<IPopupProps & IMixinStatusProps & IMixinSettingProps> {

    private renderMask(index?: number, click?: () => void, key?: string): ReactNode {
        const classList: string[] = ["popup-mask", "show-fade", 
            ...getClassList({}, this.props.setting)
        ];
        return <div
            key={key}
            onClick={click}
            className={classList.join(" ")}
            style={{
                zIndex: index,
            }}
        />
    }

    private renderRootMask(): ReactNode {
        if (this.props.status) {
            const needMask = this.props.status.popup.popups.some(popup => popup.needMask);
            if (!needMask) return null;
            return this.renderMask(this.props.status.popup.zIndex,
                () => {
                    this.props.status?.popup.popups.forEach(
                        popup => popup.onClose()
                    )
                }
            );
        } else {
            return null;
        }
    }

    private renderMaskList(): ReactNode {
        if (this.props.status) {
            return this.props.status.popup.popups
            .filter((popup) => {
                return popup.needMask && popup.maskForSelf;
            })
            .filter((_, index) => {
                if (index === 0) return false;
                return true;
            })
            .map((popup) => {
                return this.renderMask(popup.zIndex() - 1,
                    () => {
                        popup.onClose();
                    }, popup.id
                );
            })
        } else {
            return null;
        }
    }

    private renderHeader(popup: PopupModel): ReactNode {
        return <div
            className={getClassList({
                className: "popup-layer-header",
                backgroundLevel: BackgroundLevel.Level3,
                fontLevel: FontLevel.Level3
            }, this.props.setting).join(" ")}
        >
            <div
                className="header-text"
                onMouseDown={(e) => {
                    popup.isOnMouseDown = true;
                    popup.lastMouseLeft = e.clientX;
                    popup.lastMouseTop = e.clientY;
                }}
            >
                {popup.onRenderHeader()}
            </div>
            <div
                className="header-close-icon"
                onClick={() => {
                    popup.onClose();
                }}
            >
                <Icon iconName="CalculatorMultiply"/>
            </div>
        </div>
    }

    private renderContent(popup: PopupModel) {
        return <div
            className={getClassList({
                className: "popup-layer-content",
                backgroundLevel: BackgroundLevel.Level4,
                fontLevel: FontLevel.normal
            }, this.props.setting).join(" ")}
        >
            {popup.render()}
        </div>
    }

    private renderDragBlock(dir: ResizeDragDirection, popup: PopupModel) {
        return <div className="render-drag-block-root">
            <div
                draggable={false}
                style={{
                    cursor: this.mapDirToCursor.get(dir),
                    zIndex: popup.zIndex() + 2
                }}
                className="render-drag-block"
                onMouseDown={(e) => {
                    popup.lastMouseLeft = e.clientX;
                    popup.lastMouseTop = e.clientY;
                    popup.resizeDragDirection = dir;
                    popup.isResizeMouseDown = true;
                }}
                onMouseEnter={() => {
                    popup.resizeHoverDirection = dir;
                    this.forceUpdate();
                }}
                onMouseLeave={() => {
                    popup.resizeHoverDirection = undefined;
                    this.forceUpdate();
                }}
            />
        </div>
    }

    private mapDirToCursor = new Map<ResizeDragDirection, string>([
        [ResizeDragDirection.rightTop, "sw-resize"],
        [ResizeDragDirection.rightBottom, "nw-resize"],
        [ResizeDragDirection.leftBottom, "sw-resize"],
        [ResizeDragDirection.LeftTop, "nw-resize"]
    ]);

    private renderDragLine(dir: ResizeDragDirection, popup: PopupModel) {
        let xy: boolean = false;
        const dragLineCList: string[] = ["drag-line"];

        if (dir === ResizeDragDirection.top || dir === ResizeDragDirection.bottom) {
            xy = false;
        }
        if (dir === ResizeDragDirection.left || dir === ResizeDragDirection.right) {
            xy = true;
        }
        if (
            (
                dir === ResizeDragDirection.top && 
                (
                    popup.resizeHoverDirection === ResizeDragDirection.LeftTop ||
                    popup.resizeHoverDirection === ResizeDragDirection.rightTop
                )    
            ) ||
            (
                dir === ResizeDragDirection.bottom && 
                (
                    popup.resizeHoverDirection === ResizeDragDirection.leftBottom ||
                    popup.resizeHoverDirection === ResizeDragDirection.rightBottom
                )    
            ) ||
            (
                dir === ResizeDragDirection.right && 
                (
                    popup.resizeHoverDirection === ResizeDragDirection.rightTop ||
                    popup.resizeHoverDirection === ResizeDragDirection.rightBottom
                )    
            ) ||
            (
                dir === ResizeDragDirection.left && 
                (
                    popup.resizeHoverDirection === ResizeDragDirection.leftBottom ||
                    popup.resizeHoverDirection === ResizeDragDirection.LeftTop
                )    
            )
        ) {
            dragLineCList.push("hover")
        }
        
        return <div
            className={"drag-line-root" + (xy ? " drag-line-y" : "")}
            style={{
                width: xy ? "0" : "100%",
                height: xy ? "100%" : "0",
                zIndex: popup.zIndex() + 1
            }}
        >
            {
                xy && dir === ResizeDragDirection.left ? this.renderDragBlock(
                    ResizeDragDirection.LeftTop, popup
                ) : null
            }
            {
                xy && dir === ResizeDragDirection.right ? this.renderDragBlock(
                    ResizeDragDirection.rightTop, popup
                ) : null
            }
            {
                !xy && dir === ResizeDragDirection.bottom ? this.renderDragBlock(
                    ResizeDragDirection.leftBottom, popup
                ) : null
            }
            <div
                draggable={false}
                className={dragLineCList.join(" ")}
                style={{
                    cursor: xy ? "e-resize" : "n-resize",
                    minWidth: xy ? "4px" : "calc( 100% + 2px )",
                    minHeight: xy ? "calc( 100% + 2px )" : "4px"
                }}
                onMouseDown={(e) => {
                    popup.lastMouseLeft = e.clientX;
                    popup.lastMouseTop = e.clientY;
                    popup.resizeDragDirection = dir;
                    popup.isResizeMouseDown = true;
                }}
            />
            {
                !xy && dir === ResizeDragDirection.bottom ? this.renderDragBlock(
                    ResizeDragDirection.rightBottom, popup
                ) : null
            }
        </div>
    }

    private renderLayer(popup: PopupModel) {
        const pageWidth = document.documentElement.clientWidth;
        const pageHeight = document.documentElement.clientHeight;
        const hasAnimate = !popup.isInit;
        popup.isInit = true;

        if (isNaN(popup.top)) {
            popup.top = (pageHeight - popup.height) / 2;
        }
        if (isNaN(popup.left)) {
            popup.left = (pageWidth - popup.width) / 2;
        }

        return <Theme
            key={popup.id}
            style={{
                width: popup.width,
                height: popup.height,
                zIndex: popup.zIndex(),
                top: popup.top,
                left: popup.left
            }}
            className={getClassList({
                className: "popup-layer" + (hasAnimate ? " show-scale" : ""),
                backgroundLevel: BackgroundLevel.Level4,
            }, this.props.setting).join(" ")}
            onClick={() => {
                if (this.props.status) {
                    this.props.status.popup.topping(popup);
                }
            }}
        >
            {this.renderDragLine(ResizeDragDirection.top, popup)}
            <div className="popup-layer-container">
                {this.renderDragLine(ResizeDragDirection.left, popup)}
                <div className="popup-layer-root-content">
                    {this.renderHeader(popup)}
                    {this.renderContent(popup)}
                </div>
                {this.renderDragLine(ResizeDragDirection.right, popup)}
            </div>
            {this.renderDragLine(ResizeDragDirection.bottom, popup)}
        </Theme>
    }

    private isMouseDown: boolean = false;

    private handelMouseDown = () => {
        this.isMouseDown = true;
    }

    private handelMouseUp = () => {
        this.isMouseDown = false;
        if (this.props.status) {
            this.props.status.popup.popups.forEach((popup) => {
                popup.isOnMouseDown = false;
                popup.resizeDragDirection = undefined;
                popup.isResizeMouseDown = false;
            });
        }
    }

    private resize(popup: PopupModel, dis: number, dir: boolean, lsk: boolean) {
        
        if (dir) {
            // Y
            popup.isResizeOverFlowY = false;
            const heightBackup = popup.height
            const topBackup = popup.top;
            if (lsk) {
                popup.height += dis;
            } else {
                popup.top += dis;
                popup.height -= dis;
            }
            if (popup.height < popup.minHeight) {
                popup.height = heightBackup;
                popup.top = topBackup;
                popup.isResizeOverFlowY = true;
            }
        } else {
            // X
            popup.isResizeOverFlowX = false;
            const leftBackup = popup.left
            const widthBackup = popup.width;
            if (lsk) {
                popup.width += dis;
            } else {
                popup.left += dis;
                popup.width -= dis;
            }
            if (popup.width < popup.minWidth) {
                popup.width = widthBackup;
                popup.left = leftBackup;
                popup.isResizeOverFlowX = true;
            }
        }
    }

    private handelMouseMove = (e: MouseEvent) => {
        let isActionSuccess: boolean = false;
        if (
            this.isMouseDown &&
            this.props.status &&
            this.props.status.popup.popups.some(popup => popup.isOnMouseDown)
        ) {
            this.props.status.popup.popups.forEach((popup) => {
                if (popup.isOnMouseDown) {
                    popup.top += e.clientY - popup.lastMouseTop;
                    popup.left += e.clientX - popup.lastMouseLeft;
                    popup.lastMouseLeft = e.clientX;
                    popup.lastMouseTop = e.clientY;
                    isActionSuccess = true;
                    this.forceUpdate();
                }
            });
        }
        if (this.props.status) {
            this.props.status.popup.popups.forEach((popup) => {
                if (popup.resizeDragDirection) {
                        
                    let moveX = e.clientX - popup.lastMouseLeft;
                    let moveY = e.clientY - popup.lastMouseTop;
                    switch (popup.resizeDragDirection) {

                        case ResizeDragDirection.LeftTop:
                            this.resize(popup, moveX, false, false);
                            this.resize(popup, moveY, true, false);
                            break;

                        case ResizeDragDirection.leftBottom:
                            this.resize(popup, moveX, false, false);
                            this.resize(popup, moveY, true, true);
                            break;

                        case ResizeDragDirection.rightTop:
                            this.resize(popup, moveX, false, true);
                            this.resize(popup, moveY, true, false);
                            break;

                        case ResizeDragDirection.rightBottom:
                            this.resize(popup, moveX, false, true);
                            this.resize(popup, moveY, true, true);
                            break;

                        case ResizeDragDirection.top:
                            this.resize(popup, moveY, true, false);
                            break;

                        case ResizeDragDirection.left:
                            this.resize(popup, moveX, false, false);
                            break;

                        case ResizeDragDirection.bottom:
                            this.resize(popup, moveY, true, true);
                            break;

                        case ResizeDragDirection.right:
                            this.resize(popup, moveX, false, true);
                            break;
                    }
                    if (!popup.isResizeOverFlowX) {
                        popup.lastMouseLeft = e.clientX;
                    }
                    if (!popup.isResizeOverFlowY) {
                        popup.lastMouseTop = e.clientY;
                    }
                    isActionSuccess = true;
                    this.forceUpdate();
                }
            });
        }
        if (isActionSuccess) {
            e.preventDefault();
        }
    }

    public componentDidMount() {
        window.addEventListener("mousemove", this.handelMouseMove);
        window.addEventListener("mousedown", this.handelMouseDown);
        window.addEventListener("mouseup", this.handelMouseUp);
    }

    public componentWillUnmount() {
        window.removeEventListener("mousemove", this.handelMouseMove);
        window.removeEventListener("mousedown", this.handelMouseDown);
        window.removeEventListener("mouseup", this.handelMouseUp);
    }

    public render(): ReactNode {
        return <>
            {this.renderRootMask()}
            {this.renderMaskList()}
            {this.props.status?.popup.popups.map((popup) => {
                return this.renderLayer(popup);
            })}
        </>;
    }
}

export { Popup };