use super::*;

pub(super) fn material_shader_ui(ui: &mut egui::Ui, material: &mut MaterialDesc) -> bool {
    let mut changed = false;
    property_row(ui, "Shader", |ui| {
        egui::ComboBox::from_id_salt("material_shader_kind")
            .selected_text(match material.shader.kind {
                MaterialShaderKind::StandardLit => "Standard Lit",
                MaterialShaderKind::Unlit => "Unlit",
                MaterialShaderKind::Custom => "Custom",
            })
            .show_ui(ui, |ui| {
                changed |= ui
                    .selectable_value(
                        &mut material.shader.kind,
                        MaterialShaderKind::StandardLit,
                        "Standard Lit",
                    )
                    .changed();
                changed |= ui
                    .selectable_value(
                        &mut material.shader.kind,
                        MaterialShaderKind::Unlit,
                        "Unlit",
                    )
                    .changed();
                changed |= ui
                    .selectable_value(
                        &mut material.shader.kind,
                        MaterialShaderKind::Custom,
                        "Custom",
                    )
                    .changed();
            });
    });
    if matches!(material.shader.kind, MaterialShaderKind::Custom) {
        changed |= option_string_ui(ui, "Shader schema", &mut material.shader.schema_path);
        changed |= option_string_ui(ui, "WGSL shader", &mut material.shader.shader_path);
    }
    if !material.parameters.is_empty() {
        ui.label(panel_heading("Shader Parameters"));
        for parameter in &mut material.parameters {
            changed |= material_parameter_ui(ui, parameter);
        }
    }
    changed
}

pub(super) fn material_parameter_ui(ui: &mut egui::Ui, parameter: &mut MaterialParameter) -> bool {
    ui.horizontal_wrapped(|ui| {
        ui.label(&parameter.name);
        match &mut parameter.value {
            MaterialParameterValue::Bool(value) => ui.checkbox(value, "").changed(),
            MaterialParameterValue::Float(value) => ui
                .add_sized(
                    [fit_width(ui, 64.0, 120.0), 20.0],
                    egui::DragValue::new(value)
                        .speed(0.01)
                        .min_decimals(2)
                        .max_decimals(4),
                )
                .changed(),
            MaterialParameterValue::Vec2(value) => {
                let mut changed = false;
                changed |= ui
                    .add_sized(
                        [fit_width(ui, 52.0, 88.0), 20.0],
                        egui::DragValue::new(&mut value.x).speed(0.01),
                    )
                    .changed();
                changed |= ui
                    .add_sized(
                        [fit_width(ui, 52.0, 88.0), 20.0],
                        egui::DragValue::new(&mut value.y).speed(0.01),
                    )
                    .changed();
                changed
            }
            MaterialParameterValue::Vec3(value) => {
                let mut changed = false;
                changed |= ui
                    .add_sized(
                        [fit_width(ui, 52.0, 88.0), 20.0],
                        egui::DragValue::new(&mut value.x).speed(0.01),
                    )
                    .changed();
                changed |= ui
                    .add_sized(
                        [fit_width(ui, 52.0, 88.0), 20.0],
                        egui::DragValue::new(&mut value.y).speed(0.01),
                    )
                    .changed();
                changed |= ui
                    .add_sized(
                        [fit_width(ui, 52.0, 88.0), 20.0],
                        egui::DragValue::new(&mut value.z).speed(0.01),
                    )
                    .changed();
                changed
            }
            MaterialParameterValue::Color(value) => {
                let mut rgba = [value.r, value.g, value.b, value.a];
                let changed = ui.color_edit_button_rgba_unmultiplied(&mut rgba).changed();
                if changed {
                    *value = ColorDesc {
                        r: rgba[0],
                        g: rgba[1],
                        b: rgba[2],
                        a: rgba[3],
                    };
                }
                changed
            }
            MaterialParameterValue::Enum(value) => ui
                .add_sized(
                    [
                        fit_width(ui, MIN_INLINE_CONTROL_WIDTH, TEXT_FIELD_MAX_WIDTH),
                        20.0,
                    ],
                    egui::TextEdit::singleline(value),
                )
                .changed(),
        }
    })
    .inner
}

pub(crate) fn component_card(
    ui: &mut egui::Ui,
    context: &ComponentCardContext,
    add_contents: impl FnOnce(&mut egui::Ui),
) -> ComponentCardResponse {
    let options = editor_ui::inspector::ComponentCardOptions {
        type_name: context.options.type_name,
        title: context.options.title,
        icon: context.options.icon,
        active_toggle: context.options.active_toggle,
        removable: context.options.removable,
        reorderable: context.options.reorderable,
        resettable: context.options.resettable,
        copyable: context.options.copyable,
        summary: context.options.summary,
        body_margin: context.options.body_margin,
    };
    let shared = editor_ui::inspector::component_card(
        ui,
        &editor_ui::inspector::ComponentCardViewModel {
            options,
            active: context.active,
            collapsed: context.collapsed,
            pasteable: context.pasteable,
            can_move_up: context.can_move_up,
            can_move_down: context.can_move_down,
        },
        add_contents,
    );
    ComponentCardResponse {
        type_name: shared.type_name,
        collapsed: shared.collapsed,
        active: shared.active,
        reset: shared.reset,
        copy: shared.copy,
        paste: shared.paste,
        move_up: shared.move_up,
        move_down: shared.move_down,
        remove: shared.remove,
    }
}

pub(crate) fn property_row<R>(
    ui: &mut egui::Ui,
    label: &str,
    add_contents: impl FnOnce(&mut egui::Ui) -> R,
) -> R {
    if ui.available_width() < COMPACT_INSPECTOR_WIDTH {
        ui.vertical(|ui| {
            ui.label(
                crate::ui::design_system::typography::TypeRole::Body
                    .text(label)
                    .color(crate::ui::theme::TEXT_DIM),
            );
            add_contents(ui)
        })
        .inner
    } else {
        let (rect, _) = ui.allocate_exact_size(
            egui::vec2(ui.available_width().max(1.0), 30.0),
            egui::Sense::hover(),
        );
        ui.scope_builder(
            egui::UiBuilder::new()
                .max_rect(rect)
                .layout(egui::Layout::left_to_right(egui::Align::Center)),
            |ui| {
                ui.set_clip_rect(rect.intersect(ui.clip_rect()));
                let label_width = PROPERTY_LABEL_WIDTH.min(ui.available_width().max(1.0));
                ui.add_sized(
                    [label_width, 22.0],
                    egui::Label::new(
                        crate::ui::design_system::typography::TypeRole::Body
                            .text(label)
                            .color(crate::ui::theme::TEXT_DIM),
                    ),
                );
                add_contents(ui)
            },
        )
        .inner
    }
}

pub(crate) fn text_field_width(ui: &egui::Ui) -> f32 {
    fit_width(ui, MIN_INLINE_CONTROL_WIDTH, TEXT_FIELD_MAX_WIDTH).min(ui.available_width().max(1.0))
}

pub fn material_editor_ui(world: &mut World, ui: &mut egui::Ui, entity: Entity) {
    let mut material = world
        .get::<MaterialDesc>(entity)
        .cloned()
        .unwrap_or_default();
    request_texture_asset_thumbnails(world);
    let texture_candidates = texture_asset_candidates(world);
    let original = material.clone();
    let mut changed = false;
    let mut options = ComponentCardOptions::removable(
        COMPONENT_MATERIAL_DESC,
        "Brush Fallback Material",
        icons::PALETTE,
    );
    options.removable = world.get::<MaterialDesc>(entity).is_some();
    options.copyable = options.removable;
    let card = component_card_context(world, entity, options);
    let card_response = component_card(ui, &card, |ui| {
        changed |= material_shader_ui(ui, &mut material);
        let mut color = [
            material.base_color.r,
            material.base_color.g,
            material.base_color.b,
            material.base_color.a,
        ];
        property_row(ui, "Base color", |ui| {
            if ui.color_edit_button_rgba_unmultiplied(&mut color).changed() {
                material.base_color = ColorDesc {
                    r: color[0],
                    g: color[1],
                    b: color[2],
                    a: color[3],
                };
                changed = true;
            }
        });
        property_row(ui, "Metallic", |ui| {
            changed |= ui
                .add(egui::Slider::new(&mut material.metallic, 0.0..=1.0))
                .changed();
        });
        property_row(ui, "Roughness", |ui| {
            changed |= ui
                .add(egui::Slider::new(&mut material.roughness, 0.0..=1.0))
                .changed();
        });

        let mut emissive_color = [
            material.emissive_color.r,
            material.emissive_color.g,
            material.emissive_color.b,
            material.emissive_color.a,
        ];
        property_row(ui, "Emissive", |ui| {
            if ui
                .color_edit_button_rgba_unmultiplied(&mut emissive_color)
                .changed()
            {
                material.emissive_color = ColorDesc {
                    r: emissive_color[0],
                    g: emissive_color[1],
                    b: emissive_color[2],
                    a: emissive_color[3],
                };
                changed = true;
            }
        });
        property_row(ui, "Emissive nits", |ui| {
            changed |= ui
                .add(egui::Slider::new(
                    &mut material.emissive_intensity,
                    0.0..=20_000.0,
                ))
                .changed();
        });

        changed |= texture_asset_picker_ui(
            world,
            ui,
            "Base color texture",
            &mut material.base_color_texture,
            &texture_candidates,
        );
        changed |= texture_asset_picker_ui(
            world,
            ui,
            "Emissive texture",
            &mut material.emissive_texture,
            &texture_candidates,
        );
        changed |= texture_asset_picker_ui(
            world,
            ui,
            "Normal map",
            &mut material.normal_map_texture,
            &texture_candidates,
        );
        changed |= texture_asset_picker_ui(
            world,
            ui,
            "Metallic/roughness texture",
            &mut material.metallic_roughness_texture,
            &texture_candidates,
        );

        if crate::assets::materials::material_asset_picker_ui(
            world,
            ui,
            entity,
            &mut material,
            &original,
        ) {
            changed = true;
        }
    });
    apply_component_card_response(world, entity, card_response);

    if changed && !material_eq(&original, &material) {
        set_material_with_history(world, entity, material);
    }
}
