package electrosphere.renderer.ui.elements;

import org.lwjgl.util.yoga.Yoga;

import electrosphere.renderer.OpenGLState;
import electrosphere.renderer.RenderPipelineState;
import electrosphere.renderer.ui.elementtypes.DrawableElement;
import electrosphere.renderer.ui.elementtypes.Element;
import electrosphere.renderer.ui.elementtypes.ScrollableElement;
import electrosphere.renderer.ui.events.Event;
import electrosphere.renderer.ui.events.ScrollEvent;

/**
 * A scrollable container that renders to it's parent framebuffer instead of a dedicated one
 */
public class VirtualScrollable extends StandardContainerElement implements DrawableElement, ScrollableElement {

    //the current amount of scroll applied to this element
    double scroll = 0;

    //the scrollable callback
    ScrollEventCallback callback;

    //should we draw this element
    boolean visible = true;

    /**
     * Constructor
     */
    public VirtualScrollable(int width, int height){
        super();
        setWidth(width);
        setHeight(height);
        Yoga.YGNodeStyleSetOverflow(this.yogaNode, Yoga.YGOverflowScroll);
    }

    @Override
    public void draw(
        RenderPipelineState renderPipelineState,
        OpenGLState openGLState,
        int parentFramebufferPointer,
        int parentPosX,
        int parentPosY,
        int parentWidth,
        int parentHeight
        ) {
        for(Element child : childList){
            if(child instanceof DrawableElement){
                DrawableElement drawableChild = (DrawableElement) child;
                if(childIsInBounds(drawableChild)){
                    drawableChild.draw(
                        renderPipelineState,
                        openGLState,
                        parentFramebufferPointer,
                        parentPosX + internalPositionX,
                        (int)(parentPosY + internalPositionY + scroll),
                        parentWidth,
                        parentHeight
                    );
                }
            }
        }
    }

    /**
     * Checks if a given child element should be visible
     * @param element the element
     * @return true if visible, false otherwise
     */
    private boolean childIsInBounds(DrawableElement element){
        boolean rVal = true;
        if(element.getInternalY() + scroll < 0 ||element.getInternalY() + scroll > this.height){
            return false;
        }
        return rVal;
    }

    @Override
    public boolean getVisible() {
        return visible;
    }

    @Override
    public void setVisible(boolean draw) {
        this.visible = draw;
    }

    @Override
    public void setWidth(int width) {
        this.internalWidth = width;
        this.width = width;
        Yoga.YGNodeStyleSetWidth(this.yogaNode, width);
    }

    @Override
    public void setHeight(int height) {
        this.internalHeight = height;
        this.height = height;
        Yoga.YGNodeStyleSetHeight(this.yogaNode, height);
    }

    @Override
    public int getChildOffsetY(){
        return (int)scroll;
    }

    @Override
    public void setOnScrollCallback(ScrollEventCallback callback) {
        this.callback = callback;
    }

    /**
     * Default handling for the scroll event
     * @param event The scroll event
     */
    private void defaultScrollHandling(ScrollEvent event){
        scroll = scroll + event.getScrollAmount();
        if(scroll > 0){
            scroll = 0;
        }
        //calculate max scroll
        double maxScroll = 0;
        for(Element child : this.getChildren()){
            if(child.getInternalY() + child.getInternalHeight() > maxScroll){
                maxScroll = child.getInternalY() + child.getInternalHeight() - this.internalHeight;
            }
        }
        if(scroll < - maxScroll){
            scroll = -maxScroll;
        }
        for(Element childElement : this.getChildren()){
            childElement.setPositionX((int)(childElement.getInternalX() + scroll));
        }
    }

    @Override
    public boolean handleEvent(Event event){
        boolean propagate = true;
        if(event instanceof ScrollEvent){
            ScrollEvent scrollEvent = (ScrollEvent)event;
            if(callback != null){
                propagate = callback.execute(scrollEvent);
            } else {
                defaultScrollHandling(scrollEvent);
                propagate = false;
            }
        }
        return propagate;
    }
    
}
