package org.xander.chessboard;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static org.xander.chessboard.figures.Figure.KING;

public abstract class FiguresPlacement implements PlacementBehavior {
    public static final char EMPTY_FIELD = '.';
    public static final char FIELD_UNDER_ATTACK = 'x';
    public static final char NEXT_LINE_FIELD = '\n';

    protected final BoardUtils boardUtils = new BoardUtils();

    public String placeNumberOfFiguresOnBoard(int numberOfFigures, String board) {
        if (board.contains(".")) {
            String boardWithFigures = board;
            while (numberOfFigures > 0) {
                boardWithFigures = placeOneFigureOnBoardSequentially(boardWithFigures);
                boardWithFigures = calculateAttackPlaces(boardWithFigures);
                numberOfFigures--;
            }
            return boardWithFigures;
        }
        return board;
    }

    public String placeFigureOnBoard(char figure, String board) {
        StringBuilder chessboardWithFigures = new StringBuilder();
        char[] boardElements = board.toCharArray();
        for (int i = 0 ; i < boardElements.length; i++) {
            if (boardElements[i] != NEXT_LINE_FIELD && boardElements[i] == EMPTY_FIELD) {
                boardElements[i] = figure;
                break;
            }
        }
        for (char element : boardElements) {
            chessboardWithFigures.append(element);
        }
        return chessboardWithFigures.toString();
    }


    public Set<String> placeNumberOfFiguresOnBoardAll(int numberOfFigures, String board) {
        Set<String> boards = new HashSet<>();

        if (board.contains(".")) {
            while (numberOfFigures > 0) {
                if (boards.isEmpty()) {
                    boards.addAll(placeFigureOnBoardRandomly('k', board).stream()
                            .map(this::calculateAttackPlaces1)
                            .collect(Collectors.toSet()));
                } else {
                    Set<String> stringsWithoutAttack = boards.stream()
                            .map(e -> placeFigureOnBoardRandomly('k', e))
                            .flatMap(Set::stream)
                            .collect(Collectors.toSet());

                    boards.clear();

                    Set<String> stringsWithAttack = stringsWithoutAttack.stream()
                            .map(this::calculateAttackPlaces1)
                            .collect(Collectors.toSet());

                    boards.addAll(stringsWithAttack);
                }
                numberOfFigures--;
            }
            return boards;
        }
        return boards;
    }


    public String calculateAttackPlaces1(String board) {
        char[] boardElements = board.toCharArray();
        //mind the '\n' character
        int dimension = (int) Math.sqrt(board.length()) + 1;
        Chessboard.checkBoard(board, dimension);

        for (int i = 0 ; i < boardElements.length; i++) {
            if (boardElements[i] == KING.getFigure()) {
                placeHorizontally(boardElements, i, dimension);
                placeVertically(boardElements, i, dimension);
                placeDiagonallyAbove(boardElements, i, dimension);
                placeDiagonallyBelow(boardElements, i, dimension);
            }
        }
        return boardUtils.transformArrayToStringBuilder(boardElements);
    }

    private void placeHorizontally(char[] boardElements, int position, int dimension) {
        if (position % dimension + 1 < dimension) {
            if (boardElements[position + 1] != '\n' && boardElements[position + 1] == EMPTY_FIELD) {
                boardElements[position + 1] = FIELD_UNDER_ATTACK;
            }
        }
        if (position % dimension - 1 >= 0) {
            if (boardElements[position - 1] != '\n' && boardElements[position - 1] == EMPTY_FIELD) {
                boardElements[position - 1] = FIELD_UNDER_ATTACK;
            }
        }
    }

    private void placeVertically(char[] boardElements, int position, int dimension) {
        if (position + dimension < boardElements.length) {
            if (boardElements[position + dimension] != '\n' && boardElements[position + dimension] == EMPTY_FIELD) {
                boardElements[position + dimension] = FIELD_UNDER_ATTACK;
            }
        }

        if (position - dimension >= 0) {
            if (boardElements[position - dimension] != '\n' && boardElements[position - dimension] == EMPTY_FIELD) {
                boardElements[position - dimension] = FIELD_UNDER_ATTACK;
            }
        }
    }

    private void placeDiagonallyAbove(char[] boardElements, int position, int dimension) {
        if (position - dimension - 1 >= 0 && (position - dimension - 1) % dimension >= 0 ) {
            if (boardElements[position - dimension - 1] != '\n'
                    && boardElements[position - dimension - 1] == EMPTY_FIELD) {
                boardElements[position - dimension - 1] = FIELD_UNDER_ATTACK;
            }
        }

        if (position - dimension + 1 >= 0 && (position - dimension + 1) % dimension < dimension - 1) {
            if (boardElements[position - dimension + 1] != '\n'
                    && boardElements[position - dimension + 1] == EMPTY_FIELD) {
                boardElements[position - dimension + 1] = FIELD_UNDER_ATTACK;
            }
        }
    }

    private void placeDiagonallyBelow(char[] boardElements, int position, int dimension) {
        if((position + dimension - 1) % dimension < dimension
                && position + dimension - 1 < boardElements.length) {
            if (boardElements[position + dimension - 1] != '\n'
                    && boardElements[position + dimension - 1] == EMPTY_FIELD) {
                boardElements[position + dimension - 1] = FIELD_UNDER_ATTACK;
            }
        }

        if(position + dimension + 1 < boardElements.length
                && (position + dimension + 1) % dimension < dimension) {
            if (boardElements[position + dimension + 1] != '\n'
                    && boardElements[position + dimension + 1] == EMPTY_FIELD) {
                boardElements[position + dimension + 1] = FIELD_UNDER_ATTACK;
            }
        }
    }

    public Set<String> placeFigureOnBoardRandomly(char figure, String board) {
        List<Integer> numberOfEmptyPlaces = IntStream.range(0, board.length())
                .filter(i -> board.charAt(i) == '.')
                .boxed()
                .collect(Collectors.toList());

        Set<String> setOfPossibleBoards = new HashSet<>(numberOfEmptyPlaces.size());

        for (int i = 0; i < board.length(); i++) {
            char[] boardArray = board.toCharArray();
            if(boardArray[i] != NEXT_LINE_FIELD && boardArray[i] == EMPTY_FIELD){
                boardArray[i] = figure;
                StringBuilder chessboardWithFigures = new StringBuilder();
                for (char element : boardArray) {
                    chessboardWithFigures.append(element);
                }
                setOfPossibleBoards.add(chessboardWithFigures.toString());
            }
        }
        return setOfPossibleBoards;
    }

}

@@ -1803,32 +1803,81 @@
 fFigures %3E 0) %7B%0A
+                Set%3CString%3E boardRepresentation;%0A
                 
@@ -1925,25 +1925,33 @@
    board
-s.addAll(
+Representation = 
 placeFig
@@ -2109,17 +2109,16 @@
 toSet())
-)
 ;%0A      
@@ -2160,40 +2160,27 @@
     
-Set%3CString%3E stringsWithoutAttack
+boardRepresentation
  = b
@@ -2322,186 +2322,8 @@
 am)%0A
-                            .collect(Collectors.toSet());%0A%0A                    boards.clear();%0A%0A                    Set%3CString%3E stringsWithAttack = stringsWithoutAttack.stream()%0A
     
@@ -2435,25 +2435,24 @@
 s.toSet());%0A
-%0A
             
@@ -2470,52 +2470,93 @@
 rds.
-addAll(stringsWithAttack);%0A                %7D
+clear();%0A                %7D%0A                boardRepresentation.forEach(boards::add);%0A
 %0A   

simplification of lamdba