import {ChangeEvent, memo, ReactNode, useEffect, useState} from 'react';

import {
    InputAdornment, SxProps,
    TextField, Theme
} from "@mui/material";

type MyProps = {
    start_val?: number,
    default?: number,
    label: string,
    callback: (val: number, err: boolean) => void,
    counter?: number,
    min?: number,
    max?: number,
    sx?: SxProps<Theme>,
    units?: string,
    disabled?: boolean,
    fullWidth?: boolean,
    float?: boolean
}

function unitsNode(str: string | undefined): ReactNode | null {
    if (str === undefined)
        return null;
    else if (str === "us")
        return <InputAdornment position="end">&micro;s</InputAdornment>;
    else if (str === "A-1")
        return <InputAdornment position="end">
            <span>&#8491;<sup>-1</sup></span>
        </InputAdornment>;
    else
        return <InputAdornment position="end">{str}</InputAdornment>;
}

function NumberTextField({start_val, default: defaultValue, label, callback, counter, min, max, sx, units, disabled, float}: MyProps) {
    const [text, setText] = useState(".");
    const [err, setErr] = useState(true);
    const [val, setVal] = useState(0);

    // Something has to change in parent to trigger setup => otherwise this makes infinite loop.
    // In case of error state start_val could stay same, but we want to return to "safe" set
    // so there is external counter that if changed reset is triggered.
    useEffect(() => {
        let v : number = 0;
        if (start_val !== undefined)
            v = start_val;
        else if (defaultValue !== undefined)
            v = defaultValue;

        if (float !== true)
            v = Math.round(v);
        else
            v = Math.round(v * 1000.0) / 1000.0

        setErr(false);
        setVal(v);
        setText(v.toString());
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [counter, start_val]);

    const updateValue = (event: ChangeEvent<HTMLInputElement>) => {
        let new_text = event.target.value;
        if (!new_text) new_text = "0";

        let num_val : number = Number(new_text);

        let new_err : boolean = !Number.isFinite(num_val)
            || (!float && !Number.isInteger(num_val))
            || ((min !== undefined) && (num_val < min))
            || ((max !== undefined) && (num_val > max));

        // If error I keep old numeric value to return (so there is always a proper value kept)
        let new_val : number = new_err ? val : num_val;
        setErr(new_err);
        setVal(new_val);
        setText(new_text);
        callback(new_val, new_err);
    };

    return <TextField id="frame_time" label={label} variant="outlined"
                      sx = {sx}
                      error={err}
                      onChange={updateValue}
                      value={text}
                      disabled={disabled}
                      InputProps={{
                          inputProps: {
                              style: {textAlign: 'right'}
                          },
                          endAdornment: unitsNode(units)
                      }}/>
}

export default memo(NumberTextField);
