package mindustry.world.blocks.environment;

import arc.*;
import arc.graphics.g2d.*;
import arc.math.*;
import mindustry.gen.*;
import mindustry.world.*;

import static mindustry.Vars.*;

/** Renders walls in packed square tiles, sized to be as large as possible given the provided size constraints. Concept and some code taken from Twcash/Aquarion. */
public class TiledWall extends StaticWall{
    public TextureRegion[][][][] sizedRegions;
    public int maxSize = 3;

    public TiledWall(String name){
        super(name);
    }

    public TiledWall(String name, int variants, int maxSize){
        super(name);
        this.variants = variants;
        this.maxSize = maxSize;
    }

    @Override
    public void load(){
        super.load();

        sizedRegions = new TextureRegion[maxSize + 1][Math.max(variants, 1)][][];

        for(int size = 1; size <= maxSize; size++){
            for(int v = 0; v < Math.max(variants, 1); v++){
                TextureRegion base = Core.atlas.find(name + "-" + size + "-" + v);
                int actualSize = size;
                if(!base.found()) base = Core.atlas.find(name + "-" + size);
                if(!base.found()){
                    actualSize = 1; //the 1x1 sprite should not be split like a larger region
                    base = region;
                }

                sizedRegions[size][v] = base.split(base.width / actualSize, base.height / actualSize);
            }
        }
    }

    long state(Tile tile){
        return world.tiles.getTmpBlockState(tile.array());
    }

    @Override
    public void blockChanged(Tile tile){
        if(headless) return;

        if(!world.isGenerating() && TiledState.changes(state(tile)) != world.tileChanges){
            scan(tile);
        }
    }

    void scan(Tile tile){
        //max size possible
        int size = maxSize;
        int changes = world.tileChanges;

        //scan to the top right for the biggest size possible
        for(int cx = 0; cx < size; cx++){
            for(int cy = 0; cy < size; cy++){
                Tile other = tile.nearby(cx, cy);
                if(other == null || other.block() != this || TiledState.changes(state(other)) == changes){
                    int max = Math.max(cx, cy);
                    size = Math.min(max, size);
                    size = Math.min(max, size);
                }
            }
        }

        //assign roots based on max value
        for(int cx = 0; cx < size; cx++){
            for(int cy = 0; cy < size; cy++){
                Tile other = tile.nearby(cx, cy);
                if(other != null){
                    long otherState = state(other);

                    if(!headless && TiledState.changes(otherState) != 0){
                        Tile otherRoot = other.nearby(-TiledState.x(otherState), -TiledState.y(otherState));
                        if(otherRoot != null){
                            Core.app.post(() -> renderer.blocks.floor.recacheTile(otherRoot));
                        }
                    }

                    //mark as updated
                    world.tiles.setTmpBlockState(other.array(), TiledState.get(cx, cy, size, changes));
                    if(!headless && otherState != 0){
                        Core.app.post(() -> renderer.blocks.floor.recacheTile(other));
                    }
                }
            }
        }
    }

    @Override
    public void drawBase(Tile tile){
        long state = state(tile);
        //stale state, start scanning.
        if(TiledState.changes(state) != world.tileChanges){
            scan(tile);
            //state has most likely updated
            state = state(tile);
        }

        int size = Mathf.clamp(TiledState.size(state), 1, maxSize);
        int cx = TiledState.x(state), cy = TiledState.y(state);
        int variant = variant(tile.x - cx, tile.y - cy, variants);

        TextureRegion[][] regions = sizedRegions[size][variant];

        Draw.rect(regions[Mathf.clamp(cx, 0, regions.length - 1)][Mathf.clamp(size - 1 - cy, 0, regions[0].length - 1)], tile.worldx(), tile.worldy());
    }
}
