<script setup lang="ts">
import { BufferGeometry, Group, Matrix4, Mesh, Object3D, Vector3 } from 'three';
import { getColorHexFromRal } from '../ral';

const props = defineProps(['index', 'models', 'last_element', 'first_element'])

const lamelle_height = use_lamelle_height()
const lamelles_count = use_lamelles_count()
const fence_section = use_fence_section()
const section_count = use_section_count()
const extra_section = use_extra_section()
const remove_pillar = use_remove_pillar()

const pillar_color = use_pillar_color()
const pillar_pattern = use_pattern()
const pillar_topper = use_topper()
const lamelle_color = use_lamelle_color()

const pillar_size = 104 * 0.001

const pillar_one_pos = ref()
const pillar_two_pos = ref()

const scale_koef = 1
const show_pillar_one = ref(props.first_element)
const show_pillar_two = ref(true)

const setPillarValues = () => {
    extra.value = getExtraValue()
    if (extra_section.value && props.last_element && !props.first_element) {
        pillar_one_pos.value = (extra.value as number) * -0.5
        pillar_two_pos.value = (extra.value as number) * 0.5 + pillar_size
    } else {
        pillar_one_pos.value = fence_section.value * -0.5
        pillar_two_pos.value = fence_section.value * 0.5 + pillar_size
    }
}

const getExtraValue = () => (extra_section.value && props.last_element && !props.first_element) ? extra_section.value * 0.001 : false
const extra = ref(getExtraValue())
setPillarValues()

const make_translate_to_section = (source = fence_section.value) => {
    const one_s = (source + (remove_pillar.value ? 0 : pillar_size)) * scale_koef
    let r = (props.index - 1) * one_s
    if (typeof extra.value == 'number') {
        r -= (fence_section.value - extra.value) * scale_koef * 0.5
    }
    return r
}
const translate_to_section = ref(make_translate_to_section())
watch([fence_section, extra], () => {
    translate_to_section.value = make_translate_to_section()
})

set_material({ children: [props.models.pillar_brace, props.models.fixing] }, '#111');

const instanced_lamelle = shallowRef();
const instanced_lamelle_count = 24
const instanced_lamelle_el = [
    Object.assign(new BufferGeometry, props.models.lamelle.children[0].geometry),
    props.models.lamelle.children[0].material,
    instanced_lamelle_count
]

const instanced_fixing_one = shallowRef();
const instanced_fixing_one_el = [
    Object.assign(new BufferGeometry, props.models.fixing.children[0].geometry),
    props.models.fixing.children[0].material,
    instanced_lamelle_count
]

const instanced_fixing_two = shallowRef();
const instanced_fixing_two_el = [
    Object.assign(new BufferGeometry, props.models.fixing.children[0].geometry),
    props.models.fixing.children[0].material,
    instanced_lamelle_count
]
const lamelleMatrix = (i: number) => {
    const scale_x = (((extra.value as number) || fence_section.value) * 9.9)
    const pos_x = pillar_size * 0.5
    const pos_y = (lamelle_height.value * i)
    const pos_z = 0.022 * scale_koef
    return new Matrix4().fromArray([
        scale_x, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        pos_x, pos_y, pos_z, 1
    ])
}
const fixingOneMatrix = (i: number) => {
    const pos_x = pillar_one_pos.value + pillar_size * 0.66
    const pos_y = (lamelle_height.value * i) + 0.01 * scale_koef;
    const pos_z = 0.022 * scale_koef
    return new Matrix4().fromArray([
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        pos_x, pos_y, pos_z, 1
    ])
}
const fixingTwoMatrix = (i: number) => {
    const pos_x = pillar_two_pos.value - pillar_size * 0.66
    const pos_y = (lamelle_height.value * i) + 0.01 * scale_koef;
    const pos_z = 0.022 * scale_koef
    return new Matrix4().fromArray([
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        pos_x, pos_y, pos_z, 1
    ])
}

watch([section_count, fence_section, extra_section], setPillarValues)

const brace = props.models.pillar_brace.clone().children[0]
const braces = ref<Mesh[]>([])
const setBraceCount = () => {
    const brace_count = Math.floor(lamelles_count.value / 4)
    const arr: Mesh[] = []
    for (let index = 0; index < brace_count; index++) {
        const brace_item = brace.clone()
        brace_item.position.setComponent(1, lamelle_height.value * 4 * (index + 0.75))
        arr.push(brace_item)
    }
    braces.value = arr
}

setBraceCount()
watch(lamelles_count, setBraceCount)

const calculateAddScale = (model: Mesh, k = 1): number => {
    if (!model.geometry.boundingBox) {
        model.geometry.computeBoundingBox(); // Вычисляем boundingBox, если он не был вычислен ранее
    }

    const boundingBoxHeight = Math.abs(
        model.geometry.boundingBox!.max.y - model.geometry.boundingBox!.min.y
    );

    if (boundingBoxHeight === 0) {
        console.warn('Bounding box height is zero. Returning default scale of 1.');
        return 1; // Возвращаем 1 при делении на ноль
    }

    return (lamelle_height.value * k) / boundingBoxHeight;
};
const calculateFasteningScale = (model_fastening: Mesh, model_lamelle: Mesh, c = 'z'): number => {
    function getWidth(model: Mesh, c) {
        if (!model.geometry.boundingBox) {
            model.geometry.computeBoundingBox(); // Вычисляем boundingBox, если он не был вычислен ранее
        }

        return Math.abs(
            model.geometry.boundingBox!.max[c] - model.geometry.boundingBox!.min[c]
        );
    }

    return getWidth(model_lamelle, c) / (getWidth(model_fastening, c))
};

const pillar = ref<Mesh[]>([])
const setPillar = () => {
    const top = props.models.pillar_top.children[0];
    top.position.setComponent(1, lamelle_height.value * lamelles_count.value * scale_koef);

    const pillar_outer = props.models.pillar_center.children[0];
    pillar_outer.scale.setComponent(1, lamelles_count.value * calculateAddScale(pillar_outer));

    const pillar_inner = props.models.pillar_inner.children[0];
    pillar_inner.scale.setComponent(1, lamelles_count.value * calculateAddScale(pillar_inner));

    const bottom = props.models.pillar_bottom.children[0];

    let arr = [top, pillar_outer, pillar_inner, bottom]
    arr.map(el => {
        set_material({ children: [el] }, getColorHexFromRal(pillar_color.value), undefined, true)
    })
    set_material(
        { children: [arr[2]] },
        getColorHexFromRal(pillar_color.value),
        { pattern: pillar_pattern.value, count: lamelles_count.value * calculateAddScale(pillar_inner) },
        true
    )
    pillar.value = arr.map(el => el.clone())
}
setPillar()
watch([pillar_pattern, pillar_color, pillar_topper, fence_section, lamelles_count, lamelle_height], setPillar)

const fastening = ref<Object3D[]>([])
const setFastening = () => {
    const top_one = props.models.fixing.clone().children[0];
    top_one.position.set(
        pillar_one_pos.value + pillar_size * 0.66,
        lamelles_count.value * lamelle_height.value - 0.015 * scale_koef,
        0.025 * scale_koef
    )

    const top_two = props.models.fixing.clone().children[0];
    top_two.position.set(
        pillar_two_pos.value - pillar_size * 0.66,
        lamelles_count.value * lamelle_height.value - 0.01 * scale_koef,
        0.025 * scale_koef
    )

    const v = ((extra.value as number) || fence_section.value) * 10
    const top = props.models.fastening_top.clone().children[0];
    top.position.set(
        pillar_size * 0.5,
        lamelles_count.value * lamelle_height.value - 0.0275 * scale_koef,
        0
    );
    top.scale.setComponent(0, v);
    top.scale.setComponent(2, calculateFasteningScale(top, props.models.lamelle.children[0]));

    let c = 0.002 * scale_koef
    const side_one = props.models.fastening_side.clone().children[0];
    side_one.name = 'side_one'
    side_one.position.set(pillar_one_pos.value, 0, c);
    side_one.scale.set(1, lamelles_count.value * calculateAddScale(side_one), 1)
    side_one.scale.setComponent(2, calculateFasteningScale(side_one, props.models.lamelle.children[0]));

    const side_two = props.models.fastening_side.clone().children[0];
    side_two.name = 'side_two'
    side_two.scale.set(-1, lamelles_count.value * calculateAddScale(side_two), -1)
    side_two.position.set(pillar_two_pos.value, 0, c);
    side_two.scale.setComponent(2, calculateFasteningScale(side_two, props.models.lamelle.children[0]));

    let arr = [top_one, top_two, top, side_one, side_two];
    [top, side_one, side_two, ...braces.value].map(el => {
        set_material({ children: [el] }, getColorHexFromRal(pillar_color.value))
    })
    fastening.value = arr.map(el => el.clone())
}
setFastening()
watch([pillar_color, pillar_one_pos, pillar_two_pos, lamelles_count, lamelle_height], setFastening)

const setLamellesColor = () => {
    if (instanced_lamelle.value) {
        set_material({ children: [instanced_lamelle.value] }, getColorHexFromRal(lamelle_color.value))
    }
}
setLamellesColor()
watch([instanced_lamelle, lamelle_color], setLamellesColor)

watch([
    instanced_lamelle,
    lamelles_count,
    lamelle_height,
    fence_section,
], () => {
    const translationVector = new Vector3(0, 20, 20)
    for (let i = 0; i < instanced_lamelle_count; i++) {
        if (instanced_lamelle.value) {
            instanced_lamelle.value.setMatrixAt(i, lamelleMatrix(i));
            if (i >= lamelles_count.value) {
                instanced_lamelle.value.setMatrixAt(i, new Matrix4().makeTranslation(translationVector));
            }
            instanced_lamelle.value.instanceMatrix.needsUpdate = true
        }
        if (instanced_fixing_one.value) {
            instanced_fixing_one.value.setMatrixAt(i, fixingOneMatrix(i));
            if (i >= lamelles_count.value) {
                instanced_fixing_one.value.setMatrixAt(i, new Matrix4().makeTranslation(translationVector));
            }
            instanced_fixing_one.value.instanceMatrix.needsUpdate = true
        }
        if (instanced_fixing_two.value) {
            instanced_fixing_two.value.setMatrixAt(i, fixingTwoMatrix(i));
            if (i >= lamelles_count.value) {
                instanced_fixing_two.value.setMatrixAt(i, new Matrix4().makeTranslation(translationVector));
            }
            instanced_fixing_two.value.instanceMatrix.needsUpdate = true
        }
    }
});
</script>
<template>
    <TresGroup :scale="scale_koef" :position-x="translate_to_section" :name="`fence ${index}`" :position-y="0">
        <TresGroup name="pillar_one" v-if="!remove_pillar && show_pillar_one" :position-x="pillar_one_pos">
            <template v-for="item in pillar">
                <TresMesh v-bind="item.clone()" />
            </template>
            <template v-for="item in braces">
                <TresMesh v-bind="item.clone()" :name="`brace_one`" />
            </template>
        </TresGroup>

        <TresGroup name="pillar_two" v-if="!remove_pillar && show_pillar_two" :position-x="pillar_two_pos">
            <template v-for="item in pillar">
                <TresObject3D v-bind="item.clone()" />
            </template>
            <template v-for="item in braces">
                <TresMesh v-bind="item.clone()" :name="`brace_two`" />
            </template>
        </TresGroup>

        <TresGroup name="lamelles">
            <TresInstancedMesh ref="instanced_lamelle" :args="instanced_lamelle_el" />
            <TresInstancedMesh ref="instanced_fixing_one" :args="instanced_fixing_one_el" />
            <TresInstancedMesh ref="instanced_fixing_two" :args="instanced_fixing_two_el" />
        </TresGroup>

        <template v-for="item in fastening">
            <TresObject3D v-bind="item.clone()" />
        </template>
    </TresGroup>
</template>