/*! \brief Reads kernel functions from a string into this Program.
  *
  * The string object can contain multiple OpenCL kernel
  * functions. It deletes all comments and if the kernels
  * are templated, it substitutes the template parameter
  * with the provided types. For each Kernel function
  * a Kernel object is built and stored within a map.
  * The map stores the name of the kernel function and
  * the corresponding function.
  * Note that DEFINES are not supported yet.
*/
ocl::Program& ocl::Program::operator << (const std::string &k)
{
    std::string kernels = k;
    eraseComments(kernels);

    //DEBUG_COMMENT(kernels);

    size_t pos = 0;
    while(pos < kernels.npos){
        const std::string& next = nextKernel(kernels, pos);
        if(next.empty()) return *this;
        pos += next.length();

        if(_types.empty() || !ocl::Kernel::templated(next)){
            ocl::Kernel *kernel = new ocl::Kernel(*this, next);
            if(this->isBuilt()) kernel->create();
            //DEBUG_COMMENT("Creating kernel " << kernel->name() << std::endl << kernel->toString() );
            _kernels[kernel->name()] = kernel;
            continue;
        }

        for(utl::Types::const_iterator it = _types.begin(); it != _types.end(); ++it)
        {
            const utl::Type &type = **it;
            ocl::Kernel *kernel = new ocl::Kernel(*this, next, type);
            if(this->isBuilt()) kernel->create();
            //DEBUG_COMMENT("Creating kernel " << kernel->name() << std::endl << kernel->toString() );
            _kernels[kernel->name()] = kernel;
        }
    }
    return *this;
}
/*! \brief Reads kernel functions from a string into this Program.
  *
  * The string object can contain multiple OpenCL kernel
  * functions. It deletes all comments and if the kernels
  * are templated, it substitutes the template parameter
  * with the provided types. For each Kernel function
  * a Kernel object is built and stored within a map.
  * The map stores the name of the kernel function and
  * the corresponding function.
  * Note that DEFINES are not supported yet.
*/
ocl::Program& ocl::Program::operator << (const std::string &k)
{
	//   std::cout << "kernels: " << _kernels.size() << " blocks: " << commonCodeBlocks_.size() << std::endl;
	//   if ( _kernels.empty() )
	//   {
	//     commonCodeBlocks_.resize( 0 );
	//   }

	std::string kernels = k;

	//#if 0 // For now disable comment removal as we need it to play a trick on the AMD compiler.
	eraseComments(kernels);
	//#endif

	size_t pos = 0;
	while(pos < kernels.npos){
		const std::string next = nextKernel(kernels, pos);

		if(next.empty()) {
			commonCodeBlocks_.push_back( kernels.substr( pos ) );
			break;
		}
		pos += next.length() + commonCodeBlocks_.back().length();


//		std::cout << "COMMON CODE: " << commonCodeBlocks_.back();
//		std::cout << "KERNEL CODE: " << next << std::endl;

		if(_types.empty() || !ocl::Kernel::templated(next)){
			std::unique_ptr< ocl::Kernel > kernel( new ocl::Kernel(*this, next) );
			if(this->isBuilt()) kernel->create();

			auto t = std::find_if( _kernels.begin(), _kernels.end(), [&kernel]( std::unique_ptr< Kernel > const& k ){ return kernel->name() == k->name(); } );

			if ( t != _kernels.end() ) {
				t->swap( kernel );
			} else {
				_kernels.push_back( std::move( kernel ) );
			}

			continue;
		}


		for(utl::Types::const_iterator it = _types.begin(); it != _types.end(); ++it)
		{
			const utl::Type& type = **it;
			std::unique_ptr< ocl::Kernel > kernel( new ocl::Kernel(*this, next, type) );
			if(this->isBuilt()) {
				kernel->create();
			}
			Kernels::iterator t = std::find_if( _kernels.begin(), _kernels.end(), [&kernel]( std::unique_ptr< Kernel > const& k ){ return kernel->name() == k->name(); 		} );

			if ( t != _kernels.end() ) {
				t->swap( kernel );
			} else {
				_kernels.push_back( std::move( kernel ) );
			}

			// "Invent" common code blocks for kernels being generated by the type system.
			if ( /*std::distance(it, _types.begin()) > 1*/ it != _types.begin() ) {
				commonCodeBlocks_.push_back("\n");
			}
		}
	}


	commonCodeBlocks_.push_back( "" );

	//	std::cout << "Printing Program in StreamOperator:" << std::endl;
	//	this->print();
	//	std::cout << "-----------------------------------" << std::endl;


	checkConstraints();

	return *this;
}