import * as React from "react";
import { Play, Bug, StepForward, RotateCcw, Square, ChevronUp, ChevronDown, LayoutGrid } from "lucide-react";
import { CodeBlockContainer, INIT_CODEBLOCKS, CodeBlock, INSERT_CODEBLOCK, REMOVE_CODEBLOCK, SET_DROPZONE_Z_INDEX } from "../store/codeBlocks/types";
import { LevelInitializer, IPlayerData } from "../game/levels";
import { initCodeBlocks, insertCodeBlock, removeCodeBlock } from "../store/codeBlocks/actions";
import { AppState } from "../store";
import game from "../game/game"
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { START_EXECUTING, STEP_EXECUTION, TOGGLE_AUTOSTEP, TOGGLE_SPEED } from "../store/executionState/types";



const CodeBlockComponent = ({ type, blockIdx, remove, container, setDropZoneZIdx }) => {
    return (<div className="op"
        draggable={true}
        onDrag={(e) =>
            e.preventDefault()}
        onDragStart={(e) => {
            // e.preventDefault()
            for (let e of document.getElementsByClassName("opdropzone")) {
                (e as HTMLElement).style.background = ""
            }
            // console.log("BOOM START DRAGGOING");
            (e.target as HTMLElement).style.zIndex = "3"

            setDropZoneZIdx(2)

            dragged = type
        }}
        onDragEnd={(e) => {
            e.preventDefault()
            for (let e of document.getElementsByClassName("opdropzone")) {
                (e as HTMLElement).style.background = ""
            }
            (e.target as HTMLElement).style.zIndex = ""
            setDropZoneZIdx(-1)
            remove(container, blockIdx)
            // console.log("LE END OF DRAG")
        }}
    > <img src={"assets/icons/" + type + ".svg"}></img>
    </div >)
}

// show how many operations are possible
const OpIndicators = ({ n }) => {
    let ops = []
    for (let i = 0; i < n; i++) {
        ops.push(<div className="opindicator"
            key={i}
        ></div>)
    }
    return (<div className="opindicators">{ops}</div>)
}

// dropzones which determine how to insert
const OpDropZones = ({ n, cidx, insert, style }) => {
    let dropZones = []
    for (let i = 0; i < n; i++) {
        dropZones.push(
            <div className="opdropzone"
                key={i}
                onDragEnter={(e) => {
                    e.preventDefault();
                    (e.target as HTMLElement).style.background = "purple"
                }}
                onDragLeave={(e) => {
                    e.preventDefault();
                    (e.target as HTMLElement).style.background = ""
                }}
                onDragOver={(e) => { e.preventDefault() }}
                onDrop={(e) => {
                    e.preventDefault()
                    let dragOver = ((e.target as HTMLElement).className);
                    // console.log(dragOver)
                    // console.log("DRAGGED", dragged)
                    insert({ name: dragged }, cidx, i, true)
                }}>
                <div className="frontdropper"
                    onDragEnter={e => {
                        (e.target as HTMLElement).style.background = "purple"
                    }}
                    onDragOver={e => { e.preventDefault() }}
                    onDragLeave={e => {
                        (e.target as HTMLElement).style.background = ""
                    }}
                    onDrop={e => {
                        (e.target as HTMLElement).style.background = ""
                        insert({ name: dragged }, cidx, i, false)
                    }}>
                </div>
            </div>
        )
    }
    return (<div className="opdropzones" style={style}>{dropZones}</div>)
}

const FnContainers = ({ code, dropZoneZIdx, setDropZoneZIdx, remove, insert }) => {
    return (
        <div className="fncontainers">
            {
                code.map((cv, idx) => {
                    return (
                        <div className="fncontainer" key={idx} >
                            <h3>{cv.name + "(){"}</h3>
                            <div style={{ height: Math.floor(cv.nMaxBlocks / 4 + 1) * 4 + 'rem' }}>
                                <OpIndicators n={cv.nMaxBlocks} />
                                <div className="ops" >
                                    {cv.blocks.map((b, i) => {
                                        return (<CodeBlockComponent type={b.name}
                                            key={i}
                                            blockIdx={i}
                                            container={idx}
                                            remove={remove}
                                            setDropZoneZIdx={setDropZoneZIdx} />)
                                    })}
                                </div>
                                <OpDropZones n={cv.nMaxBlocks}
                                    cidx={idx}
                                    insert={insert}
                                    style={{ zIndex: dropZoneZIdx }} />
                            </div>
                            <h3>{"}"}</h3>
                        </div>)
                })
            }
        </div>)
}


var dragged: string;

const OpsPalette = ({ ops, setDropZoneZIdx }) => {
    let allowedOps = ops.map((e, i) => {
        return (
            <CodeBlockComponent type={e}
                key={i}
                blockIdx={0}
                container={0}
                remove={() => undefined}
                setDropZoneZIdx={setDropZoneZIdx} />)
    })
    return (<div className="opspalette">{allowedOps}</div>)
}

const RuntimeControls = ({ start, fast, step, running, level, code, autostep, toggleAutoStep, toggleSpeed, finished }) => {
    let DebugButton = () => (
        <div className="controlbutton debug"
            onClick={() => {
                start(level, code, level.playerPos)
            }}>
            <Bug size={16} />
            <span>debug</span>
        </div>
    )
    let StepButton = () => (
        <div className="controlbutton step"
            onClick={step}>
            <StepForward size={16} />
            <span>step</span>
        </div>
    )

    let StartButton = () => (
        <div className="controlbutton run"
            onClick={() => {
                start(level, code, level.playerPos)
                toggleAutoStep()
                step()
            }}>
            <Play size={16} />
            <span>run</span>
        </div>
    )
    let LevelsButton = () => (
        <Link to="/levels/" className="controlbutton levels">
            <LayoutGrid size={16} />
            <span>levels</span>
        </Link>
    )
    let ResetButton = () => (
        <div className="controlbutton reset"
            onClick={() => game.reset()}>
            <RotateCcw size={16} />
            <span>reset</span>
        </div>
    )

    let ToggleSpeedButton = () => (
        <div className="controlbutton speed"
            onClick={toggleSpeed}>
            {fast ? <ChevronDown size={16} /> : <ChevronUp size={16} />}
            <span>{fast ? "slower" : "faster"}</span>
        </div>
    )

    let AbortButton = () => (
        <div className="controlbutton abort"
            onClick={() => console.log("TODO")}>
            <Square size={16} />
            <span>abort</span>
        </div>
    )

    if (running) {
        if (autostep) {
            return (
                <div className="controlbuttons">
                    <ToggleSpeedButton />
                    <AbortButton />
                </div >
            )
        }
        return (
            <div className="controlbuttons">
                <StepButton />
            </div>
        )
    }

    if (finished) {
        return (<div className="controlbuttons">
            <ResetButton />
        </div>)
    }
    return (<div className="controlbuttons">
        <LevelsButton />
        <StartButton />
        <DebugButton />
    </div>)
}


export interface EditorProps {
    level: LevelInitializer,
    code?: Array<CodeBlockContainer>,
    init: typeof initCodeBlocks,
    insert: typeof insertCodeBlock,
    remove: typeof removeCodeBlock,
    setDropZoneZIdx: (n: number) => void
    dropZoneZIdx: number,
    start: (level: LevelInitializer, code: Array<Array<string>>, playerPos: IPlayerData) => void,
    running: boolean,
    step: () => void,
    toggleSpeed: () => void,
    autostep: boolean,
    fast: boolean,
    toggleAutoStep: () => void,
    finished: boolean
}

class Editor extends React.Component<EditorProps> {

    constructor(props: EditorProps) {
        super(props)

        let { init, level } = props
        // console.log(props)
        init(level)
        // initCodeBlocks(props.level)

    }

    componentDidMount() {
        // console.log("YES IT MOUNTS")
    }

    componentWillUnmount() {
        // console.log("ME NOW UNMOUNT!")
    }

    render() {
        return (
            <div className="editor">
                <div className="titlebar">
                    <h2 className="levelname">{this.props.level.name}</h2>

                    <RuntimeControls
                        start={this.props.start}
                        fast={this.props.fast}
                        running={this.props.running}
                        level={this.props.level}
                        code={this.props.code}
                        step={this.props.step}
                        autostep={this.props.autostep}
                        toggleAutoStep={this.props.toggleAutoStep}
                        toggleSpeed={this.props.toggleSpeed}
                        finished={this.props.finished} />
                </div>
                <FnContainers code={this.props.code}
                    dropZoneZIdx={this.props.dropZoneZIdx}
                    setDropZoneZIdx={this.props.setDropZoneZIdx}
                    remove={this.props.remove}
                    insert={this.props.insert} />
                <OpsPalette ops={this.props.level.allowedCodeBlocks}
                    setDropZoneZIdx={this.props.setDropZoneZIdx} />
            </div>);
    }
}

const mapStateToProps = (state: AppState, ownProps?: { level: LevelInitializer }) => {
    // console.log(ownProps)
    return {
        ...ownProps,
        code: state.codeBlocks,
        dropZoneZIdx: state.opDropZoneZIdx,
        running: state.executionState.running,
        autostep: state.executionState.autostep,
        finished: state.executionState.finished,
        fast: state.executionState.fast
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        init: (level: LevelInitializer) => dispatch({ type: INIT_CODEBLOCKS, level: level }),
        insert: (cb: CodeBlock, cidx: number, bidx: number, ov: boolean) =>
            dispatch({ type: INSERT_CODEBLOCK, block: cb, containerIdx: cidx, blocksIdx: bidx, overwrite: ov }),
        remove: (cidx: number, bidx: number) => {
            dispatch({
                type: REMOVE_CODEBLOCK,
                containerIdx: cidx, blocksIdx: bidx
            })
        },
        setDropZoneZIdx: (idx: number) => {
            dispatch({ type: SET_DROPZONE_Z_INDEX, zIdx: idx })
        },
        start: (level: LevelInitializer, code: Array<Array<string>>, playerPos: IPlayerData) => {
            dispatch({ type: START_EXECUTING, level: level, code: code, playerPos: playerPos })
        },
        step: () => dispatch({ type: STEP_EXECUTION }),
        toggleAutoStep: () => dispatch({ type: TOGGLE_AUTOSTEP }),
        toggleSpeed: () => dispatch({ type: TOGGLE_SPEED })
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(Editor);


