package sudoku.io;

import java.util.ArrayList;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONObject;

import sudoku.Block;
import sudoku.Cell;
import sudoku.ImmutableCell;
import sudoku.MultiDoku;
import sudoku.MutableCell;
import sudoku.Sudoku;

public class SudokuSerializer {

	public static String serializeSudoku(final MultiDoku multidoku) {
		List<Cell> cellIds = new ArrayList<>();
		List<Block> blockIds = new ArrayList<>();

		JSONObject jsonRoot = new JSONObject();
		JSONArray jsonCells = new JSONArray();
		JSONArray jsonBlocks = new JSONArray();
		JSONArray jsonSudokus = new JSONArray(multidoku.getNbSubGrids());

		for (int i = 0; i < multidoku.getNbSubGrids(); i++) {
			Sudoku sudoku = multidoku.getSubGrid(i);

			// init cells
			for (Cell cell : sudoku.getCells()) {
				if (!cellIds.contains(cell)) {
					cellIds.add(cell);

				}

				Block block = cell.getBlock();
				if (!blockIds.contains(block)) {
					blockIds.add(block);
				}

				int blockID = blockIds.indexOf(block);
				int symboleIndex = cell.getSymbolIndex();

				JSONObject cellJsonObject = new JSONObject();
				cellJsonObject.put("blockID", blockID);
				cellJsonObject.put("symboleIndex", symboleIndex);
				if (cell instanceof ImmutableCell) {
					cellJsonObject.put("immutable", true);
				}
				jsonCells.put(cellJsonObject);
			}

		}

		// init blocks

		for (int i = 0; i < blockIds.size(); i++) {
			JSONObject blockJsonObject = new JSONObject();
			JSONArray cellsJsonArray = new JSONArray();
			Block block = blockIds.get(i);
			for (Cell cell : block.getCells()) {
				int cellID = cellIds.indexOf(cell);
				cellsJsonArray.put(cellID);
			}
			blockJsonObject.put("cellIDs", cellsJsonArray);
			jsonBlocks.put(blockJsonObject);
		}

		for (int i = 0; i < multidoku.getNbSubGrids(); i++) {
			// serialise sub grid
			JSONObject jsonSudoku = new JSONObject();
			JSONArray cellsJsonArray = new JSONArray();
			JSONArray blocksJsonArray = new JSONArray();

			Sudoku sudoku = multidoku.getSubGrid(i);

			// serialize cells

			for (Cell cell : sudoku.getCells()) {
				int cellID = cellIds.indexOf(cell);
				cellsJsonArray.put(cellID);
			}

			// serialize blocks

			for (Block block : sudoku.getBlocks()) {
				int blockID = blockIds.indexOf(block);
				blocksJsonArray.put(blockID);
			}

			jsonSudoku.put("cells", cellsJsonArray);
			jsonSudoku.put("blocks", blocksJsonArray);
			jsonSudokus.put(i, jsonSudoku);
		}

		jsonRoot.put("multidoku", jsonSudokus);
		jsonRoot.put("cells", jsonCells);
		jsonRoot.put("blocks", jsonBlocks);
		return jsonRoot.toString();
	}

	public static MultiDoku deserializeSudoku(final String data) {
		JSONObject jsonObject = new JSONObject(data);

		List<Cell> cells = new ArrayList<>();
		List<Block> blocks = new ArrayList<>();
		List<Sudoku> sudokus = new ArrayList<>();

		// init cells
		JSONArray cellsJson = jsonObject.getJSONArray("cells");
		for (int i = 0; i < cellsJson.length(); i++) {
			JSONObject entry = cellsJson.getJSONObject(i);
			int symboleIndex = entry.getInt("symboleIndex");
			if (entry.has("immutable")) {
				cells.add(new ImmutableCell(symboleIndex));
			} else {
				cells.add(new MutableCell(symboleIndex));
			}
		}

		// init blocks
		JSONArray blocksJson = jsonObject.getJSONArray("blocks");
		for (int i = 0; i < blocksJson.length(); i++) {
			JSONObject entry = blocksJson.getJSONObject(i);
			JSONArray cellIds = entry.getJSONArray("cellIDs");
			List<Cell> blockCells = new ArrayList<>();
			Block newBlock = new Block(blockCells);
			for (int j = 0; j < cellIds.length(); j++) {
				int cellID = cellIds.getInt(j);
				Cell blockCell = cells.get(cellID);
				blockCell.setBlock(newBlock);
				blockCells.add(blockCell);
			}
			blocks.add(newBlock);
		}

		JSONArray multidokuJsonObject = jsonObject.getJSONArray("multidoku");
		for (int i = 0; i < multidokuJsonObject.length(); i++) {
			JSONObject sudokuJsonObject = multidokuJsonObject.getJSONObject(i);
			JSONArray sudokuCellsJsonArray = sudokuJsonObject.getJSONArray("cells");
			JSONArray sudokuBlocksJsonArray = sudokuJsonObject.getJSONArray("blocks");

			List<Cell> sudokuCells = new ArrayList<>();
			List<Block> sudokuBlocks = new ArrayList<>();

			for (int j = 0; j < sudokuCellsJsonArray.length(); j++) {
				int cellID = sudokuCellsJsonArray.getInt(j);
				sudokuCells.add(cells.get(cellID));
			}

			for (int j = 0; j < sudokuBlocksJsonArray.length(); j++) {
				int blockID = sudokuBlocksJsonArray.getInt(j);
				sudokuBlocks.add(blocks.get(blockID));
			}

			sudokus.add(new Sudoku(sudokuCells, sudokuBlocks, null));
		}

		return new MultiDoku(sudokus);
	}

}
