<template>
    <div vertical class="material-adjuster">
        <div class="adjust-section">
            <h3>操作</h3>
            <fieldset v-if="debugMode" style="margin: 1em 0;">
                <legend>DEBUG</legend>
                <label>上传路径ID</label>
                <input v-model="materialInfo.dirId" type="text" placeholder="材质路径ID" />
                <label>配置JSON</label>
                <input v-model="materialInfo.inputText" type="text" />
                <button class="btn-success" style="min-width: 110px;" @click="loadData">加载</button>
            </fieldset>
            <label>材质名</label>
            <input v-model.trim="materialInfo.materialName" type="text" placeholder="材质名" />

            <CfFlex gap="1em" v-if="debugMode">
                <button class="btn-success" style="min-width: 110px;" @click="HandleUpload">保存</button>
                <button class="btn-danger" style="min-width: 110px;" @click="HandleCancel">取消</button>
                <button v-if="debugMode" class="btn-primary" style="min-width: 110px;"
                    @click="HandleGenerateLogo">预览缩略图</button>
            </CfFlex>
        </div>

        <div class="adjust-section">
            <h3>模型预览</h3>
            <label>选择模型样式</label>
            <select v-model="CurrGeometry">
                <option v-for="name in Geometries" :value="name">{{ name }}</option>
            </select>
        </div>
        <div class="adjust-section" v-if="debugMode">
            <h3>纹理选择（DEBUG）</h3>
            <label>纹理链接</label>
            <input v-model.trim="debugTextureSrc" type="text" placeholder="在此键入纹理图像的URL..." />
            <button class="btn-primary" @click="scene.ChangeTextureFromUrlAsync(debugTextureSrc)"
                style="margin-left: 1em;">应用</button>
        </div>
        <div class="adjust-section">
            <h3>材质调节</h3>

            <label>金属度</label>
            <CfFlex gap="1em" class="input-range">
                <input v-model="metallic" type="range" min="0" max="1" step="0.01" />
                <input v-model="metallic" type="number" @change="ClampNumericValue" min="0" max="1" step="0.01" />
            </CfFlex>

            <label>粗糙度</label>
            <CfFlex gap="1em" class="input-range">
                <input v-model="roughness" type="range" min="0" max="1" step="0.01" />
                <input v-model="roughness" type="number" @change="ClampNumericValue" min="0" max="1" step="0.01" />
            </CfFlex>

            <label>法线强度</label>
            <CfFlex gap="1em" class="input-range">
                <input v-model="normalScale" type="range" min="0" max="1" step="0.01" />
                <input v-model="normalScale" type="number" @change="ClampNumericValue" min="0" max="1" step="0.01" />
            </CfFlex>

            <label>高光</label>
            <CfFlex gap="1em" class="input-range">
                <input v-model="emissiveIntensity" type="range" min="0" max="1" step="0.01" />
                <input v-model="emissiveIntensity" type="number" @change="ClampNumericValue" min="0" max="1" step="0.01" />
            </CfFlex>
        </div>

        <div class="adjust-section">
            <h3>纹理调节</h3>
            <label>启用纹理</label>
            <input type="checkbox" v-model="Material.useMap" @change="UpdateMaterial" />

            <label>平铺U</label>
            <select v-model="textureAdjustment.wrapS">
                <option :value="MirroredRepeatWrapping">镜像平铺</option>
                <option :value="RepeatWrapping">平铺</option>
                <option :value="ClampToEdgeWrapping">展开</option>
            </select>

            <label>平铺V</label>
            <select v-model="textureAdjustment.wrapT">
                <option :value="MirroredRepeatWrapping">镜像平铺</option>
                <option :value="RepeatWrapping">平铺</option>
                <option :value="ClampToEdgeWrapping">展开</option>
            </select>

            <label>旋转</label>
            <input v-model="textureAdjustment.rotation" type="number" step="0.01" min="0" max="359" />

            <div>
                <label>偏移</label>
                <span style="margin-right: 0.5em;">X</span>
                <input v-model="textureAdjustment.moveX" type="number" step="0.01" style="max-width: 60px;" />
                &NonBreakingSpace;
                &NonBreakingSpace;
                <span style="margin-right: 0.5em;">Y</span>
                <input v-model="textureAdjustment.moveY" type="number" step="0.01" style="max-width: 60px;" />
            </div>

            <div>
                <label>尺寸</label>
                <span style="margin-right: 0.5em;">长</span>
                <input v-model="textureAdjustment.repeatX" type="number" step="0.01" style="max-width: 60px;" />
                &NonBreakingSpace;
                &NonBreakingSpace;
                <span style="margin-right: 0.5em;">宽</span>
                <input v-model="textureAdjustment.repeatY" type="number" step="0.01" style="max-width: 60px;" />
            </div>

        </div>
    </div>
</template>

<script setup lang='ts'>
import { ref, reactive, watch, computed } from "vue"
import { useScene, type TextureAdjustment } from "../stores/sceneStore";
import CfFlex from "./CfFlex.vue";
import { DirectoryId } from "../api/Request";
import { IsNullOrWhitespace } from "../helpers/helper.string";
import { FromDeflateBase64, ToDeflatedBase64 } from "../helpers/helper.material";
import { storeToRefs } from "pinia";
import { DownloadFile } from "../helpers/helper.web";
import MathHelper from "../helpers/MathHelper";
import { ClampToEdgeWrapping, MirroredRepeatWrapping, RepeatWrapping, Wrapping } from "three";

export interface MaterialRequest {
    /** 材质名 */
    name: string;
    /** 材质logo文件 */
    logo: Blob;
    /** 序列化并Deflate压缩后的材质文件的Base64编码 */
    file: string;
}

const scene = useScene();
const { Material, CurrGeometry, Geometries } = storeToRefs(scene);

const props = defineProps<{
    name?: string;
    textures?: {name: string, src: string}[];
    /** 忽略纹理参数，提交时直接输出场景内的材质，用于某些在组件外对场景进行编辑的特殊情况（例如材质编辑模式） */
    ignoreTexture?: boolean;
}>();
const emits = defineEmits<{
    (e: 'cancel'): void;
    (e: 'submit', data: MaterialRequest[]): void;
}>();

const debugMode = ref(true);
const Textures = computed(() => props.textures || []);
const debugTextureSrc = ref("");
const textureAdjustment = ref<TextureAdjustment>({
    wrapS: MirroredRepeatWrapping,
    wrapT: MirroredRepeatWrapping,
    rotation: 0,
    repeatX: 1,
    repeatY: 1,
    moveX: 0,
    moveY: 0
});

const metallic = computed({
    get: () => Material.value.matalness,
    set: (val) => {
        Material.value.matalness = MathHelper.clamp(val, 0, 1);
        UpdateMaterial();
    }
});

const roughness = computed({
    get: () => Material.value.roughness,
    set: (val) => {
        Material.value.roughness = MathHelper.clamp(val, 0, 1);
        UpdateMaterial();
    }
});

const normalScale = computed({
    get: () => Material.value.bumpScale,
    set: (val) => {
        Material.value.bumpScale = MathHelper.clamp(val, 0, 1);
        UpdateMaterial();
    }
});

const emissiveIntensity = computed({
    get: () => Material.value.specular,
    set: (val) => {
        Material.value.specular = MathHelper.clamp(val, 0, 1);
        UpdateMaterial();
    }
});

const uploading = ref(false);
// const model = reactive({
//     metallic: Material.value.matalness,
//     roughness: Material.value.roughness,
//     normalScale: Material.value.bumpScale,
//     emissiveIntensity: Material.value.specular
// });
const materialInfo = reactive({
    dirId: DirectoryId.MaterialDir, // 正常来说是2
    materialName: props.name || '材质',
    inputText: '',
});

watch(Textures, async (val) => {
    if (val.length == 0) return;
    await scene.ChangeTextureFromUrlAsync(val[0].src);
});

watch(() => props.name, () => {
    materialInfo.materialName = props.name || '材质';
});

watch(textureAdjustment, async (val) => {
    scene.UpdateTexture();
}, { deep: true });

// 监听纹理更新
watch(() => scene.CurrTexture, (val) => {
    textureAdjustment.value = {
        wrapS: val.WrapS,
        wrapT: val.WrapT,
        rotation: val.rotation,
        repeatX: val.repeatX,
        repeatY: val.repeatY,
        moveX: val.moveX,
        moveY: val.moveY
    }
})

async function UpdateMaterial() {
    // console.log((scene.CurrentShowObject.children[0] as Mesh).material.metalness);
    // console.log(Material.value.Material.metalness);
    // console.log(scene.Material == scene.GetEditor().Material)

    // Material.value.matalness = model.metallic;
    // Material.value.roughness = model.roughness;
    // Material.value.bumpScale = model.normalScale;
    // Material.value.specular = model.emissiveIntensity;
    // Material.value.side = DoubleSide;
    await scene.UpdateMaterialAsync();
    scene.Update();
    await scene.UpdateMaterialAsync();
}

async function loadData() {
    if (!materialInfo.inputText) return;
    const json = JSON.parse(materialInfo.inputText);
    const cadFile = FromDeflateBase64(json.file);
    scene.ImportMaterialAsync(cadFile)
}

async function HandleUpload() {
    console.log(scene.GetEditor().Viewer.Scene.children);
    try {
        if (IsNullOrWhitespace(materialInfo.materialName)) {
            alert('材质名称不可为空');
            return;
        }

        uploading.value = true;
        const result = [];
        if (props.ignoreTexture) {
            // 注意更改材质内部名称
            Material.value.Name = materialInfo.materialName;
            // 将材质序列化
            const mat = {
                name: materialInfo.materialName,
                logo: await scene.GenerateMaterialLogoAsync(),
                // jsonString -> Deflate -> BinaryString -> Base64
                file: ToDeflatedBase64(await scene.SerializeMaterialAsync())
            };
            result.push(mat);
        }
        else {
            // 遍历纹理链接列表，更改纹理后将材质序列化，然后还原场景
            for (const texture of Textures.value) {
                // 注意更改材质内部名称
                Material.value.Name = texture.name;
                // 更新纹理
                await scene.ChangeTextureFromUrlAsync(texture.src);
                const mat = {
                    name: texture.name,
                    logo: await scene.GenerateMaterialLogoAsync(),
                    // jsonString -> Deflate -> BinaryString -> Base64
                    file: ToDeflatedBase64(await scene.SerializeMaterialAsync())
                };
                result.push(mat);
            }
        }

        // 还原场景纹理
        await scene.ChangeTextureFromUrlAsync(Textures.value[0].src);
        emits('submit', result);
        return result;
    } finally {
        uploading.value = false;
    }
}

function HandleCancel() {
    emits('cancel');
}

async function HandleGenerateLogo() {
    const blob = await scene.GenerateMaterialLogoAsync();
    DownloadFile("logo.png", blob);
}

function ClampNumericValue(e: InputEvent) {
    const elm = e.target as HTMLInputElement;
    const min = +elm.min;
    const max = +elm.max;

    if (isNaN(min) || isNaN(max)) return;

    elm.valueAsNumber = MathHelper.clamp(elm.valueAsNumber, min, max);
}

defineExpose({
    Upload: HandleUpload,
    Cancel: HandleCancel
})


</script>

<style scoped>
.material-adjuster
{
    padding: 1rem;
    overflow-y: auto;
}

.adjust-section
{
    border-radius: 8px;
    border: 1px solid #ccc;
    padding: 1rem;
    margin-bottom: 1rem;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    background-color: white;
}

h3
{
    margin: 0 0 0.5rem 0;
}

label
{
    display: block;
}

select
{
    width: 220px;
    padding: 4px;
    margin: 0.5rem 0;
}

.btn-success
{
    background-color: #4caf50;
    color: white;
    padding: 0.5rem 1rem;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}

.btn-success:hover
{
    background-color: #3e8e41;
}

.btn-danger
{
    background-color: #f44336;
    color: white;
    padding: 0.5rem 1rem;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}

.btn-danger:hover
{
    background-color: #da190b;
}

.btn-primary
{
    background-color: #2196f3;
    color: white;
    padding: 0.5rem 1rem;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}

.btn-primary:hover
{
    background-color: #0a7ed2;
}

.input-range
{
    max-width: 300px;

    input[type="range"]
    {
        flex: 14;
        margin: 0.5rem 0;
    }

    input[type="text"]
    {
        flex: 2;
        padding: 4px;
        margin: 0.5rem 0;
    }

    input[type="number"]
    {
        flex: 2;
        min-width: 0;
        padding: 4px;
        margin: 0.5rem 0;
        appearance: textfield;
        -moz-appearance: textfield;
        margin: 0;
    }

    input::-webkit-outer-spin-button,
    input::-webkit-inner-spin-button
    {
        -webkit-appearance: none;
        margin: 0;
    }
}
</style>