#include <games/io.h>
#include "camera_control.h"

#include <strmif.h>

#include "games/iidx/camera.h"
#include "games/iidx/local_camera.h"
#include "misc/eamuse.h"
#include "util/utils.h"
#include "util/fileutils.h"
#include "util/logging.h"
#include "overlay/imgui/extensions.h"

using namespace games::iidx;

namespace overlay::windows {

    CameraControl::CameraControl(SpiceOverlay *overlay) : Window(overlay) {
        this->title = "Camera Control";
        this->flags |= ImGuiWindowFlags_AlwaysAutoResize;
        this->init_pos = ImVec2(0, 0);
        this->toggle_button = games::OverlayButtons::ToggleCameraControl;
    }

    CameraControl::~CameraControl() {
    }

    void CameraControl::build_content() {
        if (!CAMERA_READY) {
            ImGui::TextColored(ImVec4(1.f, 1.f, 0.f, 1.f), "%s", "Camera not ready");
            return;
        }

        // camera combo box
        auto numCameras = LOCAL_CAMERA_LIST.size();

        if (numCameras == 0) {
            return;
        }

        IIDXLocalCamera *selectedCamera = LOCAL_CAMERA_LIST.at(m_selectedCameraIndex);
        auto selectedCameraChanged = ImGui::BeginCombo(
            "Source", selectedCamera->GetName().c_str()
        );

        if (selectedCameraChanged) {
            for (size_t i = 0; i < numCameras; i++) {
                IIDXLocalCamera *cameraItem = LOCAL_CAMERA_LIST.at(i);
                const bool is_selected = (m_selectedCameraIndex == (int) i);
                if (ImGui::Selectable(cameraItem->GetName().c_str(), is_selected)) {
                    m_selectedCameraIndex = i;
                }

                if (is_selected) {
                    ImGui::SetItemDefaultFocus();
                }
            }
            ImGui::EndCombo();
        }

        selectedCamera = LOCAL_CAMERA_LIST.at(m_selectedCameraIndex);

        // Render parameters
        ImGui::Separator();
        ImGui::Text("Rendering");

        // Media Type Selector
        int selectedMediaTypeIndex = selectedCamera->m_selectedMediaTypeIndex;
        auto numMediaTypes = selectedCamera->m_mediaTypeInfos.size();

        auto selectedMediaTypeIndexChanged = ImGui::BeginCombo(
            "Media Type", selectedCamera->m_mediaTypeInfos.at(selectedMediaTypeIndex).description.c_str()
        );

        if (selectedMediaTypeIndexChanged) {
            for (size_t i = 0; i < numMediaTypes; i++) {
                const bool is_selected = (selectedMediaTypeIndex == (int) i);
                if (ImGui::Selectable(selectedCamera->m_mediaTypeInfos.at(i).description.c_str(), is_selected)) {
                    selectedMediaTypeIndex = i;
                }

                if (is_selected) {
                    ImGui::SetItemDefaultFocus();
                }
            }
            ImGui::EndCombo();
        }

        if (selectedMediaTypeIndexChanged && selectedMediaTypeIndex != selectedCamera->m_selectedMediaTypeIndex) {
            selectedCamera->m_useAutoMediaType = false;
            selectedCamera->ChangeMediaType(selectedCamera->m_mediaTypeInfos.at(selectedMediaTypeIndex).p_mediaType);
        }

        // Auto media type
        bool isAutoMediaType = selectedCamera->m_useAutoMediaType;
        ImGui::SameLine(300);
        if (ImGui::Checkbox("Auto##MediaType", &isAutoMediaType)) {
            selectedCamera->m_useAutoMediaType = isAutoMediaType;
            if (isAutoMediaType) {
                selectedCamera->ChangeMediaType(selectedCamera->m_pAutoMediaType);
            }
        }

        // Draw mode
        int selectedDrawModeIndex = selectedCamera->m_drawMode;
        if (ImGui::BeginCombo("Draw Mode", DRAW_MODE_LABELS[selectedCamera->m_drawMode].c_str())) {
            for (size_t i = 0; i < DRAW_MODE_SIZE; i++) {
                const bool is_selected = (selectedDrawModeIndex == (int) i);
                if (ImGui::Selectable(DRAW_MODE_LABELS[i].c_str(), is_selected)) {
                    selectedDrawModeIndex = i;
                }

                if (is_selected) {
                    ImGui::SetItemDefaultFocus();
                }
            }
            ImGui::EndCombo();
        }
        ImGui::SameLine();
        ImGui::HelpMarker(
            "Stretch: direct copy from source to destination\n\n"
            "Crop: Keep aspect ratio (16:9) and cut off horizontally or vertically\n\n"
            "Letterbox: Keep aspect ratio (16:9) and add black space horizontally or vertically\n\n"
            "Crop to 4:3: Crop to display as 4:3\n\n"
            "Letterbox to 4:3: Like Letterbox, but target 4:3"
            );

        if (selectedDrawModeIndex != selectedCamera->m_drawMode) {
            selectedCamera->m_drawMode = (LocalCameraDrawMode)selectedDrawModeIndex;
            selectedCamera->UpdateDrawRect();
        }

        // Camera control parameters
        ImGui::Separator();
        ImGui::Text("Camera control");

        // some high end webcams store settings on its onboard memory, with the user configuring it
        // via proprietary software outside of the game, so don't mess with it unless the user
        // explicitly wants to change things here
        ImGui::Checkbox("Allow manual control", &selectedCamera->m_allowManualControl);

        ImGui::BeginDisabled(!selectedCamera->m_allowManualControl);
        IAMCameraControl *pCameraControl = selectedCamera->GetCameraControl();
        if (pCameraControl) {
            for (size_t i = 0; i < CAMERA_CONTROL_PROP_SIZE; i++) {
                CameraControlProp prop = {};
                selectedCamera->GetCameraControlProp(i, &prop);

                auto value = prop.value;

                bool isDisabled = (prop.defFlags == 0 || prop.valueFlags & CameraControl_Flags_Auto);
                ImGui::BeginDisabled(isDisabled);

                int sliderFlag = ImGuiSliderFlags_AlwaysClamp | ImGuiSliderFlags_NoRoundToFormat;

                bool isDefAuto = prop.defFlags & CameraControl_Flags_Auto;

                if (ImGui::SliderInt(CAMERA_CONTROL_LABELS[i].c_str(), (int*) &value, prop.minValue, prop.maxValue, "%d", sliderFlag)) {
                    selectedCamera->SetCameraControlProp(i, value, prop.valueFlags);
                }

                ImGui::EndDisabled();

                if (isDefAuto) {
                    ImGui::SameLine(300);
                    if (ImGui::CheckboxFlags(("Auto##" + CAMERA_CONTROL_LABELS[i]).c_str(), (int*) &prop.valueFlags, CameraControl_Flags_Auto)) {
                        selectedCamera->SetCameraControlProp(i, value, prop.valueFlags);
                    }
                }
            }
        }

        ImGui::Separator();
        
        // reset button
        if (ImGui::Button("Reset")) {
            selectedCamera->ResetCameraControlProps();
        }

        ImGui::EndDisabled();

        // save button
        ImGui::SameLine();
        if (ImGui::Button("Save")) {
            this->config_save();
        }
    }

    void CameraControl::config_save() {
        camera_config_save();
    }
}
