//
// Created by sebastian on 10.02.26.
//

#include "UiPositioner.h"

#include "../UiComponent.h"
#include "../UiText.h"
#include "../../../Application.h"
#include "../../text/Font.h"

void UiPositioner::compute(const Dimensions &parent) {
    const float screenWidth = static_cast<float>(Application::getInstance().getWindow().GetWidth());
    const float screenHeight = static_cast<float>(Application::getInstance().getWindow().GetHeight());

    screenSpace.x = parent.x  + resolve(style.margin.left, parent.width, screenWidth);
    screenSpace.y = parent.y + resolve(style.margin.top, parent.height, screenHeight);
    screenSpace.width  = resolve(style.width, parent.width, screenWidth);

    if (auto text = dynamic_cast<UiText*>(&uiComponent)) {
        screenSpace.height = text->getFont().getLineHeight() / screenHeight;
    } else {
        screenSpace.height = resolve(style.height, parent.height, screenHeight);
    }


    // 3. Kinder rekursiv positionieren mit JustifyContent
    for (auto& child : uiComponent.children) {
        child->uiPositioner.compute(screenSpace);
    }
    float totalChildrenMainSize = 0.0f;
    // 3a. Gesamtgröße der Kinder berechnen (inkl. Top/Left Margins)
    for (auto& child : uiComponent.children) {
        float childMainSize = 0.0f;
        float marginMain = 0.0;
        if (style.flexDirection == FlexDirection::Column) {
            childMainSize = child->uiPositioner.screenSpace.height;
            marginMain = child->uiPositioner.resolve(child->uiPositioner.style.margin.top, screenSpace.height, screenHeight);
        } else {
            childMainSize = child->uiPositioner.screenSpace.width;
            marginMain = child->uiPositioner.resolve(child->uiPositioner.style.margin.left, screenSpace.width, screenWidth);
        }

        totalChildrenMainSize += childMainSize + marginMain;
    }

    // 3b JustifyContent-offset berechnen
    float remainingSpace = ((style.flexDirection == FlexDirection::Column) ? screenSpace.height : screenSpace.width) - totalChildrenMainSize;
    float gap =0.0f;

    float justifyOffset = 0.0f;
    switch (style.justifyContent) {
        case JustifyContent::Start: justifyOffset = 0.0f; break;
        case JustifyContent::Center: justifyOffset = remainingSpace / 2.0f; break;
        case JustifyContent::End: justifyOffset = remainingSpace; break;
        case JustifyContent::SpaceBetween:
            justifyOffset = 0.0;
            if (uiComponent.children.size() > 1) {
                gap = remainingSpace / (static_cast<float>(uiComponent.children.size()) - 1.0f);
            }
            break;
        default: break; // Space between later
    }


    // 3. Kinder rekursiv positionieren
    float currentMainOffset = justifyOffset;
    bool first = true;
    for (auto& child : uiComponent.children) {
        if (!first && style.justifyContent == JustifyContent::SpaceBetween) {
            currentMainOffset += gap;
        }

        if (style.flexDirection == FlexDirection::Column) {
            child->uiPositioner.screenSpace.y = screenSpace.y + currentMainOffset;
            currentMainOffset += child->uiPositioner.screenSpace.height;
            currentMainOffset += child->uiPositioner.resolve(child->uiPositioner.style.margin.top, parent.height, screenHeight);
        } else {
            child->uiPositioner.screenSpace.x += currentMainOffset;
            currentMainOffset += child->uiPositioner.screenSpace.width;
            currentMainOffset += child->uiPositioner.resolve(child->uiPositioner.style.margin.left, parent.width, screenWidth);
        }

        first = false;
    }

    // AlignItems
    for (auto& child : uiComponent.children) {
        if (style.flexDirection == FlexDirection::Column) {
            float marginLeft  = child->uiPositioner.resolve(child->uiPositioner.style.margin.left,screenSpace.width,screenWidth);
            float remainingCrossSpace = screenSpace.width - child->uiPositioner.screenSpace.width ;
            switch (style.alignItems) {
                case AlignItems::Start:
                    child->uiPositioner.screenSpace.x = screenSpace.x + marginLeft;
                    break;

                case AlignItems::Center:
                    child->uiPositioner.screenSpace.x =
                        screenSpace.x + remainingCrossSpace / 2.0f + marginLeft;
                    break;

                case AlignItems::End:
                    child->uiPositioner.screenSpace.x = screenSpace.x + remainingCrossSpace + marginLeft;
                    break;

                default:
                    break;
            }
        } else {
            float remainingCrossSpace = screenSpace.height - child->uiPositioner.screenSpace.height;
            float marginTop = child->uiPositioner.resolve(child->uiPositioner.style.margin.top,screenSpace.height,screenHeight);
            switch (style.alignItems) {
                case AlignItems::Start:
                    child->uiPositioner.screenSpace.y = screenSpace.y + marginTop;
                    break;

                case AlignItems::Center:
                    child->uiPositioner.screenSpace.y =
                        screenSpace.y + remainingCrossSpace / 2.0f + marginTop;
                    break;

                case AlignItems::End:
                    child->uiPositioner.screenSpace.y =
                        screenSpace.y + remainingCrossSpace + marginTop;
                    break;

                default:
                    break;
            }
        }
    }

}

LayoutStyle & UiPositioner::getLayout() {
    return style;
}

void UiPositioner::setLayout(const LayoutStyle &style) {
    this->style = style;
}


float UiPositioner::resolve(const SizeValue &size, float parentSize, float axisLength) {
    switch (size.unit) {
        case SizeUnit::Percent:
            return size.value * parentSize;
        case SizeUnit::Pixels:
            return size.value / axisLength; // has to be normalized!
        default:
            return 0.0f;
    }
}


