// 1
function hcyl(bottom, height, radius, id) {
    let radsq = radius * radius
    let innerRadsq = (radius - 1.2) * (radius - 1.2)
    height += bottom
    for (let x = -radius; x <= radius; x++) {
        for (let y = bottom; y < height; y++) {
            for (let z = -radius; z <= radius; z++) {
                let d = x * x + z * z
                if (d < radsq && d >= innerRadsq) {
                    world.setBlock(p2.x + x, p2.y + y, p2.z + z, id)
                }
            }
        }
    }
}

function cyl(bottom, height, radius, id) {
    let radsq = radius * radius
    height += bottom
    for (let x = -radius; x <= radius; x++) {
        for (let y = bottom; y < height; y++) {
            for (let z = -radius; z <= radius; z++) {
                let d = x * x + z * z
                if (d < radsq) {
                    world.setBlock(p2.x + x, p2.y + y, p2.z + z, id)
                }
            }
        }
    }
}

function cube(bottom, height, radius, id) {
    let radsq = radius * radius
    height += bottom
    for (let x = -radius; x <= radius; x++) {
        for (let y = bottom; y < height; y++) {
            for (let z = -radius; z <= radius; z++) {
                let d = x + z
                if (d < radsq) {
                    world.setBlock(p2.x + x, p2.y + y, p2.z + z, id)
                }
            }
        }
    }
}

function sphereoid(w, h, d, id) {
    let w2 = w * w
    let h2 = h * h
    let d2 = d * d
    let w3 = (w - 1.5) * (w - 1.5)
    let h3 = (h - 1.5) * (h - 1.5)
    let d3 = (d - 1.5) * (d - 1.5)

    for (let y = -h; y < h; y++) {
        for (let x = -w; x <= w; x++) {
            for (let z = -d; z <= d; z++) {
                let n = x * x / w2 + y * y / h2 + z * z / d2
                let n2 = x * x / w3 + y * y / h3 + z * z / d3
                if (n < 1 && n2 >= 1) {
                    world.setBlock(p2.x + x, p2.y + y, p2.z + z, id)
                }
            }
        }
    }
}

function sleep(ms) {
    return new Promise(resolve => setTimeout(() => resolve(), ms))
}

async function asphereoid(w, h, d, id) {
    let px = p2.x
    let py = p2.y
    let pz = p2.z
    let w2 = w * w
    let h2 = h * h
    let d2 = d * d
    let w3 = (w - 1.5) * (w - 1.5)
    let h3 = (h - 1.5) * (h - 1.5)
    let d3 = (d - 1.5) * (d - 1.5)

    for (let y = -h; y < h; y++) {
        for (let x = -w; x <= w; x++) {
            for (let z = -d; z <= d; z++) {
                let n = x * x / w2 + y * y / h2 + z * z / d2
                let n2 = x * x / w3 + y * y / h3 + z * z / d3
                if (n < 1 && n2 >= 1) {
                    world.setBlock(px + x, py + y, pz + z, id)
                    await sleep(10)
                }
            }
        }
    }
}

function cloneBlock(sx, sy, sz, dx, dy, dz, w, h, l) {
    for (let x = 0; x < w; x++) {
        for (let y = 0; y < h; y++) {
            for (let z = 0; z < l; z++) {
                let block = world.getBlock(sx + x, sy + y, sz + z);
                world.setBlock(dx + x, dy + y, dz + z, block);
            }
        }
    }
}

function duplicateBlock(sx, sy, sz, dx, dy, dz, w, h, l, offsetX, offsetY, offsetZ, num) {
    for (let i = 0; i < num; i++) {
        cloneBlock(sx, sy, sz, dx + offsetX * i, dy + offsetY * i, dz + offsetZ * i, w, h, l);
    }
}

function rotateBlock(sx, sy, sz, dx, dy, dz, w, h, l, angle) {
    let rad = angle * (Math.PI / 180);
    let sin = Math.sin(rad);
    let cos = Math.cos(rad);

    for (let x = 0; x < w; x++) {
        for (let y = 0; y < h; y++) {
            for (let z = 0; z < l; z++) {
                let nx = Math.round(x * cos - z * sin);
                let nz = Math.round(x * sin + z * cos);
                let block = world.getBlock(sx + x, sy + y, sz + z);
                world.setBlock(dx + nx, dy + y, dz + nz, block);
            }
        }
    }
}

function fillBlock(sx, sy, sz, dx, dy, dz, id) {
    let w = Math.abs(dx - sx) + 1;
    let h = Math.abs(dy - sy) + 1;
    let l = Math.abs(dz - sz) + 1;

    let startX = Math.min(sx, dx);
    let startY = Math.min(sy, dy);
    let startZ = Math.min(sz, dz);

    for (let x = 0; x < w; x++) {
        for (let y = 0; y < h; y++) {
            for (let z = 0; z < l; z++) {
                world.setBlock(startX + x, startY + y, startZ + z, id);
            }
        }
    }
}

function moveBlock(sx, sy, sz, dx, dy, dz, w, h, l) {
    cloneBlock(sx, sy, sz, dx, dy, dz, w, h, l);
    fillBlock(sx, sy, sz, sx + w - 1, sy + h - 1, sz + l - 1, 0);
}

function paintBlock(sx, sy, sz, dx, dy, dz, w, h, l, colorId) {
    cloneBlock(sx, sy, sz, dx, dy, dz, w, h, l);

    for (let x = 0; x < w; x++) {
        for (let y = 0; y < h; y++) {
            for (let z = 0; z < l; z++) {
                world.setBlock(dx + x, dy + y, dz + z, colorId);
            }
        }
    }
}

function replaceBlock(sx, sy, sz, dx, dy, dz, id, newId) {
    let w = Math.abs(dx - sx) + 1;
    let h = Math.abs(dy - sy) + 1;
    let l = Math.abs(dz - sz) + 1;

    let startX = Math.min(sx, dx);
    let startY = Math.min(sy, dy);
    let startZ = Math.min(sz, dz);

    for (let x = 0; x < w; x++) {
        for (let y = 0; y < h; y++) {
            for (let z = 0; z < l; z++) {
                if (world.getBlock(startX + x, startY + y, startZ + z) === id) {
                    world.setBlock(startX + x, startY + y, startZ + z, newId);
                }
            }
        }
    }
}

function mirrorBlock(sx, sy, sz, dx, dy, dz, w, h, l, axis) {
    if (axis === "x") {
        for (let x = 0; x < w; x++) {
            for (let y = 0; y < h; y++) {
                for (let z = 0; z < l; z++) {
                    let block = world.getBlock(sx + x, sy + y, sz + z);
                    world.setBlock(dx + w - x - 1, dy + y, dz + z, block);
                }
            }
        }
    } else if (axis === "y") {
        for (let x = 0; x < w; x++) {
            for (let y = 0; y < h; y++) {
                for (let z = 0; z < l; z++) {
                    let block = world.getBlock(sx + x, sy + y, sz + z);
                    world.setBlock(dx + x, dy + h - y - 1, dz + z, block);
                }
            }
        }
    } else if (axis === "z") {
        for (let x = 0; x < w; x++) {
            for (let y = 0; y < h; y++) {
                for (let z = 0; z < l; z++) {
                    let block = world.getBlock(sx + x, sy + y, sz + z);
                    world.setBlock(dx + x, dy + y, dz + l - z - 1, block);
                }
            }
        }
    }
}

function clearBlock(sx, sy, sz, dx, dy, dz) {
    let w = Math.abs(dx - sx) + 1;
    let h = Math.abs(dy - sy) + 1;
    let l = Math.abs(dz - sz) + 1;

    let startX = Math.min(sx, dx);
    let startY = Math.min(sy, dy);
    let startZ = Math.min(sz, dz);

    for (let x = 0; x < w; x++) {
        for (let y = 0; y < h; y++) {
            for (let z = 0; z < l; z++) {
                world.setBlock(startX + x, startY + y, startZ + z, 0);
            }
        }
    }
}

// bad circle
function circleBlock(x, y, z, height, radius, thickness, startDeg, endDeg, id) {
    for (let deg = startDeg; deg <= endDeg; deg++) {
        let rad = deg * (Math.PI / 180);
        let sin = Math.sin(rad);
        let cos = Math.cos(rad);
        for (let h = 0; h < height; h++) {
            for (let r = radius - thickness; r <= radius; r++) {
                let dx = Math.round(r * cos);
                let dz = Math.round(r * sin);
                world.setBlock(x + dx, y + h, z + dz, id);
            }
        }
    }
}


function detectSize(sx, sy, sz, dx, dy, dz) {
    let w = Math.abs(dx - sx) + 1;
    let h = Math.abs(dy - sy) + 1;
    let l = Math.abs(dz - sz) + 1;

    let startX = Math.min(sx, dx);
    let startY = Math.min(sy, dy);
    let startZ = Math.min(sz, dz);

    return {
        width: w,
        height: h,
        length: l
    };
}

function hollowCube(sx, sy, sz, dx, dy, dz, id) {
    let w = Math.abs(dx - sx) + 1;
    let h = Math.abs(dy - sy) + 1;
    let l = Math.abs(dz - sz) + 1;

    let startX = Math.min(sx, dx);
    let startY = Math.min(sy, dy);
    let startZ = Math.min(sz, dz);

    for (let x = 0; x < w; x++) {
        for (let y = 0; y < h; y++) {
            for (let z = 0; z < l; z++) {
                if (x === 0 || x === w - 1 || y === 0 || y === h - 1 || z === 0 || z === l - 1) {
                    world.setBlock(startX + x, startY + y, startZ + z, id);
                }
            }
        }
    }
}

function fillBlockWithRotation(sx, sy, sz, w, h, l, id, angle) {
    let rad = angle * (Math.PI / 180);
    let sin = Math.sin(rad);
    let cos = Math.cos(rad);

    for (let x = 0; x < w; x++) {
        for (let y = 0; y < h; y++) {
            for (let z = 0; z < l; z++) {
                // Calculate the rotated positions
                let nx = Math.round(x * cos - z * sin);
                let nz = Math.round(x * sin + z * cos);

                // Place the block at the new rotated position
                world.setBlock(sx + nx, sy + y, sz + nz, id);
            }
        }
    }
}

function lineBlock(sx, sy, sz, ex, ey, ez, id) {
    // Calculate the differences in each direction
    let dx = ex - sx;
    let dy = ey - sy;
    let dz = ez - sz;

    // Calculate the total steps needed based on the largest difference
    let steps = Math.max(Math.abs(dx), Math.abs(dy), Math.abs(dz));

    // Calculate the increment for each axis
    let stepX = dx / steps;
    let stepY = dy / steps;
    let stepZ = dz / steps;

    // Place blocks along the line
    for (let i = 0; i <= steps; i++) {
        // Calculate the current position based on the step
        let x = Math.round(sx + stepX * i);
        let y = Math.round(sy + stepY * i);
        let z = Math.round(sz + stepZ * i);

        // Set the block at the calculated position
        world.setBlock(x, y, z, id);
    }
}

function outlineBlock(sx, sy, sz, dx, dy, dz, id) {
    let w = Math.abs(dx - sx) + 1;
    let h = Math.abs(dy - sy) + 1;
    let l = Math.abs(dz - sz) + 1;

    let startX = Math.min(sx, dx);
    let startY = Math.min(sy, dy);
    let startZ = Math.min(sz, dz);

    // Create the outline by setting blocks at the edges of the rectangle
    for (let x = 0; x < w; x++) {
        for (let y = 0; y < h; y++) {
            // Set corners and edges
            if (x === 0 || x === w - 1 || y === 0 || y === h - 1) {
                world.setBlock(startX + x, startY + y, startZ, id); // Bottom face
                world.setBlock(startX + x, startY + y, startZ + l - 1, id); // Top face
            }
            // Vertical edges
            world.setBlock(startX + x, startY, startZ + l - 1, id); // Bottom face
            world.setBlock(startX + x, startY + h - 1, startZ + l - 1, id); // Top face
        }
    }

    for (let y = 0; y < h; y++) {
        for (let z = 0; z < l; z++) {
            // Set corners in the vertical columns
            world.setBlock(startX, startY + y, startZ + z, id); // Front face
            world.setBlock(startX + w - 1, startY + y, startZ + z, id); // Back face
        }
    }
}

function generateMountain(sx, sy, sz, width, height, depth, peakHeight, id) {
    const centerX = width / 2;
    const centerZ = depth / 2;

    for (let x = 0; x < width; x++) {
        for (let z = 0; z < depth; z++) {
            // Calculate the distance from the center to determine height
            let distance = Math.sqrt((x - centerX) ** 2 + (z - centerZ) ** 2);
            let maxHeight = peakHeight - Math.floor((distance / Math.sqrt((centerX) ** 2 + (centerZ) ** 2)) * peakHeight);

            // Create the mountain structure
            for (let y = 0; y < height; y++) {
                if (y < maxHeight) {
                    world.setBlock(sx + x, sy + y, sz + z, id); // Set block for mountain
                }
            }
        }
    }
}

let clipboard = [];

function copyBlockToClipboard(sx, sy, sz, dx, dy, dz) {
    let w = Math.abs(dx - sx) + 1;
    let h = Math.abs(dy - sy) + 1;
    let l = Math.abs(dz - sz) + 1;

    clipboard = [];  // Clear previous clipboard content

    for (let x = 0; x < w; x++) {
        for (let y = 0; y < h; y++) {
            for (let z = 0; z < l; z++) {
                let block = world.getBlock(sx + x, sy + y, sz + z);
                clipboard.push({x: x, y: y, z: z, id: block});
            }
        }
    }
}

function pasteBlockFromClipboard(dx, dy, dz) {
    clipboard.forEach(block => {
        world.setBlock(dx + block.x, dy + block.y, dz + block.z, block.id);
    });
}

function blockToDeg(sx, sy, sz, dx, dy, dz) {
    let dx_ = dx - sx;
    let dz_ = dz - sz;
    let rad = Math.atan2(dz_, dx_); // angle in radians relative to X-axis
    let deg = rad * (180 / Math.PI);
    if (deg < 0) deg += 360;
    return deg;
}

function degToBlock(x, y, z, deg, length) {