import { Fragment, h, render } from "preact";
import { useState } from "preact/hooks";

const formatter = new Intl.NumberFormat(undefined, { maximumFractionDigits: 2 });

type HighlightFormProps = {
    onSubmit(code: string, language: string);
}

const HighlightForm = ({ onSubmit }: HighlightFormProps) => {
    const [code, setCode] = useState<string>("");
    const [language, setLanguage] = useState<string>("");

    const handleReset = () => {
        setCode("");
        setLanguage("");
    };

    const handleSubmit = () => onSubmit && onSubmit(code, language);

    return (
        <form class="demo__form">
            <div class="form-group">
                <label for="form_source">
                    <span class="far fa-code" aria-hidden="true"/> try it
                </label>
                <textarea name="source" class="form-control" id="form_source" rows={ 15 } onInput={ ev => setCode((ev.target as HTMLTextAreaElement).value) }>{ code }</textarea>
            </div>
            <div class="form-group row">
                <label for="form_language" class="col-md-2 col-form-label">
                    <span class="far fa-lightbulb" aria-hidden="true"/> language
                </label>
                <div class="col-md-10">
                    <input type="text" name="language" id="form_language" class="form-control" required value={ language } onInput={ ev => setLanguage((ev.target as HTMLInputElement).value) }/>
                </div>
            </div>
            <div class="form-group">
                <div id="language-selector" class="standalone languages">
                </div>
            </div>
            <div class="demo__actions">
                <button type="button" class="btn btn-sm btn-outline-danger" onClick={ handleReset }>
                    <span class="far fa-trash fa-fw" aria-hidden="true"/> reset
                </button>
                <button type="button" class="btn btn-sm btn-outline-primary" onClick={ handleSubmit }>
                    <span class="far fa-code fa-fw" aria-hidden="true"/> highlight
                </button>
            </div>
        </form>
    )
};

const Loading = () => <div class="loading">
    <span class="fad fa-spinner-third fa-spin fa-10x" />
</div>;

type HighlightedCodeProps = {
    highlighted: HighlightedResponse;
    onClose?(): void;
}

const HighlightedCode = ({ highlighted, onClose }: HighlightedCodeProps) =>
    <Fragment>
        <pre class="keylighter" dangerouslySetInnerHTML={{ __html: highlighted.html }}/>

        <div class="d-flex align-items-center">
            <div class="demo__times">
                <div class="demo__time-splits">
                    <div class="demo__time-split demo__time-split--tokenization" style={{ width: `${highlighted.times.tokenization / highlighted.times.total * 100}%` }}/>
                    <div class="demo__time-split demo__time-split--processing" style={{ width: `${highlighted.times.processing / highlighted.times.total * 100}%` }}/>
                    <div class="demo__time-split demo__time-split--formatting" style={{ width: `${highlighted.times.formatting / highlighted.times.total * 100}%` }}/>
                </div>
                <div class="demo__time-total">
                    Total time: { formatter.format(highlighted.times.total) }ms
                </div>
            </div>
            <div class="demo__actions ml-auto">
                <button type="button" class="btn btn-sm btn-outline-primary" onClick={ () => onClose && onClose() }>
                    <span class="far fa-sync fa-fw mr-1" aria-hidden="true"/>
                    try another
                </button>
            </div>
        </div>
    </Fragment>;

type DemoState = "form" | "submitting" | "show";
type HighlightedResponse = {
    html: string;
    times: {
        total: number;
        tokenization: number;
        processing: number;
        formatting: number;
    }
}

const KeylighterDemo = () => {
    const [state, setState] = useState<DemoState>("form");
    const [highlighted, setHighlighted] = useState<HighlightedResponse>(null);

    const handleCodeHighlight = async (source, language) => {
        setState("submitting");
        setHighlighted(await fetch('/highlight', {
            credentials: "include",
            method: "POST",
            body: JSON.stringify({ source, language }),
            headers: {
                'Content-Type': 'application/json'
            }
        }).then(res => res.json()));
        setState("show");
    };

    return (
        <div id="try-form" class="demo">
            <div class="container">
                { state === "form" && <HighlightForm onSubmit={ handleCodeHighlight }/> }
                { state === "submitting" && <Loading /> }
                { state === "show" && <HighlightedCode highlighted={ highlighted } onClose={ () => setState("form") } /> }
            </div>
        </div>
    )
};

const root = document.getElementById('try-form');
render(<KeylighterDemo />, root.parentElement, root);
