package com.draga.spaceTravels3.physic;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.PixmapIO;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Rectangle;
import com.draga.PooledVector2;
import com.draga.spaceTravels3.Constants;
import com.draga.spaceTravels3.component.physicsComponent.PhysicsComponent;
import com.draga.spaceTravels3.component.physicsComponent.PhysicsComponentType;
import com.draga.spaceTravels3.gameEntity.GameEntity;
import com.draga.spaceTravels3.manager.GameEntityManager;
import com.draga.utils.FileUtils;
import com.google.common.base.Stopwatch;

import java.util.ArrayList;
import java.util.concurrent.TimeUnit;

public class GravityCache
{
    private static final String LOGGING_TAG = GravityCache.class.getSimpleName();
    private static final float  MIN_GRAVITY = 100f;
    private final GravityCacheNode rootNode;

    private Rectangle bounds;

    public GravityCache()
    {
        Stopwatch stopwatch = Stopwatch.createStarted();

        ArrayList<PhysicsComponent> staticPhysicsComponentsWithMass = new ArrayList<>();

        for (GameEntity gameEntity : GameEntityManager.getGameEntities())
        {
            if (gameEntity.physicsComponent.getPhysicsComponentType() == PhysicsComponentType.STATIC
                && gameEntity.physicsComponent.getMass() > 0)
            {
                staticPhysicsComponentsWithMass.add(gameEntity.physicsComponent);
            }
        }

        this.bounds = calculateBounds(staticPhysicsComponentsWithMass);
        this.rootNode = calculateNodes(staticPhysicsComponentsWithMass);

        float elapsed = stopwatch.elapsed(TimeUnit.NANOSECONDS) * Constants.General.NANO;
        Gdx.app.debug(LOGGING_TAG, "Caching gravity took " + elapsed + "s");

//        saveBitMap();
    }

    private Rectangle calculateBounds(ArrayList<PhysicsComponent> staticPhysicsComponentsWithMass)
    {
        try (PooledVector2 barycentre = PooledVector2.newVector2(0f, 0f))
        {
            float totalMass = 0f;
            for (PhysicsComponent otherPhysicsComponent : staticPhysicsComponentsWithMass)
            {
                totalMass += otherPhysicsComponent.getMass();
                try (PooledVector2 weightedPosition = otherPhysicsComponent.getPosition().cpy())
                {
                    weightedPosition.scl(otherPhysicsComponent.getMass());
                    barycentre.add(weightedPosition);
                }
            }
            barycentre.scl(1 / totalMass);
            Gdx.app.debug(
                LOGGING_TAG,
                "Barycentre calculated at x: " + barycentre.x + " y: " + barycentre.y);


            // TODO: does this even?
            float halfSizeSqrt = totalMass / MIN_GRAVITY;
            float halfSize = halfSizeSqrt * halfSizeSqrt;

            bounds = new Rectangle(
                barycentre.x - halfSize,
                barycentre.y - halfSize,
                halfSize * 2,
                halfSize * 2);
        }


        return bounds;
    }

    private GravityCacheNode calculateNodes(ArrayList<PhysicsComponent> staticPhysicsComponentsWithMass)
    {
        GravityCacheNode rootNode =
            new GravityCacheNode(this.bounds, staticPhysicsComponentsWithMass);

        return rootNode;
    }

    public PooledVector2 getCachedGravity(PhysicsComponent physicsComponent)
    {
        // Outside the bounds.
        if (!this.rootNode.getBounds().contains(physicsComponent.getPosition()))
        {
            return PooledVector2.newVector2(0f, 0f);
        }

        PooledVector2 gravity = this.rootNode.getGravity(physicsComponent.getPosition());

        return gravity;
    }

    private void saveBitMap()
    {
        float scale = 10000f / this.bounds.width;
        int pixmapWidth = MathUtils.ceil(this.bounds.width * scale);
        int pixmapHeight = MathUtils.ceil(this.bounds.height * scale);

        Pixmap pixmap = new Pixmap(
            pixmapWidth,
            pixmapHeight,
            Pixmap.Format.RGBA8888);

        pixmap.setColor(Color.WHITE);

        drawGravityCacheNodeRecursive(pixmap, rootNode, -this.bounds.x, -this.bounds.y, scale);

        PixmapIO.writePNG(FileUtils.getFileHandle("gravityBitmap.png"), pixmap);
    }

    private void drawGravityCacheNodeRecursive(
        Pixmap pixmap,
        GravityCacheNode gravityCacheNode,
        float offsetX,
        float offsetY,
        float scale)
    {
        float scaledWidth = gravityCacheNode.getBounds().width * scale;
        float scaledHeight = gravityCacheNode.getBounds().height * scale;
        pixmap.drawRectangle(
            (int) ((gravityCacheNode.getBounds().x + offsetX) * scale),
            (int) (((gravityCacheNode.getBounds().y + offsetY) * scale) + scaledHeight),
            (int) scaledWidth,
            (int) scaledHeight);

        if (gravityCacheNode.getTopLeftNode() != null)
        {
            drawGravityCacheNodeRecursive(
                pixmap,
                gravityCacheNode.getTopLeftNode(),
                offsetX,
                offsetY,
                scale);
        }
        if (gravityCacheNode.getTopRightNode() != null)
        {
            drawGravityCacheNodeRecursive(
                pixmap,
                gravityCacheNode.getTopRightNode(),
                offsetX,
                offsetY,
                scale);
        }
        if (gravityCacheNode.getBottomLeftNode() != null)
        {
            drawGravityCacheNodeRecursive(
                pixmap,
                gravityCacheNode.getBottomLeftNode(),
                offsetX,
                offsetY,
                scale);
        }
        if (gravityCacheNode.getBottomRightNode() != null)
        {
            drawGravityCacheNodeRecursive(
                pixmap,
                gravityCacheNode.getBottomRightNode(),
                offsetX,
                offsetY,
                scale);
        }
    }
}

@@ -858,24 +858,25 @@
 mpleName();%0A
+%0A
     private 
@@ -914,13 +914,12 @@
  = 1
-00
 f;%0A
+%0A
     
@@ -1855,33 +1855,8 @@
 s%22);
-%0A%0A//        saveBitMap();
 %0A   
@@ -2713,17 +2713,16 @@
 re.y);%0A%0A
-%0A
         
@@ -2729,147 +2729,65 @@
     
-// TODO: does this even?%0A            float halfSizeSqrt = totalMass / MIN_GRAVITY;%0A            float halfSize = halfSizeSqrt * halfSizeSqrt
+float radius = (float) Math.sqrt(totalMass / MIN_GRAVITY)
 ;%0A%0A 
@@ -2852,24 +2852,22 @@
 tre.x - 
-halfSize
+radius
 ,%0A      
@@ -2891,24 +2891,22 @@
 tre.y - 
-halfSize
+radius
 ,%0A      
@@ -2911,32 +2911,30 @@
             
-halfSize
+radius
  * 2,%0A      
@@ -2947,16 +2947,14 @@
     
-halfSize
+radius
  * 2
@@ -2967,17 +2967,16 @@
      %7D%0A%0A
-%0A
         

fix gravity cache boundaries calculations