import {
    Mesh,
    Nullable,
    Scene,
    StandardMaterial,
    Texture,
} from '@babylonjs/core/Legacy/legacy';

import { adjust, box, subtractMany } from './shapes.js';

export interface SurfaceOptions {
    height?: number,
    thickness?: number,
    texturePath?: string,
};

export function attachTexture(b: Mesh, scene: Scene, texturePath: string | undefined): Mesh {
    if (texturePath !== void 0) {
        const mat = new StandardMaterial(b.name + "_mat", scene);
        mat.diffuseTexture = new Texture(texturePath, scene);
        b.material = mat;
    }
    return b;
}

export const DEFAULT_THICKNESS = 0.125;

export async function wall(name: string, length: number, scene: Scene, options: SurfaceOptions = {}): Promise<Mesh> {
    return attachTexture(box(name, length, options.height ?? 2.90, options.thickness ?? DEFAULT_THICKNESS),
                         scene,
                         options.texturePath);
}

export async function floor(name: string, width: number, depth: number, scene: Scene, options: SurfaceOptions = {}): Promise<Mesh> {
    return attachTexture(box(name, width, options.thickness ?? DEFAULT_THICKNESS, depth),
                         scene,
                         options.texturePath);
}

export type Wall = {
    texture: string,
    thickness?: number,
    holes: Array<Mesh>,
};

export async function room(name: string, width: number, depth: number, scene: Scene,
                    floorTexture: string,
                    ceilingTexture: string,
                    wall0: Nullable<Wall>,
                    wall1: Nullable<Wall>,
                    wall2: Nullable<Wall>,
                    wall3: Nullable<Wall>): Promise<Mesh> {
    const th = (wallDetails: Wall): number => wallDetails.thickness ?? DEFAULT_THICKNESS;
    const mkw = async (n: number, width: number, wallDetails: Wall): Promise<Mesh> => {
        return adjust(subtractMany,
                      await wall(`${name}_${n}`, width, scene, {
                          texturePath: wallDetails.texture,
                          thickness: wallDetails.thickness,
                      }),
                      ... wallDetails.holes);
    };

    const f = await floor(name +"_f", width, depth, scene, { texturePath: floorTexture });
    f.position.y = -DEFAULT_THICKNESS / 2;

    const c = await floor(name + "_c", width, depth, scene, { texturePath: ceilingTexture });
    c.position.y = 2.90 + DEFAULT_THICKNESS / 2;
    c.setParent(f);

    if (wall0 !== null) {
        const w0 = await mkw(0, width, wall0);
        w0.position.z = f.scaling.z / 2 + th(wall0) / 2;
        w0.setParent(f);
    }

    if (wall1 !== null) {
        const w1 = await mkw(1, depth, wall1);
        w1.position.x = f.scaling.x / 2 + th(wall1) / 2;
        w1.rotation.y = Math.PI / 2;
        w1.setParent(f);
    }

    if (wall2 !== null) {
        const w2 = await mkw(2, width, wall2);
        w2.position.z = -(f.scaling.z / 2 + th(wall2) / 2);
        w2.rotation.y = Math.PI;
        w2.setParent(f);
    }

    if (wall3 !== null) {
        const w3 = await mkw(3, depth, wall3);
        w3.position.x = -(f.scaling.x / 2 + th(wall3) / 2);
        w3.rotation.y = -Math.PI / 2;
        w3.setParent(f);
    }

    f.position.y = DEFAULT_THICKNESS / 2;

    return f;
}
