/*
 * RandomRegExpFactory.cpp
 *
 *  Created on: 27. 3. 2014
 *	  Author: Jan Travnicek
 */

#include "RandomRegExpFactory.h"
#include <regexp/unbounded/UnboundedRegExpElements.h>
#include <exception/CommonException.h>

#include <alib/algorithm>
#include <alib/random>

#include <registration/AlgoRegistration.hpp>

namespace regexp {

namespace generate {

regexp::UnboundedRegExp < > RandomRegExpFactory::generateUnboundedRegExp( size_t leafNodes, size_t height, size_t alphabetSize, bool randomizedAlphabet ) {
	if(alphabetSize > 26)
		throw exception::CommonException("Too big alphabet.");

	if( alphabetSize <= 0 )
		throw exception::CommonException( "Alphabet size must be greater than 0." );

	ext::vector<DefaultSymbolType> symbols;
	for(int i = 0; i < 26; i++) symbols.push_back(DefaultSymbolType((char)(i + 'a')));
	if(randomizedAlphabet) shuffle(symbols.begin(), symbols.end(), ext::random_devices::semirandom);
	ext::set<DefaultSymbolType> alphabet(symbols.begin(), symbols.begin() + alphabetSize);

	return RandomRegExpFactory::generateUnboundedRegExp( leafNodes, height, alphabet );
}

auto GenerateUnboundedRegExp1 = registration::AbstractRegister < RandomRegExpFactory, regexp::UnboundedRegExp < >, size_t, size_t, size_t, bool > ( RandomRegExpFactory::generateUnboundedRegExp, abstraction::AlgorithmCategories::AlgorithmCategory::DEFAULT, "leafNodes", "height", "alphabetSize", "randomizedAlphabet" );

regexp::UnboundedRegExp < > RandomRegExpFactory::generateUnboundedRegExp( size_t leafNodes, size_t height, ext::set<DefaultSymbolType> alphabet) {

	if( alphabet.size() > 26)
		throw exception::CommonException("Too big alphabet.");

	if( alphabet.size() <= 0 )
		throw exception::CommonException( "Alphabet size must be greater than 0." );

	ext::ptr_vector < regexp::UnboundedRegExpElement < DefaultSymbolType > > elems;

	{
		elems.push_back ( regexp::UnboundedRegExpEmpty < DefaultSymbolType > ( ) );
		elems.push_back ( regexp::UnboundedRegExpEpsilon < DefaultSymbolType > ( ) );
	}
	if(alphabet.size() > 6) {
		elems.push_back ( regexp::UnboundedRegExpEmpty < DefaultSymbolType > ( ) );
		elems.push_back ( regexp::UnboundedRegExpEpsilon < DefaultSymbolType > ( ) );
	}
	if(alphabet.size() > 16) {
		elems.push_back ( regexp::UnboundedRegExpEmpty < DefaultSymbolType > ( ) );
		elems.push_back ( regexp::UnboundedRegExpEpsilon < DefaultSymbolType > ( ) );
	}

	for ( const DefaultSymbolType & symbol : alphabet) {
		elems.push_back ( regexp::UnboundedRegExpSymbol < DefaultSymbolType > ( symbol ) );
	}

	regexp::UnboundedRegExp < > res = RandomRegExpFactory::SimpleUnboundedRegExp( leafNodes, height, elems );

	return res;
}

auto GenerateUnboundedRegExp2 = registration::AbstractRegister < RandomRegExpFactory, regexp::UnboundedRegExp < >, size_t, size_t, ext::set < DefaultSymbolType > > ( RandomRegExpFactory::generateUnboundedRegExp, abstraction::AlgorithmCategories::AlgorithmCategory::DEFAULT, "leafNodes", "height", "alphabet" );

regexp::UnboundedRegExp < > RandomRegExpFactory::SimpleUnboundedRegExp( size_t n, size_t h, const ext::ptr_vector < regexp::UnboundedRegExpElement < DefaultSymbolType > > & elems) {
	return regexp::UnboundedRegExp < > (regexp::UnboundedRegExpStructure < DefaultSymbolType > ( SimpleUnboundedRegExpElement (n, h, elems)));
}

ext::ptr_value < regexp::UnboundedRegExpElement < DefaultSymbolType > > RandomRegExpFactory::SimpleUnboundedRegExpElement (size_t n, size_t h, const ext::ptr_vector < regexp::UnboundedRegExpElement < DefaultSymbolType > > & elems) {
	if(h == 0 || n == 0) {
		return ext::ptr_value < UnboundedRegExpElement < DefaultSymbolType > > ( elems [ ext::random_devices::semirandom ( ) % elems.size ( ) ] );
	} else {
		unsigned childNodes = ext::random_devices::semirandom() % 10;
		if(childNodes <  4) childNodes = 1;
		else if(childNodes <  6) childNodes = 2;
		else if(childNodes <  8) childNodes = 3;
		else childNodes = 4;

		childNodes = childNodes > n ? n : childNodes;

		int subSizes[4];
		if(childNodes == 4) {
			subSizes[3] = ext::random_devices::semirandom() % ( n - 1 );
			subSizes[2] = ext::random_devices::semirandom() % ( n - subSizes[3] - 1 );
			subSizes[1] = ext::random_devices::semirandom() % ( n - subSizes[2] - subSizes [3] - 1 );

			subSizes[3] += 1;
			subSizes[2] += 1;
			subSizes[1] += 1;

			subSizes[0] = n - subSizes[1] - subSizes[2] - subSizes[3];
		}
		if(childNodes == 3) {
			subSizes[2] = ext::random_devices::semirandom() % ( n - 1);
			subSizes[1] = ext::random_devices::semirandom() % ( n - subSizes[2] - 1);

			subSizes[2] += 1;
			subSizes[1] += 1;

			subSizes[0] = n - subSizes[1] - subSizes[2];
		}
		if(childNodes == 2) {
			subSizes[1] = ext::random_devices::semirandom() % ( n - 1 );

			subSizes[1] += 1;

			subSizes[0] = n - subSizes[1];
		}
		if(childNodes == 1) {
			return ext::ptr_value < regexp::UnboundedRegExpElement < DefaultSymbolType > > ( regexp::UnboundedRegExpIteration < DefaultSymbolType > (SimpleUnboundedRegExpElement ( n, h - 1, elems ) ) );
		}

		int nodeType = ext::random_devices::semirandom() % 2;
		if(nodeType == 0) {
			regexp::UnboundedRegExpConcatenation < DefaultSymbolType > con;
			for ( unsigned i = 0; i < childNodes; i ++ ) {
				con.appendElement ( std::move ( SimpleUnboundedRegExpElement ( subSizes [ i ], h - 1, elems ) ) );
			}
			return ext::ptr_value < regexp::UnboundedRegExpElement < DefaultSymbolType > > ( std::move ( con ) );
		} else {
			regexp::UnboundedRegExpAlternation < DefaultSymbolType > alt;
			for ( unsigned i = 0; i < childNodes; i ++) {
				alt.appendElement ( std::move ( SimpleUnboundedRegExpElement ( subSizes [ i ], h - 1, elems ) ) );
			}
			return ext::ptr_value < regexp::UnboundedRegExpElement < DefaultSymbolType > > ( std::move ( alt ) );
		}

	}
}

} /* namespace generate */

} /* namespace regexp */