#include "aps.hpp"
#include "puzzle.h"

namespace puzzle {

PosIds::PosIds(int n) : vector<PosId>(n)
{
}

PosMap::PosMap(Part apart) : part(apart)
{
}

std::ostream &operator<<(std::ostream &output, const PosMap &posmap)
{
	output << posmap.part << ':' << posmap.posids;
	return (output);
}

PosMapIds::PosMapIds(int n) : vector<PosMapId>(n)
{
}

Hed::Hed(int n) : PosMapIds(n)
{
}

Heds::Heds(int n) : vector<Hed>(n)
{
}

Shape::Shape(int n) : heds(n)
{
}

Shapes::Shapes(int n) : vector<Shape>(n)
{
}

std::ostream &operator<<(std::ostream &output, const Shape &shape)
{
	output << shape.n << ':' << shape.heds;
	return (output);
}

void PosMapper::setoutputmaps(const Maps &maps)
{
	outputmaps = maps.close();
}

PosMapId PosMapper::add(const Part &part)
{
	PosMapId posmapid;

	posmapid = posmaps.size();
	posmaps.push_back(PosMap(part));
	return (posmapid);
}

Part &PosMapper::getpart(PosMapId posmapid)
{
	return (posmaps[posmapid].part);
}

PosId PosMapper::getposid(PosMapId posmapid, int i)
{
	PosIds &posids = posmaps[posmapid].posids;

	if (!posids.size()) makeposids(posids, posmaps[posmapid].part.pos);
	return (posids[i]);
}

void PosMapper::makeposids(PosIds &posids, const Pos &pos)
{
	Maps::const_iterator sb, si, se;
	PosIds::iterator di;
	::Pos p;

	posids.resize(outputmaps.size());
	sb = outputmaps.begin();
	se = outputmaps.end();
	di = posids.begin();
	for (si = sb; si != se; si++, di++)
		*di = posdb.set(sort(p = (*si * pos)));
}

bool PosMapper::isnew(const PosMapIds &posmapids)
{
	PosIds posids;
	PosMapIds::const_iterator pmb, pmi, pme;
	PosIds::iterator pi;
	int i;

	posids = PosIds(posmapids.size());
	for (i = 0; i < outputmaps.size(); i++) {
		pmb = posmapids.begin();
		pme = posmapids.end();
		pi = posids.begin();
		for (pmi = pmb; pmi != pme; pmi++, pi++)
			*pi = getposid(*pmi, i);
		if (!posidsdb.isnew(sort(posids)) && !i) return (false);
	}
	return (true);
}

void PartMapper::setinputmaps(const Maps &maps)
{
	inputmaps = maps.close();
}

Poss PartMapper::map(const Poss &poss) const
{
	return (inputmaps * poss);
}

void PartMapper::add(const Part &part)
{
	MapPosId mapposid;
	PosIdId posidid;

	mapposid = mappingposdb.set(part.pos);
	if (mapposid == mappingparts.size()) savemappedpart(part);
	mapparts.push_back(mappingparts[mapposid]);
}

void PartMapper::savemappedpart(const Part &part)
{
	Poss poss;
	Pos pos;
	int i, n;

	poss = Poss(n = inputmaps.size());
	for (i = 0; i < n; i++) poss[i] = sort(inputmaps[i] * part.pos);
	pos = *std::min_element(poss.begin(), poss.end());
	for (i = 0; i < n; i++) poss[i] = sort(inputmaps[i] * pos);
	i = std::find(poss.begin(), poss.end(), part.pos) - poss.begin();
	savemappingpart(poss, i);
	for (i = 0; i < n; i++) savemappingpart(poss, i);
	mappedparts.push_back(Part(part.shapeid, pos));
}

void PartMapper::savemappingpart(const Poss &poss, int i)
{
	if (mappingposdb.set(poss[i]) == mappingparts.size())
		mappingparts.push_back(MapPart(i, mappedparts.size()));
}

MapParts &PartMapper::getmapparts(void)
{
	return (mapparts);
}

Parts &PartMapper::getmappedparts(void)
{
	return (mappedparts);
}

int Puzzle::loadshape(Shape &dst, const ::Shape &src, ShapeId shapeid)
{
	Poss::const_iterator pb, pi, pe;
	Pos p;
	Poss poss;
	Posdb posdb;

	dst.n = src.n;
	dst.heds.resize(nsit);
	poss = partmapper.map(src.poss);
	pb = poss.begin();
	pe = poss.end();
	for (pi = pb; pi != pe; pi++)
		if (pos.includes(p = sort(*pi)) && posdb.isnew(p))
			dst.heds[sitemap[p[0]]].push_back(posmapper.add(Part(shapeid, p)));
	return (dst.n);
}

Puzzle::Puzzle(LocId anloc)
	: nloc(anloc)
{
	setpos(Pos(anloc));
}

void Puzzle::setpos(const Pos &apos)
{
	nsit = apos.size();
	pos = apos;
	sitemap = Map(pos.begin(), pos.end()).inverse(nloc);
	setmaps(Map(pos.size()));
}

SitId Puzzle::getnsit(void)
{
	return (nsit);
}

int Puzzle::getwholeshapes(void)
{
	ishapes = shapes.begin();
	return (shapes.size());
}

int Puzzle::getshapeparts(void)
{
	ishape = ishapes->heds.begin();
	return (ishapes++->n);
}

int Puzzle::gethedposs(void)
{
	ihed = ishape->begin();
	return (ishape++->size());
}

int Puzzle::getpossits(void)
{
	Part &part = posmapper.getpart(*ihed++);

	ipos = part.pos.begin();
	return (part.pos.size());
}

SitId Puzzle::getsit(void)
{
	return (sitemap[*ipos++]);
}

void Puzzle::setsol(void)
{
}

void Puzzle::setsolparts(int nparts)
{
	iposmapids = posmapids.begin();
}

void Puzzle::setsolpart(ShapeId shapeid, SitId hedid, SitId sitid)
{
	*iposmapids++ = shapes[shapeid].heds[hedid][sitid];
	if (iposmapids == posmapids.end()) savesol();
}

void Puzzle::savesol(void)
{
	PosMapIds::const_iterator pb, pi, pe;
	PartIds::iterator si;
	PartIds solution;
	vector<PartIds::iterator> ss;
	vector<PartIds::iterator>::iterator ssi;
	Shapes::iterator hb, hi, he;
	PosIdId posidid;

	if (!posmapper.isnew(posmapids)) return;
	solution = ::PartIds(posmapids.size());
	ss = vector< ::PartIds::iterator>(shapes.size());
	hb = shapes.begin();
	he = shapes.end();
	ssi = ss.begin();
	si = solution.begin();
	for (hi = hb; hi != he; si += hi++->n) *ssi++ = si;
	pb = posmapids.begin();
	pe = posmapids.end();
	for (pi = pb; pi != pe; pi++) {
		Part &part = posmapper.getpart(*pi);
		posidid = posiddb.set(posmapper.getposid(*pi));
		*ss[part.shapeid]++ = posidid;
		if (posidid == parts.size()) {
			parts.push_back(part);
			partmapper.add(part);
		}
	}
	solutions.push_back(solution);
}

void Puzzle::setmaps(const Maps &maps)
{
	setinputmaps(maps);
	setoutputmaps(maps);
}

void Puzzle::setinputmaps(const Maps &maps)
{
	partmapper.setinputmaps(maps);
}

void Puzzle::setoutputmaps(const Maps &maps)
{
	posmapper.setoutputmaps(maps);
}

void Puzzle::setshapes(const ::Shapes &inputshapes)
{
	Shapes::iterator di;
	::Shapes::const_iterator  sb, si, se;
	ShapeId shapeid;
	int npart;

	shapes = Shapes(inputshapes.size());
	sb = inputshapes.begin();
	se = inputshapes.end();
	di = shapes.begin();
	npart = 0;
	shapeid = 0;
	for (si = sb; si != se; si++, di++)
		npart += loadshape(*di, *si, shapeid++);
	posmapids = PosMapIds(npart);
}

void Puzzle::search(search::ServerBase &search)
{
	search.run(this);
}

PartIdss &Puzzle::getsolutions(void)
{
	return (solutions);
}

Parts &Puzzle::getparts(void)
{
	return (parts);
}

MapParts &Puzzle::getmapparts(void)
{
	return (partmapper.getmapparts());
}

Parts &Puzzle::getmappedparts(void)
{
	return (partmapper.getmappedparts());
}

} // end of namespace puzzle
