/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *
 * This file is part of SanKit -- A multiplatform set of C code
 * for reading and writing PGN formatted chess games. In addition
 * it can be used as an engine to determine in real time if proposed
 * moves are legal or not. It appears to generate near perfect PGN
 * from incorrectly formatted files.
 *
 * Copyright 2012 David Hoerl All Rights Reserved.
 *
 *
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice, this list of
 *       conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above copyright notice, this list
 *       of conditions and the following disclaimer in the documentation and/or other materials
 *       provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY David Hoerl ''AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David Hoerl OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include <iostream>
#include <vector>

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cctype>
#include <cassert>
#include <unistd.h>	// sleep()

#include "CompareFiles.h"
#include "PGN_Parser.h"

using namespace std;
using namespace SANKit;

namespace 
{
	std::string string_format(const char *fmt, ...);
	void append_format(std::string& str, const char *fmt, ...);
}

CompareFilesPGN::CompareFilesPGN(const string& f1str, const string& f2str, const std::string& boards) :
	didCompare(false),
	game_count(0),
	f1(fopen(f1str.c_str(), "r")),
	f2(fopen(f2str.c_str(), "r")),
	moveFP(fopen(boards.c_str(), "r")),
	differences()
{
	if(!f1) { 
		append_format(differences, "Error: file1 (\"%s\") could not be opened.\n", f1str.c_str());
	}

	if(!f2) { 
		append_format(differences, "Error: file2 (\"%s\") could not be opened.\n", f2str.c_str());
	}

	if(!moveFP) { 
		append_format(differences, "Error: moves file (\"%s\") could not be opened.\n", boards.c_str());
	}
}
CompareFilesPGN::~CompareFilesPGN()
{
	if(f1) fclose(f1);
	if(f2) fclose(f2);
	if(moveFP) fclose(moveFP);
}

bool CompareFilesPGN::Compare()
{
	if(!f1 || !f2 || !moveFP) { 
		return false;
	}
	
	if(didCompare) {
		return true;
	}
	didCompare = true;

	uint32_t gameCount = 0;
	//uint32_t playCount = 0;
	
	bool err = false;
	while(true) {
		long pos = ftell(moveFP);
		//fseek(moveFP, pos, SEEK_SET);
		Game g1(f1, moveFP);
		fseek(moveFP, pos, SEEK_SET);
		Game g2(f2, moveFP);
		if(!g1.GetSuccess() && !g2.GetSuccess()) {
			// printf("games=%u playcount=%u\n", gameCount, playCount);
			return gameCount ? true : false;
		}
		if(!g1.GetSuccess() || !g2.GetSuccess()) {
			differences.append("Error: game count mismatch\n");
			return false;
		}
		++gameCount;
		//pos -= half_move;	// so we can see the board just before the move
		size_t num_plays = g1.GetFullMoveCount();
		if(num_plays == g2.GetFullMoveCount()) {
			for(size_t i=0; i<num_plays; ++i) {
				if(g1.GetIsForfeit()) { assert(!"Impossible"); continue; }
				//++playCount;
				string p1 = g1.GetFullMove(i);
				string p2 = g2.GetFullMove(i);
				
				if(p1.compare(p2)) {
					append_format(differences, "Error: game %u play mismatch: \"%s\" \"%s\"\n", gameCount, p1.c_str(), p2.c_str());
					if(!err && i) {
						differences.append("\n");
						g1.AppendBoard(differences, i-1, false);
						differences.append("\n\n");
						g1.AppendBoard(differences, i, true);
						differences.append("\n\n");
						g1.AppendBoard(differences, i, false);
						differences.append("\n\n");
					}
				}
				//pos += full_move;
			}
		} else {
			append_format(differences, "Error: game %u play count mismatch (%lu %lu)\n", gameCount, g1.GetFullMoveCount(), g2.GetFullMoveCount());
			err = true;
		}

		++game_count;
	}
	assert(!"impossible");
}

string CompareFilesPGN::GetResults()
{
	if(!didCompare) {
		Compare();
	}
	return differences;
}



namespace
{

	std::string string_format(const char *fmt, ...)
	{
		char *ret;
		va_list ap;

		va_start(ap, fmt);
		vasprintf(&ret, fmt, ap);
		std::string str(ret);
		free(ret);
		va_end(ap);

		return str;
	}
	void append_format(std::string& str, const char *fmt, ...)
	{
		va_list ap;
		char *ret;

		va_start(ap, fmt);
		vasprintf(&ret, fmt, ap);
		va_end(ap);

		str.append(ret);
		free(ret);
	}

}
