import React, {MutableRefObject, ReactElement, useEffect, useRef, useState} from "react";

export enum ModalWindowSize{
    Default,
    Small,
    Large,
    ExtraLarge,
}

export enum ModalWindowFullscreenMode{
    None,
    Always,
    BelowSmall,
    BelowMedium,
    BelowLarge,
    BelowExtraLarge,
    BelowExtraExtraLarge,
}

export interface IModalWindowComponentBaseProps extends React.PropsWithChildren{
    id? : string | undefined | null,
    labelId? : string | undefined | null,
    tabIndex? : number | null,
    zIndex? : number | null,
    size?: ModalWindowSize | undefined | null,
    fullscreen_mode?: ModalWindowFullscreenMode | undefined | null,
    fade?: boolean | undefined | null,
    show?: boolean | undefined | null,
    showBackdrop?: boolean | undefined | null,
    vertically_centered?: boolean | undefined | null,
    scrollable?: boolean | undefined | null,
    respectMouseInsideOut?: boolean | undefined | null,
    onOutsideClicked?: () => void,
    children: ReactElement
}
export default function ModalWindowComponentBase(props: IModalWindowComponentBaseProps){
    const [show, setShow] = useState<boolean>(false);
    const [id, setId] = useState<string>('');

    const modalRef = useRef<HTMLDivElement | null>(null);
    const modalContentRef = useRef<HTMLDivElement | null>(null);
    
    useEffect(() =>{
        if (props.id != id && (props.id != null && props.id != '' && id != '')){
            setId(props.id != null && props.id != '' ? props.id : Math.random().toString(36).substring(2, 9));
        }
        const propsShow = props.show ?? false;
        if (propsShow != show){
            setShow(propsShow);
        }
    }, [ props ])
    
    return (
        <>
            {show &&
                <>
                    {props.showBackdrop == true &&
                        <div className="modal-backdrop show" style={{zIndex:(props.zIndex ?? 1250) - 1}}></div>
                    }
                    <div className={'modal' + (props.fade ? ' fade' : '') + ' show'} id={id}
                         tabIndex={props.tabIndex ?? -1}
                         aria-labelledby={props.labelId ?? undefined} aria-modal="true" role="dialog" ref={modalRef}
                         style={{display: 'block',zIndex:props.zIndex ?? 1250}}
                         onMouseDown={(e) => handleMouseDown(e, modalRef, modalContentRef)}
                         onMouseUp={(e) => handleMouseUp(e, modalRef, modalContentRef, props)}
                        >
                        <div
                            className={'modal-dialog' + SizeToClass(props.size) + FullscreenModeToClass(props.fullscreen_mode ?? null) +
                                (props.vertically_centered  == true ? ' modal-dialog-centered' : '') + (props.scrollable == true ? ' modal-dialog-scrollable' : '')}>
                            <div className="modal-content" ref={modalContentRef}> {props.children} </div>
                        </div>
                    </div>
                </>
            }
        </>
    );
}

function handleMouseDown(e: React.MouseEvent<HTMLElement>, modalRef: MutableRefObject<HTMLDivElement | null>,
                         modalContentRef: MutableRefObject<HTMLDivElement | null>) {
    if (!modalRef.current){
        return;
    }
    if (modalContentRef.current && modalContentRef.current.contains(e.target as Node)) {
        (modalRef.current as any).mouseDownTarget = e.target;
    }
    else {
        (modalRef.current as any).mouseDownTarget = null;
    }
}

function handleMouseUp  (e: React.MouseEvent<HTMLElement>, modalRef: MutableRefObject<HTMLDivElement | null>,
                         modalContentRef :MutableRefObject<HTMLDivElement | null>, props: IModalWindowComponentBaseProps) {
    if (!modalRef.current || !modalContentRef.current) {
        return;
    }

    const mouseUpInside = modalContentRef.current.contains(e.target as Node);
    if (mouseUpInside){
        return;
    }

    const mouseDownInside = modalContentRef.current.contains((modalRef.current as any).mouseDownTarget);

    if ((props.respectMouseInsideOut == true && !mouseDownInside) || props.respectMouseInsideOut != true){
        props.onOutsideClicked?.();
    }
}

function SizeToClass(size?: ModalWindowSize | null): string {
    let result = '';
    switch (size) {
        case ModalWindowSize.Small:
            result = 'modal-sm';
            break;
        case ModalWindowSize.Large:
            result = 'modal-lg';
            break;
        case ModalWindowSize.ExtraLarge:
            result = 'modal-xl';
            break;
        // case ModalWindowSize.Default:
        default:
            break;
    }
    if (result.length > 0) {
        result = ' ' + result;
    }
    return result;
}

function FullscreenModeToClass(fullscreenMode?: ModalWindowFullscreenMode | null): string {
    let result = 'modal-fullscreen';
    switch(fullscreenMode){
        case null:
        case ModalWindowFullscreenMode.None:
            return '';
        case ModalWindowFullscreenMode.BelowSmall:
            result += '-sm-down';
            break;
        case ModalWindowFullscreenMode.BelowMedium:
            result += '-md-down';
            break;
        case ModalWindowFullscreenMode.BelowLarge:
            result += '-lg-down';
            break;
        case ModalWindowFullscreenMode.BelowExtraLarge:
            result += '-xl-down';
            break;
        case ModalWindowFullscreenMode.BelowExtraExtraLarge:
            result += '-xxl-down';
            break;
        // case ModalWindowFullscreenMode.Always:
        default:
            break;
    }
    if (result.length > 0){
        result = ' ' + result;
    }
    return result;
}