<script setup lang="ts">
import {
    AnyTypes,
    type CommandParameterData,
    type DynamicDataType,
    type OptionalDynamicComponentData,
} from '@/composables/dynamic.ts';
import { computed, defineAsyncComponent, inject, type Ref, ref } from 'vue';
import CommandParameterListConfigurator from '@/components/CommandParameterListConfigurator.vue';

const TelemetryValue = defineAsyncComponent(
    () => import('@/components/TelemetryValue.vue'),
);
const GridLayout = defineAsyncComponent(
    () => import('@/components/layout/GridLayout.vue'),
);
const CommandInput = defineAsyncComponent(
    () => import('@/components/CommandInput.vue'),
);

const model = defineModel<OptionalDynamicComponentData>('data', {
    required: true,
});

const selection = defineModel<symbol>('selection');

const props = defineProps<{
    editable: boolean;
}>();

const busy = ref(false);

// Provide a fallback option
const inputs = inject<Ref<{ [id: string]: DynamicDataType }>>(
    'inputs',
    ref({}),
);

const thisSymbol = Symbol();

const isSelected = computed(() => {
    return selection.value == thisSymbol && props.editable;
});

function selectThis(e: Event) {
    if (props.editable) {
        // Only do this when we are editable
        e.stopPropagation();
        e.preventDefault();
    }
    selection.value = thisSymbol;
}

function deleteThis() {
    model.value = {
        type: 'none',
    };
}

function makeText() {
    model.value = {
        type: 'text',
        text: '',
        justify_right: false,
    };
}

function makeTelemetry() {
    model.value = {
        type: 'telemetry',
        data: '',
    };
}

function makeGrid() {
    model.value = {
        type: 'grid',
        columns: 1,
        equal_width: false,
        cells: [],
    };
}

function addRow() {
    const grid = model.value;
    if (grid.type == 'grid') {
        const row: OptionalDynamicComponentData[] = [];
        for (let i = 0; i < grid.columns; i++) {
            row.push({ type: 'none' });
        }
        grid.cells.push(row);
        model.value = grid;
    }
}

function deleteRow() {
    const grid = model.value;
    if (grid.type == 'grid') {
        grid.cells.pop();
        model.value = grid;
    }
}

function addColumn() {
    const grid = model.value;
    if (grid.type == 'grid') {
        for (let i = 0; i < grid.cells.length; i++) {
            grid.cells[i].push({ type: 'none' });
        }
        grid.columns += 1;
        model.value = grid;
    }
}

function deleteColumn() {
    const grid = model.value;
    if (grid.type == 'grid') {
        for (let i = 0; i < grid.cells.length; i++) {
            grid.cells[i].pop();
        }
        grid.columns -= 1;
        model.value = grid;
    }
}

function makeInput() {
    model.value = {
        type: 'input',
        id: [...Array(32)]
            .map(() => Math.floor(Math.random() * 16).toString(16))
            .join(''),
        data_type: 'Float32',
    };
}

function makeCommandButton() {
    model.value = {
        type: 'command_button',
        text: 'Button Text',
        command_name: '',
        parameters: {},
    };
}

async function sendCommand(command: {
    command_name: string;
    parameters: { [key: string]: CommandParameterData };
}) {
    busy.value = true;

    const params: { [key: string]: DynamicDataType } = {};
    for (const param_name in command.parameters) {
        const parameter = command.parameters[param_name];
        switch (parameter.type) {
            case 'constant':
                params[param_name] = parameter.value;
                break;
            case 'input':
                params[param_name] = inputs.value[parameter.id];
                break;
        }
    }

    await fetch(`/api/cmd/${command.command_name}`, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify(params),
    });
    busy.value = false;
}
</script>

<template>
    <Teleport v-if="isSelected" to="#inspector">
        <div class="row">
            <button
                v-if="model.type != 'none'"
                @click.stop.prevent="deleteThis"
            >
                Delete
            </button>
            <button v-if="model.type != 'text'" @click.stop.prevent="makeText">
                Make Text
            </button>
            <button
                v-if="model.type != 'telemetry'"
                @click.stop.prevent="makeTelemetry"
            >
                Make Telemetry
            </button>
            <button v-if="model.type != 'grid'" @click.stop.prevent="makeGrid">
                Make Grid
            </button>
            <button
                v-if="model.type != 'input'"
                @click.stop.prevent="makeInput"
            >
                Make Input
            </button>
            <button
                v-if="model.type != 'command_button'"
                @click.stop.prevent="makeCommandButton"
            >
                Make Command Button
            </button>
        </div>
    </Teleport>
    <template v-if="model.type == 'none'">
        <span
            :class="`${editable ? 'editable' : ''} ${isSelected ? 'selected' : ''}`"
            @click="selectThis"
        ></span>
    </template>
    <template v-else-if="model.type == 'text'">
        <span
            :class="`${model.justify_right ? 'justify-right' : ''} ${editable ? 'editable' : ''} ${isSelected ? 'selected' : ''}`"
            @click="selectThis"
        >
            {{ model.text }}
        </span>
        <Teleport v-if="isSelected" to="#inspector">
            <div class="row">
                <label>Text: </label>
                <input v-model="model.text" />
            </div>
            <div class="row">
                <label>Justify Right: </label>
                <input type="checkbox" v-model="model.justify_right" />
            </div>
        </Teleport>
    </template>
    <template v-else-if="model.type == 'telemetry'">
        <span
            v-if="editable"
            :class="`${editable ? 'editable' : ''} ${isSelected ? 'selected' : ''}`"
            @click="selectThis"
        >
            {{ '{' }} {{ model.data }} {{ '}' }}
        </span>
        <TelemetryValue
            v-else
            :class="`${editable ? 'editable' : ''} ${isSelected ? 'selected' : ''}`"
            :data="model.data"
            @click="selectThis"
        ></TelemetryValue>
        <Teleport v-if="isSelected" to="#inspector">
            <label>Telemetry Item: </label>
            <input v-model="model.data" />
        </Teleport>
    </template>
    <template v-else-if="model.type == 'grid'">
        <GridLayout
            :class="`${editable ? 'editable' : ''} ${isSelected ? 'selected' : ''}`"
            :cols="model.columns"
            :equal_col_width="model.equal_width"
            @click="selectThis"
        >
            <template v-for="x in model.cells.length" :key="x">
                <template v-for="y in model.columns" :key="y">
                    <DynamicComponent
                        v-model:data="model.cells[x - 1][y - 1]"
                        :editable="editable"
                        v-model:selection="selection"
                    ></DynamicComponent>
                </template>
            </template>
        </GridLayout>
        <Teleport v-if="isSelected" to="#inspector">
            <div class="row">
                <label>Equal Width: </label>
                <input type="checkbox" v-model="model.equal_width" />
            </div>
            <div class="row">
                <button @click.stop.prevent="addRow">Add Row</button>
                <button
                    :disabled="model.cells.length <= 0"
                    @click.stop.prevent="deleteRow"
                >
                    Delete Row
                </button>
                <button @click.stop.prevent="addColumn">Add Column</button>
                <button
                    :disabled="model.columns <= 0"
                    @click.stop.prevent="deleteColumn"
                >
                    Delete Column
                </button>
            </div>
        </Teleport>
    </template>
    <template v-else-if="model.type == 'input'">
        <span
            v-if="editable"
            :class="`${editable ? 'editable' : ''} ${isSelected ? 'selected' : ''}`"
            @click="selectThis"
        >
            {{ '[' }} {{ model.id }} {{ ']' }}
        </span>
        <CommandInput
            v-else
            :type="model.data_type"
            v-model="inputs[model.id]"
        ></CommandInput>
        <Teleport v-if="isSelected" to="#inspector">
            <div class="row">
                <label>Input ID: </label>
                <input v-model="model.id" />
            </div>
            <div class="row">
                <label>Data Type: </label>
                <select v-model="model.data_type">
                    <option v-for="type in AnyTypes" :key="type" :value="type">
                        {{ type }}
                    </option>
                </select>
            </div>
        </Teleport>
    </template>
    <template v-else-if="model.type == 'command_button'">
        <button
            :disabled="busy"
            :class="`${editable ? 'editable' : ''} ${isSelected ? 'selected' : ''}`"
            @click.stop.prevent="
                (e) => (editable ? selectThis(e) : sendCommand(model as any))
            "
        >
            {{ model.text }}
        </button>
        <Teleport v-if="isSelected" to="#inspector">
            <div class="row">
                <label>Button Text: </label>
                <input v-model="model.text" />
            </div>
            <div class="row">
                <label>Command: </label>
                <input v-model="model.command_name" />
            </div>
            <CommandParameterListConfigurator
                :key="model.command_name"
                :command_name="model.command_name"
                v-model="model.parameters"
            ></CommandParameterListConfigurator>
        </Teleport>
    </template>
    <template v-else> ERROR: Unknown data: {{ model }} </template>
</template>

<style scoped lang="scss">
@use '@/assets/variables';
</style>
