import * as React from "react";
import {ChangeEvent, JSX, useState} from "react";
import {
    cellTypeToDetails,
    CoordinateData,
    GridArrowData,
    GridArrowDispatch,
    GridArrowDispatchActionType,
    HighlightableLetterData,
    LocationType,
    PlayableLetterData,
    TileDispatch,
    TileDispatchActionType,
} from "./utils";
import {API, APIPlayer, CellType, Difficulty} from "./api";
import {GameWasm} from "./wasm";
import {Game} from "./Game";


export function TileSlot(props: {
        tile?: React.JSX.Element | undefined,
        location: CoordinateData,
        tileDispatch: TileDispatch,
        arrowDispatch?: GridArrowDispatch,
}): React.JSX.Element {
    let isDraggable = props.tile !== undefined;

    function onDragStart(e: React.DragEvent<HTMLDivElement>) {
        e.dataTransfer.effectAllowed = "move";
        e.dataTransfer.setData("wordGrid/coords", JSON.stringify(props.location));
    }

    function onDrop(e: React.DragEvent<HTMLDivElement>) {
        const startLocation: CoordinateData = JSON.parse(e.dataTransfer.getData("wordGrid/coords"));
        const thisLocation = props.location;

        props.tileDispatch({action: TileDispatchActionType.MOVE, start: startLocation, end: thisLocation});
    }

    let className = "tileSpot";
    if (props.location.location === LocationType.GRID) {
        className += " ephemeral";
    }

    let onClick: () => void;
    if(props.arrowDispatch != null && props.location.location == LocationType.GRID) {
        onClick = () => {
            props.arrowDispatch({action: GridArrowDispatchActionType.CYCLE, position: props.location.index});
        }
    } else if(props.location.location == LocationType.TRAY && props.tile != null && props.tile.props.data.text != ' ' && props.tile.props.data.text != '') {
        onClick = () => {
            props.tileDispatch({
                action: TileDispatchActionType.MOVE_TO_ARROW, end: undefined, start: props.location,
            });
        }
    }

    return <div className={className}
        draggable={isDraggable}
        onDragStart={onDragStart}
        onDrop={onDrop}
        onClick={onClick}
        onDragOver={(e) => {e.preventDefault()}}
    >
        {props.tile}
    </div>

}

export function Letter(props: { data: HighlightableLetterData, letterUpdater?: (value: string) => void }): React.JSX.Element {

    function modifyThisLetter(value:string){
        props.letterUpdater(value);
    }


    function onBlankInput(e: ChangeEvent<HTMLInputElement>){
        let value = e.target.value.toUpperCase();
        if(value.length > 1){
            value = value[value.length - 1];
        } else if(value.length == 0){
            modifyThisLetter(value);
            return;
        }

        // Now check that it's a letter
        let is_letter = value.match("[A-Z]") != null;
        if(is_letter){
            modifyThisLetter(value);
        } else {
            // Cancel and do nothing
            e.preventDefault();
        }

    }

    if(props.data.is_blank && props.data.ephemeral) {
        return <div className="letter">
            <input className="blank-input" type="text" onChange={onBlankInput} value={props.data.text} />
            <div className="letter-points">{props.data.points}</div>
        </div>
    } else {
        let className = "text";
        if (props.data.is_blank) { // not ephemeral
            className += " prev-blank";
        }
        let letterClassName = "letter";
        if (props.data.highlight) {
            letterClassName += " highlight";
        }
        return <div className={letterClassName}>
            <div className={className}>{props.data.text}</div>
            <div className="letter-points">{props.data.points}</div>
        </div>
    }



}

export function TileTray(props: { letters: Array<PlayableLetterData>, trayLength: number, trayDispatch: TileDispatch }): React.JSX.Element {

    let elements: JSX.Element[] = [];
    for (let i=0; i<props.trayLength; i++) {
        elements.push(
            <TileSlot
                key={"empty" + i}
                location={{location: LocationType.TRAY, index: i}}
                tileDispatch={props.trayDispatch} />
        );
    }

    props.letters
        .forEach((letter, i) => {
        if (letter != null && letter.location === LocationType.TRAY) {
            elements[letter.index] =
                <TileSlot
                    tile={<Letter
                        data={{...letter, highlight: false}}
                        letterUpdater={(value) => {
                            props.trayDispatch({action: TileDispatchActionType.SET_BLANK, blankIndex: i, newBlankValue: value})
                        }}
                    />}
                    key={"letter_tray" + letter.index}
                    location={{location: LocationType.TRAY, index: letter.index}}
                    tileDispatch={props.trayDispatch} />

        }
    });

    return (
        <div className="tray">
            {elements}
        </div>
    )

}

export function Arrow(props: {
    data: GridArrowData,
    dispatch: GridArrowDispatch,
}) {

    return <div
        className={`arrow ${props.data.direction}`}
    >
        ➡
    </div>;

}

export function Grid(props: {
        cellTypes: CellType[],
        playerLetters: Array<PlayableLetterData>,
        boardLetters: HighlightableLetterData[],
        tileDispatch: TileDispatch,
        arrow: GridArrowData,
        arrowDispatch: GridArrowDispatch,
}) {

    const elements = props.cellTypes.map((ct, i) => {
        const {className, text} = cellTypeToDetails(ct);

        let tileElement: JSX.Element;
        if (props.boardLetters[i] !== undefined) {
            tileElement = <Letter data={props.boardLetters[i]} />;
        } else {
            tileElement = <>
                <span>{text}</span>
                <TileSlot
                    location={{location: LocationType.GRID, index: i}}
                    tileDispatch={props.tileDispatch}
                    arrowDispatch={props.arrowDispatch}
                />

            </>;
        }

        let arrowElement: JSX.Element;
        if(props.arrow != null && props.arrow.position == i) {
            arrowElement = <Arrow data={props.arrow} dispatch={props.arrowDispatch} />;
        }

        return <div key={i} className={"grid-spot " + className}>
            {tileElement}
            {arrowElement}
        </div>;
    });

    props.playerLetters
        .forEach((letter, i) => {
        if (letter != null && letter.location === LocationType.GRID) {
            const ct = props.cellTypes[letter.index];
            const {className} = cellTypeToDetails(ct);

            elements[letter.index] =
                <div key={"letter" + letter.index} className={"grid-spot " + className}>
                    <TileSlot
                        tile={<Letter
                            data={{...letter, highlight: false}}
                            letterUpdater={(value) => {
                                props.tileDispatch({action: TileDispatchActionType.SET_BLANK, blankIndex: i, newBlankValue: value});
                            }}
                        />}
                        location={{location: LocationType.GRID, index: letter.index}}
                        tileDispatch={props.tileDispatch} />
                </div>;


        }
    });

    return <div className="board-grid">
        {elements}
    </div>
}

export function Scores(props: {playerScores: Array<APIPlayer>}){
    let elements = props.playerScores.map((ps) => {
        return <div key={ps.name}>
            <h3>{ps.name}</h3>
            <span>{ps.score}</span>
        </div>;
    });

    return <div className="scoring">
        {elements}
    </div>
}

export function AISelection(props: {
    aiRandomness: number,
    setAIRandomness: (x: number) => void,
    proportionDictionary: number,
    setProportionDictionary: (x: number) => void,
}) {


    // Can change log scale to control shape of curve using following equation:
    // aiRandomness = log(1 + x*(n-1))/log(n) when x, the user input, ranges between 0 and 1
    const logBase: number = 10000;
    const processedAIRandomness = Math.log(1 + (logBase - 1)*props.aiRandomness/100) / Math.log(logBase);
    //const processedProportionDictionary = 1.0 - props.proportionDictionary / 100;

        return  <>
                <div className="grid">
                    <label htmlFor="proportion-dictionary">AI's proportion of dictionary:</label>
                    <input type="number"
                           name="proportion-dictionary"
                           value={props.proportionDictionary}
                           onInput={(e) => {
                               props.setProportionDictionary(e.currentTarget.valueAsNumber);
                           }}
                           min={1}
                           max={100}/>
                    <label htmlFor="randomness">Level of randomness in AI:</label>
                    <input type="number"
                           name="randomness"
                           value={props.aiRandomness}
                           onInput={(e) => {
                               props.setAIRandomness(e.currentTarget.valueAsNumber);
                           }}
                           min={0}
                           max={100}/>
                </div>
                <details>
                    <ul>
                        <li>
                            "AI's proportion of dictionary" controls what percent of the total AI dictionary
                            the AI can form words with. At 100%, it has access to its entire dictionary -
                            although this dictionary is still less than what the player has access to.</li>
                        <li>
                            <div>
                                "Level of randomness in AI" controls the degree to which the AI picks the optimal move
                                for each of its turns. At 0, it always picks the highest scoring move it can do using the
                                dictionary it has access to. At 1, it picks from its available moves at random.
                            </div>
                            <div>
                                Note that "Level of randomness in AI" is now mapped on a log scale.
                                Your current setting is equivalent to {(100*processedAIRandomness).toFixed(1)}% on the previous scale.
                            </div>
                        </li>

                    </ul>
                </details>
            </>
}