package org.artisanlogiciel.games.maze.model;

import org.artisanlogiciel.games.maze.MovesProvider;
import org.artisanlogiciel.games.maze.Position;
import org.artisanlogiciel.games.maze.WallsProvider;

/**
 * minimal model without repetition
 * keep only left and down wall and rely on neightbor squares to obtain right and up
 * left down compatible with Bricks
 *
 * LEFT 01
 * DOWN 10
 *
 * 2 bits for walls
 *
 * bit value :
 * - set : there is a wall , no move in that direction
 * - unset : move is possible , there is no wall
 *
 */
public class HalfSquareRasterModel
    implements WallsProvider,
        MovesProvider,
        WidthHeightProvider,
        HalfSquareProvider
{
    int LEFT = 0x01;
    int DOWN = 0x02;
    // RIGHT 4
    // UP 8

    // 64 bits for long, 4 bits reserved
    int USED_BITS = 60;
    // 2 bits for LEFT UP, ( half square )
    int BITSX = 2;

    int width;
    int height;

    long lines[][];

    public HalfSquareRasterModel(int width, int height)
    {
        this.width = width;
        this.height = height;

        lines = new long[height][getLongs()];
    }

    public int getLongs()
    {
        return (BITSX * width)/USED_BITS;
    }

    public long[][] getLines() {
        return lines;
    }

    public void setLeftDown(int x, int y, short moves)
    {
        if (( x < width ) && ( y < height)) {
            int index = x % (USED_BITS / BITSX);
            long mask = 0xffffffff ^ ((moves & 0x3) << index);
            lines[y][(BITSX * x) / USED_BITS] &= mask;
        }
    }

    public void setWalls(int x, int y, short moves)
    {
        short walls = (short) ~ moves;
        setMoves(x,y,moves);
    }

    public void setMoves(int x, int y, short moves)
    {
        setLeftDown(x,y,moves); // left up
        setLeftDown(x+1,y,(short) (moves & (LEFT << BITSX))); // right
        setLeftDown(x,y-1,(short) (moves & (DOWN << BITSX))); // up
    }

    public long getLeftDown(int x, int y)
    {
        if (( x < width ) && ( y < height)) {
            // left up
            long line = lines[y][(BITSX * x) / USED_BITS];
            int index = x % (USED_BITS / BITSX);
            return ((line >> index) & (LEFT | DOWN));
        }
        return 0;
    }

    public short getWalls(int x, int y)
    {
        return (short) (
                        getLeftDown(x,y) |
                        ( getLeftDown(x+1,y) & LEFT ) << USED_BITS | // right
                        ( getLeftDown( x, y-1) & DOWN) << USED_BITS   // up
        );
    }

    public short getWalls(Position cell)
    {
        return getWalls(cell.getX(), cell.getY());
    }


    public short getMoves(int x, int y)
    {
        // moves are where there is no walls ...
        return (short) ((~ getWalls(x,y)) & 0x0f);
    }

    @Override
    public int getWidth() {
        return width;
    }

    @Override
    public int getHeight() {
        return height;
    }
}
