/***************************************************************************
                           corner.cpp
                           -------------------
    copyright            : (C) 2004 by Jos van den Oever
    email                : jos@vandenoever.info
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
#include "corner.h"
#include "question.h"
#include <iostream>
using std::cerr;
using std::endl;
#include <cstdlib>

void
Corner::reduceCorner() {
	for (short i=0; i<3; i++) {
		cor[i] = mapcor(i, cor[i]);
	}
}
// map corner values to 0 for C4, 0,1 for C2 and 0,1,2,3 for C4
short
Corner::mapcor(short s, short c) const {
	short r = 0;
	if (sym[s] == C1) {
		r = short(c%4);
		if (r < 0) r += short(4);
	} else if (sym[s] == C2) {
		r = short(c%2);
		if (r < 0) r += short(2);
	}
	return r;
}
bool
Corner::operator==(const Corner &c) const {
	for (short i=0; i<3; ++i) {
		bool equal = true;;
		for (short j=0; j<3; ++j) {
			if (type[j] != -1 && c.type[(j+i)%3] != -1) {
				equal &= type[j] == c.type[(j+i)%3];
				equal &= sym[j] == c.sym[(j+i)%3];
				equal &= cor[j] == c.cor[(j+i)%3];
			}
		}
		if (equal) {
			return true;
		}
	}
	return false;
}
bool
Corner::isBlank() const {
	for (short i=0; i<3; ++i) {
		if (type[i] != 0 || sym[i] != C4) return false;
	}
	return true;
}
bool
Corner::is2_3Blank() const {
	bool oneblank = false;
	for (short i=0; i<3; ++i) {
		if (type[i] == 0 && sym[i] == C4) {
			if (oneblank) return true;
			oneblank = true;
		}
	}
	return false;
}
short
Corner::count(short t, Symmetry s) const {
	short c = 0;
	for (short i=0; i<3; i++) {
		if (type[i] == t && sym[i] == s) ++c;
	}
	return c;
}
Corners::Corners(const CubeDecoration &cube) {
	setCorners(orig, cube, false);
}

void
Corners::setCorners(Corner o[8], const CubeDecoration &c, bool answer) const {
	short ct0, ct1, ct2, ct3, ct4, ct5;
	Symmetry cs0, cs1, cs2, cs3, cs4, cs5;
	Rotation r0, r1, r2, r3, r4, r5;
	// copy the types and syms into the corner object
	// only copy what we can know from the above projection.
	// -1 means unknown
 	//   0: 012   1: 024   2: 043   3: 031
	//   4: 215   5: 425   6: 345   7: 135
	ct0 = c.type[0];   ct1 = c.type[1];   ct2 = c.type[2];
	cs0 = c.sym[0];    cs1 = c.sym[1];    cs2 = c.sym[2];
	r0 = c.siderot[0]; r1 = c.siderot[1]; r2 = c.siderot[2];
	if (answer) {
		ct3 = c.type[3];   ct4 = c.type[4];   ct5 = c.type[5];
		cs3 = c.sym[3];    cs4 = c.sym[4];    cs5 = c.sym[5];
		r3 = c.siderot[3]; r4 = c.siderot[4]; r5 = c.siderot[5];
	} else {
		ct3 = ct4 = ct5 = -1;
		cs3 = cs4 = cs5 = none;
		r3 = r4 = r5 = DEG0;
	}

	o[0].type[0] = ct0; o[0].type[1] = ct1; o[0].type[2] = ct2;
	o[0].sym[0]  = cs0; o[0].sym[1]  = cs1; o[0].sym[2]  = cs2;
	o[0].cor[0]  =1-r0; o[0].cor[1]  =0-r1; o[0].cor[2]  =2-r2;
	o[1].type[0] = ct0; o[1].type[1] = ct2; o[1].type[2] = ct4;
	o[1].sym[0]  = cs0; o[1].sym[1]  = cs2; o[1].sym[2]  = cs4;
	o[1].cor[0]  =0-r0; o[1].cor[1]  =3-r2; o[1].cor[2]  =1-r4;
	o[2].type[0] = ct0; o[2].type[1] = ct4; o[2].type[2] = ct3;
	o[2].sym[0]  = cs0; o[2].sym[1]  = cs4; o[2].sym[2]  = cs3;
	o[2].cor[0]  =3-r0; o[2].cor[1]  =2-r4; o[2].cor[2]  =0-r3;
	o[3].type[0] = ct0; o[3].type[1] = ct3; o[3].type[2] = ct1;
	o[3].sym[0]  = cs0; o[3].sym[1]  = cs3; o[3].sym[2]  = cs1;
	o[3].cor[0]  =2-r0; o[3].cor[1]  =1-r3; o[3].cor[2]  =3-r1;
	o[4].type[0] = ct1; o[4].type[1] = ct5; o[4].type[2] = ct2;
	o[4].sym[0]  = cs1; o[4].sym[1]  = cs5; o[4].sym[2]  = cs2;
	o[4].cor[0]  =1-r1; o[4].cor[1]  =0-r5; o[4].cor[2]  =1-r2;
	o[5].type[0] = ct2; o[5].type[1] = ct5; o[5].type[2] = ct4;
	o[5].sym[0]  = cs2; o[5].sym[1]  = cs5; o[5].sym[2]  = cs4;
	o[5].cor[0]  =0-r2; o[5].cor[1]  =1-r5; o[5].cor[2]  =0-r4;
	o[6].type[0] = ct3; o[6].type[1] = ct4; o[6].type[2] = ct5;
	o[6].sym[0]  = cs3; o[6].sym[1]  = cs4; o[6].sym[2]  = cs5;
	o[6].cor[0]  =3-r3; o[6].cor[1]  =3-r4; o[6].cor[2]  =2-r5;
	o[7].type[0] = ct1; o[7].type[1] = ct3; o[7].type[2] = ct5;
	o[7].sym[0]  = cs1; o[7].sym[1]  = cs3; o[7].sym[2]  = cs5;
	o[7].cor[0]  =2-r1; o[7].cor[1]  =2-r3; o[7].cor[2]  =3-r5;
	for (short i=0; i<8; ++i) {
		o[i].reduceCorner();
	}
}
short
rand(short max) {
	return short(double(max)*rand()/(RAND_MAX+1.0));
}
/* this function checks for each possible orientation of answer c if it can
   be distinguished from the projection of the original cube. From the list
   of distinguishable orientations, one is chosen and returned.
   If no orientation is distinguishable, -1 is returned. */
short
Corners::chooseOrientation(const CubeDecoration &c, const Question &q,
		Corner &visc) const {
	bool orient[8];
	Corner a[8];
	setCorners(a, c, true);
//	for (short i=0; i<6; ++i) {
//		cerr << c.type[i]<<","<<c.sym[i]<<","<<c.siderot[i]<<" ";
//	}
	// check if a certain orientation of the answer can be distinguished
	// as false;
	short count=0;
	for (short i=0; i<8; ++i) {
		orient[i] = !checkCorner(a[i], q);
		if (orient[i]) {
			++count;
			if (q.cube == c) {
				cerr << " ERROR!!!" << endl;
			}
		} else {
		}
	}
	if (count == 0) {
		return -1;
	}
	short sub = rand(3);
	short main = rand(count);
	for (short i=0; i<8; ++i) {
		if (!orient[i]) {
			main++;
		} else if (main == i) {
			visc = a[main];
			return short(3*main+sub);
		}
	}
	// should never reach this point
	cerr << "should never reach this point!!" << endl;
	return -1;
}
/* return true when the cubes cannot be distinguised */
bool
Corners::checkCorner(const Corner &c, const Question &q) const {
	bool equal = false;
	short t1, t2;
	Symmetry s1, s2;
	// is the completely know corner equal?
	if (c == orig[0]) {
		equal = true;
	}
	// is a 2/3 known corner equal?
	if (!equal && c == orig[1]) {
		t1 = q.cube.type[1];
		s1 = q.cube.sym[1];
		equal = q.maximalAmountOf(t1, s1) >= 1+c.count(t1, s1);
	}
	if (!equal && c == orig[3]) {
		t1 = q.cube.type[2];
		s1 = q.cube.sym[2];
		equal = q.maximalAmountOf(t1, s1) >= 1+c.count(t1, s1);
	}
	if (!equal && c == orig[4]) {
		t1 = q.cube.type[0];
		s1 = q.cube.sym[0];
		equal = q.maximalAmountOf(t1, s1) >= 1+c.count(t1, s1);
	}
	// is a 1/3 known corner equal?
	if (!equal && c == orig[2]) {
		t1 = q.cube.type[1];
		s1 = q.cube.sym[1];
		t2 = q.cube.type[2];
		s2 = q.cube.sym[2];
		if (t1 == t2 && s1 == s2) {
			equal = q.maximalAmountOf(t1, s1) >= 2+c.count(t1, s1);
		} else {
			equal = q.maximalAmountOf(t1, s1) >= 1+c.count(t1, s1)
			&& q.maximalAmountOf(t2, s2) >= 1+c.count(t2, s2);
		}
	}
	if (!equal && c == orig[5]) {
		t1 = q.cube.type[0];
		s1 = q.cube.sym[0];
		t2 = q.cube.type[1];
		s2 = q.cube.sym[1];
		if (t1 == t2 && s1 == s2) {
			equal = q.maximalAmountOf(t1, s1) >= 2+c.count(t1, s1);
		} else {
			equal = q.maximalAmountOf(t1, s1) >= 1+c.count(t1, s1)
			&& q.maximalAmountOf(t2, s2) >= 1+c.count(t2, s2);
		}
	}
	if (!equal && c == orig[7]) {
		t1 = q.cube.type[0];
		s1 = q.cube.sym[0];
		t2 = q.cube.type[2];
		s2 = q.cube.sym[2];
		if (t1 == t2 && s1 == s2) {
			equal = q.maximalAmountOf(t1, s1) >= 2+c.count(t1, s1);
		} else {
			equal = q.maximalAmountOf(t1, s1) >= 1+c.count(t1, s1)
			&& q.maximalAmountOf(t2, s2) >= 1+c.count(t2, s2);
		}
	}
	// compare to the completely unknown region
	// if a corner does not not fit any partially known corner, but
	// does contain side types that only appear in the known region,
	// then it is recognizably wrong
	if (!equal) {
		equal = true;
		for (short i=0; i<3; ++i) {
			t1 = c.type[i];
			s1 = c.sym[i];
			if (q.maximalAmountOf(t1, s1)
				< c.count(t1, s1) + orig[0].count(t1,s1)) {
				equal = false;
			}
		}
	}
	return equal;
}
