// cube.cpp - high-level program based on box module.
// Input is a series of integers for a box assembly puzzle.
// Output are diagrams of solutions and positions of parts in solutions.
// The input is as follows:
// 1) An integer number of dimensions, then for each dimension,
// 1) A) An integer length of each dimension
// 2) An integer number of excluded locations, then for each location,
// 2) A) For each dimension,
// 2) A) 1) An integer offset from 0 of the location in that dimension
// 3) A string with one symbol for each shape, then for each shape,
// 3) A) A string with one symbol for each part,
// 3) B) An integer number of locations, then for each location,
// 3) B) 1) For each dimension,
// 3) B) 1) A) An integer offset from 0 of the location in that dimension
// The program only supports one position for each part. Translations and
// rotations of that position are generated by the program.
// Also, it is assumed that parts can not be reflected and
// that solutions are not unique if they can be produced by reflecting parts.
// Symmetries are preserved where present in the box and the excluded locations.

#include <fstream>
#include "search.hpp"
#include "box.h"

// class Boxer - for box puzzle, read input, find solutions and print output
class Boxer {

private:

// searchserver - search server with arbitrary number of locations
search::Server<search::PosVariable> searchserver;

// shapes - puzzle shape data
Shapes shapes;

// partcodes - table of characters per part
std::string partcodes;

// shapecodes - table of characters per shape
std::string shapecodes;

box::Ords getords(std::istream &input, int n)
{
	box::Ords ords;
	box::Ords::iterator i;

	ords = box::Ords(n);
	for (i = ords.begin(); i != ords.end(); i++)
		input >> *i;
	return (ords);
}

box::Ordss getordss(std::istream &input, int n, int m)
{
	box::Ordss ordss;
	box::Ordss::iterator i;

	ordss = box::Ordss(m);
	for (i = ordss.begin(); i != ordss.end(); i++)
		*i = getords(input, n);
	return (ordss);
}

Pos getpos(std::istream &input, box::Space &space)
{
	int m;

	input >> m;
	return (space.getpos(getordss(input, space.getlens().size(), m)));
}

// getshapes - read shapes data from input
void getshapes(std::istream &input, box::Space &space)
{
	std::string s;
	Shapes::iterator si;

	shapes = Shapes(shapecodes.size());
	for (si = shapes.begin(); si != shapes.end(); si++) {
		input >> s;
		partcodes += s;
		*si = Shape(s.size(), Poss(getpos(input, space)));
	}
}

public:

box::Box getbox(std::istream &input)
{
	int n;

	input >> n;
	return (box::Box(getords(input, n)));
}

// run - read from input, find solutions and print to output
void run(std::istream &input, std::ostream &output)
{
	box::Box box(getbox(input));

	box.setpos(set_difference(Pos(box.getnloc()), getpos(input, box)));
	input >> shapecodes;
	getshapes(input, box);
	box.setshapes(shapes);
	box.setreflectout(true);
	box.search(searchserver);
	box.print(output, partcodes, shapecodes);
}

}; // end of class Boxer

// main - run cube program, input from standard input or filename on
// command line. output to standard output
int main(int argc, char **argv)
{
	std::ifstream infile;

	if (argc > 2) {
		std::cerr << "Usage: " << *argv << " [file]" << std::endl;
		return (1);
	}
	if (argc > 1) infile.open(argv[1], std::ifstream::in);
	if (argc > 1 &&	!infile.good()) {
		std::cout << "Can't open " << argv[1] << std::endl;
		exit(1);
	}
	Boxer().run(argc > 1? infile: std::cin, std::cout);
	return (0);
}
