package electrosphere.renderer.ui.elements;

import org.joml.Vector3f;

import electrosphere.engine.Globals;
import electrosphere.logger.LoggerInterface;
import electrosphere.renderer.OpenGLState;
import electrosphere.renderer.RenderPipelineState;
import electrosphere.renderer.framebuffer.Framebuffer;
import electrosphere.renderer.framebuffer.FramebufferUtils;
import electrosphere.renderer.model.Material;
import electrosphere.renderer.model.Model;
import electrosphere.renderer.texture.Texture;
import electrosphere.renderer.ui.elementtypes.ContainerElement;
import electrosphere.renderer.ui.elementtypes.DrawableElement;
import electrosphere.renderer.ui.elementtypes.Element;
import electrosphere.renderer.ui.events.Event;

import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL30.*;

public class ScrollableContainer extends StandardContainerElement implements DrawableElement {

    Vector3f color = new Vector3f(1.0f);

    boolean focused = false;

    public boolean visible = false;

    Framebuffer widgetBuffer;
    Material customMat = new Material();
    Vector3f boxPosition = new Vector3f();
    Vector3f boxDimensions = new Vector3f();
    Vector3f texPosition = new Vector3f(0,0,0);
    Vector3f texScale = new Vector3f(1,1,0);


    public ScrollableContainer(OpenGLState openGLState, int positionX, int positionY, int width, int height){
        super();
        widgetBuffer = FramebufferUtils.generateTextureFramebuffer(openGLState, width, height);
//        widgetBuffer = FramebufferUtils.generateScreensizeTextureFramebuffer();
        customMat.setTexturePointer(widgetBuffer.getTexture().getTexturePointer());
//        customMat.setTexturePointer(Globals.assetManager.fetchTexture("Textures/Testing1.png").getTexturePointer());
        float ndcX =      (float)positionX/Globals.WINDOW_WIDTH;
        float ndcY =      (float)positionY/Globals.WINDOW_HEIGHT;
        float ndcWidth =  (float)width/Globals.WINDOW_WIDTH;
        float ndcHeight = (float)height/Globals.WINDOW_HEIGHT;
        setWidth(width);
        setHeight(height);
        boxPosition = new Vector3f(ndcX,ndcY,0);
        boxDimensions = new Vector3f(ndcWidth,ndcHeight,0);
    }



    @Override
    public boolean handleEvent(Event event) {
        return false;
    }

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

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

    //recursively check if focused element is child of input element or is input element
    boolean containsFocusedElement(Element parent){
        Element focusedElement = Globals.elementManager.getFocusedElement();
        if(parent == focusedElement){
            return true;
        } else if(parent instanceof ContainerElement){
            ContainerElement container = (ContainerElement)parent;
            for(Element child : container.getChildren()){
                if(containsFocusedElement(child)){
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public void draw(
        RenderPipelineState renderPipelineState,
        OpenGLState openGLState,
        int parentFramebufferPointer,
        int parentPosX,
        int parentPosY,
        int parentWidth,
        int parentHeight
    ) {
        //figure out if currently focused element is a child or subchild of this container
        if(containsFocusedElement(this)){
            //if it is, if it is offscreen, calculate offset to put it onscreen
            Element focused = Globals.elementManager.getFocusedElement();
            if(
                focused.getRelativeX() + focused.getWidth() > this.width ||
                focused.getRelativeY() + focused.getHeight() > this.height ||
                focused.getRelativeX() < 0 ||
                focused.getRelativeY() < 0
            ){
                int neededOffsetX = 0;
                int neededOffsetY = 0;
                //basically if we're offscreen negative, pull to positive
                //if we're offscreen positive and we're not as large as the screen, pull from the positive into focus
                //if we are larger than the screen, set position to 0
                if(focused.getRelativeX() < 0){
                    neededOffsetX = -focused.getRelativeX();
                } else if(focused.getRelativeX() + focused.getWidth() > this.width){
                    if(focused.getWidth() > this.width){
                        neededOffsetX = -focused.getRelativeX();
                    } else {
                        neededOffsetX = -((focused.getRelativeX() - this.width) + focused.getWidth());
                    }
                }
                if(focused.getRelativeY() < 0){
                    neededOffsetY = -focused.getRelativeY();
                } else if(focused.getRelativeY() + focused.getHeight() > this.height){
                    if(focused.getHeight() > this.height){
                        neededOffsetY = -focused.getRelativeY();
                    } else {
                        neededOffsetY = -((focused.getRelativeY() - this.height) + focused.getHeight());
                        // System.out.println(focused.getPositionY() + " " + this.height + " " + focused.getHeight());
                    }
                }
                //apply offset to all children
                for(Element child : childList){
                    int newX = child.getRelativeX() + neededOffsetX;
                    int newY = child.getRelativeY() + neededOffsetY;
                    child.setPositionX(newX);
                    child.setPositionY(newY);
                    // System.out.println(currentX + " " + currentY);
                }
            }
        }

        float ndcX =      (float)internalPositionX/parentWidth;
        float ndcY =      (float)internalPositionY/parentHeight;
        float ndcWidth =  (float)internalWidth/parentWidth;
        float ndcHeight = (float)internalHeight/parentHeight;
        boxPosition = new Vector3f(ndcX,ndcY,0);
        boxDimensions = new Vector3f(ndcWidth,ndcHeight,0);

        //grab assets required to render window
        Model planeModel = Globals.assetManager.fetchModel(Globals.imagePlaneModelID);
        Texture windowFrame = Globals.assetManager.fetchTexture("Textures/ui/uiFrame1.png");


        widgetBuffer.bind(openGLState);
        openGLState.glViewport(width, height);
        
        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        
        for(Element child : childList){
            if(child instanceof DrawableElement){
                DrawableElement drawableChild = (DrawableElement) child;
                drawableChild.draw(renderPipelineState,openGLState,widgetBuffer.getFramebufferPointer(),0,0,width,height);
            }
        }
        //this call binds the screen as the "texture" we're rendering to
        //have to call before actually rendering
        openGLState.glBindFramebuffer(GL_FRAMEBUFFER, parentFramebufferPointer);
        openGLState.glViewport(parentWidth, parentHeight);

        //render background of window
        if(planeModel != null && windowFrame != null){
            planeModel.pushUniformToMesh("plane", "mPosition", boxPosition);
            planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions);
            planeModel.pushUniformToMesh("plane", "tPosition", texPosition);
            planeModel.pushUniformToMesh("plane", "tDimension", texScale);
            planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", color);
            customMat.setTexturePointer(windowFrame.getTexturePointer());
            planeModel.getMeshes().get(0).setMaterial(customMat);
            planeModel.drawUI();
        }
        
        if(planeModel != null){
            planeModel.pushUniformToMesh("plane", "mPosition", boxPosition);
            planeModel.pushUniformToMesh("plane", "mDimension", boxDimensions);
            planeModel.pushUniformToMesh("plane", "tPosition", texPosition);
            planeModel.pushUniformToMesh("plane", "tDimension", texScale);
            planeModel.pushUniformToMesh(planeModel.getMeshes().get(0).getMeshName(), "color", color);
            customMat.setTexturePointer(widgetBuffer.getTexture().getTexturePointer());
            planeModel.getMeshes().get(0).setMaterial(customMat);
            planeModel.drawUI();
        } else {
            LoggerInterface.loggerRenderer.ERROR("ScrollableContainer unable to find plane model!!", new Exception());
        }
    }
    
}
