package electrosphere.renderer.ui.elements;

import java.util.LinkedList;
import java.util.List;

import org.lwjgl.util.yoga.Yoga;

import electrosphere.logger.LoggerInterface;
import electrosphere.renderer.ui.elementtypes.ContainerElement;
import electrosphere.renderer.ui.elementtypes.DrawableElement;
import electrosphere.renderer.ui.elementtypes.Element;

/**
 * An element that contains other elements
 */
public class StandardContainerElement extends StandardElement implements ContainerElement {

    List<Element> childList = new LinkedList<Element>();

    public StandardContainerElement(){
        super();
    }

    @Override
    public int getWidth() {
        if(width == -1){
            if(childList.size() > 0){
                int minX = -1;
                int maxX = -1;
                for(Element child : childList){
                    if(minX == -1){
                        minX = child.getRelativeX();
                    } else if(child.getRelativeX() < minX){
                        minX = child.getRelativeX();
                    }
                    if(maxX == -1){
                        maxX = child.getRelativeX() + child.getWidth();
                    } else if(child.getRelativeX() + child.getWidth() > maxX){
                        maxX = child.getRelativeX() + child.getWidth();
                    }
                }
                if(minX == -1){
                    minX = 0;
                }
                if(maxX == -1){
                    maxX = 0;
                }
                return maxX - minX;
            } else {
                return 1;
            }
        } else {
            return width;
        }
    }

    @Override
    public int getHeight() {
        if(height == -1){
            if(childList.size() > 0){
                int minY = -1;
                int maxY = -1;
                for(Element child : childList){
                    if(minY == -1){
                        minY = child.getRelativeY();
                    } else if(child.getRelativeY() < minY){
                        minY = child.getRelativeY();
                    }
                    if(maxY == -1){
                        maxY = child.getRelativeY() + child.getHeight();
                    } else if(child.getRelativeY() + child.getHeight() > maxY){
                        maxY = child.getRelativeY() + child.getHeight();
                    }
                }
                if(minY == -1){
                    minY = 0;
                }
                if(maxY == -1){
                    maxY = 0;
                }
                return maxY - minY;
            } else {
                return 1;
            }
        } else {
            return height;
        }
    }

    @Override
    public int getRelativeX() {
        if(internalPositionX == -1){
            if(childList.size() > 0){
                int minX = -1;
                for(Element child : childList){
                    if(minX == -1){
                        minX = child.getRelativeX();
                    } else if(child.getRelativeX() < minX){
                        minX = child.getRelativeX();
                    }
                }
                if(minX == -1){
                    minX = 0;
                }
                return minX;
            } else {
                return internalPositionX;
            }
        } else {
            return internalPositionX;
        }
    }

    @Override
    public int getRelativeY() {
        if(internalPositionY == -1){
            if(childList.size() > 0){
                int minY = -1;
                for(Element child : childList){
                    if(minY == -1){
                        minY = child.getRelativeY();
                    } else if(child.getRelativeY() < minY){
                        minY = child.getRelativeY();
                    }
                }
                if(minY == -1){
                    minY = 0;
                }
                return minY;
            } else {
                return internalPositionY;
            }
        } else {
            return internalPositionY;
        }
    }

    // @Override
    // public void setWidth(int width) {
    //     this.width = width;
    // }

    // @Override
    // public void setHeight(int height) {
    //     this.height = height;
    // }

    @Override
    public void setPositionX(int posX) {
        internalPositionX = posX;
        absoluteX = posX;
    }
    @Override

    public void setPositionY(int posY) {
        internalPositionY = posY;
        absoluteY = posY;
    }

    @Override
    public void setDirection(int layout) {
        Yoga.YGNodeStyleSetDirection(yogaNode, layout);
    }

    @Override
    public void setFlexDirection(YogaFlexDirection layout){
        int directionInteger = Yoga.YGFlexDirectionColumn;
        switch(layout){
            case Column:
                directionInteger = Yoga.YGFlexDirectionColumn;
                break;
            case Column_Reverse:
                directionInteger = Yoga.YGFlexDirectionColumnReverse;
                break;
            case Row:
                directionInteger = Yoga.YGFlexDirectionRow;
                break;
            case Row_Reverse:
                directionInteger = Yoga.YGFlexDirectionRowReverse;
                break;
        }
        Yoga.YGNodeStyleSetFlexDirection(yogaNode, directionInteger);
    }

    @Override
    public void addChild(Element child) {
        childList.add(child);
        child.setParent(this);
        if(child instanceof DrawableElement){
            DrawableElement drawableChild = (DrawableElement) child;
            drawableChild.setParentWidth(width);
            drawableChild.setParentHeight(height);
            drawableChild.setVisible(true);
            Yoga.YGNodeInsertChild(yogaNode, drawableChild.getYogaNode(), childList.size() - 1);
        }
    }

    @Override
    public List<Element> getChildren() {
        return childList;
    }

    @Override
    public void removeChild(Element child) {
        childList.remove(child);
        child.setParent(null);
        Yoga.YGNodeRemoveChild(this.yogaNode, child.getYogaNode());
    }

    @Override
    public void clearChildren(){
        Yoga.YGNodeRemoveAllChildren(yogaNode);
        childList.clear();
    }

    @Override
    public int getChildOffsetX(){
        return 0;
    }

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

    @Override
    public float getChildScaleX(){
        return 1;
    }

    @Override
    public float getChildScaleY(){
        return 1;
    }

    @Override
    public void destroy(){
        for(Element child : childList){
            child.destroy();
        }
        Yoga.YGNodeFree(this.yogaNode);
    }

    @Override
    public void applyYoga(int parentX, int parentY) {
        //get the values from yoga
        float leftRaw = Yoga.YGNodeLayoutGetLeft(yogaNode);
        float topRaw = Yoga.YGNodeLayoutGetTop(yogaNode);
        float widthRaw = Yoga.YGNodeLayoutGetWidth(yogaNode);
        float heightRaw = Yoga.YGNodeLayoutGetHeight(yogaNode);
        LoggerInterface.loggerUI.DEBUG("" + this);
        LoggerInterface.loggerUI.DEBUG("pos(" + leftRaw + "," + topRaw + ") dim(" + widthRaw + "," + heightRaw + ")");
        //apply the values to this component
        if(!useAbsolutePosition){
            this.internalPositionX = (int)leftRaw;
            this.internalPositionY = (int)topRaw;
            this.internalWidth = (int)widthRaw;
            this.internalHeight = (int)heightRaw;
            //calculate absolute values
            this.absoluteX = parentX + internalPositionX;
            this.absoluteY = parentY + internalPositionY;
        }
        //apply yoga values to all children
        for(Element child : this.getChildren()){
            child.applyYoga(parentX + internalPositionX,parentY + internalPositionY);
        }
    }

    @Override
    public void setJustifyContent(YogaJustification justification){
        int justificationInteger = Yoga.YGJustifyFlexStart;
        switch(justification){
            case Center:
                justificationInteger = Yoga.YGJustifyCenter;
                break;
            case Start:
                justificationInteger = Yoga.YGJustifyFlexStart;
                break;
            case End:
                justificationInteger = Yoga.YGJustifyFlexEnd;
                break;
            case Around:
                justificationInteger = Yoga.YGJustifySpaceAround;
                break;
            case Between:
                justificationInteger = Yoga.YGJustifySpaceBetween;
                break;
            case Evenly:
                justificationInteger = Yoga.YGJustifySpaceEvenly;
                break;
        }
        Yoga.YGNodeStyleSetJustifyContent(this.yogaNode, justificationInteger);
    }

    @Override
    public void setAlignItems(YogaAlignment alignment){
        int alignmentInteger = Yoga.YGAlignAuto;
        switch(alignment){
            case Auto:
                alignmentInteger = Yoga.YGAlignAuto;
                break;
            case Start:
                alignmentInteger = Yoga.YGAlignFlexStart;
                break;
            case End:
                alignmentInteger = Yoga.YGAlignFlexEnd;
                break;
            case Around:
                alignmentInteger = Yoga.YGAlignSpaceAround;
                break;
            case Between:
                alignmentInteger = Yoga.YGAlignSpaceBetween;
                break;
            case Stretch:
                alignmentInteger = Yoga.YGAlignStretch;
                break;
            case Baseline:
                alignmentInteger = Yoga.YGAlignBaseline;
                break;
            case Center:
                alignmentInteger = Yoga.YGAlignCenter;
                break;
        }
        Yoga.YGNodeStyleSetAlignItems(this.yogaNode, alignmentInteger);
    }

    @Override
    public void setAlignContent(YogaAlignment alignment){
        int alignmentInteger = Yoga.YGAlignAuto;
        switch(alignment){
            case Auto:
                alignmentInteger = Yoga.YGAlignAuto;
                break;
            case Start:
                alignmentInteger = Yoga.YGAlignFlexStart;
                break;
            case End:
                alignmentInteger = Yoga.YGAlignFlexEnd;
                break;
            case Around:
                alignmentInteger = Yoga.YGAlignSpaceAround;
                break;
            case Between:
                alignmentInteger = Yoga.YGAlignSpaceBetween;
                break;
            case Stretch:
                alignmentInteger = Yoga.YGAlignStretch;
                break;
            case Baseline:
                alignmentInteger = Yoga.YGAlignBaseline;
                break;
            case Center:
                alignmentInteger = Yoga.YGAlignCenter;
                break;
        }
        Yoga.YGNodeStyleSetAlignContent(this.yogaNode, alignmentInteger);
    }

    @Override
    public void setWrap(YogaWrap wrap) {
        switch(wrap){
            case WRAP:
                Yoga.YGNodeStyleSetFlexWrap(yogaNode, Yoga.YGWrapWrap);
                break;
            case NO_WRAP:
                Yoga.YGNodeStyleSetFlexWrap(yogaNode, Yoga.YGWrapNoWrap);
                break;
            case REVERSE:
                Yoga.YGNodeStyleSetFlexWrap(yogaNode, Yoga.YGWrapReverse);
                break;
        }
    }

    @Override
    public void setOverflow(YogaOverflow overflow){
        switch(overflow){
            case Visible:
                Yoga.YGNodeStyleSetOverflow(yogaNode, Yoga.YGOverflowVisible);
                break;
            case Hidden:
                Yoga.YGNodeStyleSetOverflow(yogaNode, Yoga.YGOverflowHidden);
                break;
            case Scroll:
                Yoga.YGNodeStyleSetOverflow(yogaNode, Yoga.YGOverflowScroll);
                break;
        }
    }
    
    

    
}
