#include <malloc.h>
#include <stdlib.h>
#include <PR__include/PR__WL.h>

#include "Matrix_Mult.h"
#include "Reo__Matrix_Mult/Reo__Matrix_Mult.h"

void set_up_performance_counters();

// =============================================================================

int NUM_ITER;


int main(int argc, char **argv) 
 { Matrix      *leftMatrix, *rightMatrix, *resultMatrix;
   ParamBag    *paramBag;
   MatrixMultWorkUnit **workUnits;
   VPParams    *vpParams, **vpParamsArray;
//   ProdParams  *prodParams;
//   ConsParams  *consParams;
   int32        numUnitsToMake, vpIdx;
   PRProcess   *matrixMultProcess;
   
         DEBUG__printf(TRUE, "arguments --  numIter: %s | path: %s", argv[1], argv[2] );
   if(argc < 3) {printf("give: num iter and path to param file on cmd line\n"); exit(1);}
   NUM_ITER = atoi(argv[1]);
   numUnitsToMake = NUM_PROD;  //defined by Reo circuit generator
   
         printf("[reo] Settings: %i workers, %i iterations | file: %s \n", NUM_PROD, NUM_ITER, argv[2]);

         set_up_performance_counters();
   
   //read parameters, from file whose path is given on command line
   paramBag = makeParamBag();
   readParamFileIntoBag( argv[2], paramBag );
   initialize_Input_Matrices_Via( &leftMatrix, &rightMatrix, paramBag );
   

   PR__start();
   PR__set_app_info( "matrix multiply prod cons in Reo" );
   ParamStruc *inputInfo = getParamFromBag( "inputInfo", paramBag );
   PR__set_input_info( inputInfo->strValue );
	
   resultMatrix = PR__malloc( sizeof(Matrix) );
   resultMatrix->array = PR__malloc( leftMatrix->numRows * rightMatrix->numCols * sizeof(double) );
   resultMatrix->numCols = rightMatrix->numCols;
   resultMatrix->numRows = leftMatrix->numRows;
   workUnits = divideWork( leftMatrix, rightMatrix, resultMatrix, 
                           numUnitsToMake );
   
/* For now, don't need any params inside producer function other than
 * the work unit.  Also, don't need any params at all inside cons function.
 * So, just send the array of work units to the seed Fn, which will pass it
 * along to the create_VP part of circuit creation, which will copy the
 * elements of the array into the param structs given as initData to the
 * VPs, as they are created.
 *Need a dummy work unit added to end of work-unit array, to avoid a seg
 * fault -- just did a bad hack inside divideWork.
   //Now, create params struct for each producer -- holds work unit
   for( vpIdx=0; vpIdx<numUnitsToMake; vpIdx++)
    {
      vpParams = PR__malloc( sizeof(VPParams) );
      prodParams = PR__malloc( sizeof(ProdParams) );
      
      vpParams->initData = prodParams; 
      vpParamsArray[vpIdx] = vpParams;
    }
  //create params for consumer
           
   vpParams = PR__malloc( sizeof(VPParams) );
     = malloc( sizeof(ProdParams) );
      
   vpParams->initData = 
   vpParamsArray[vpIdx] = vpParams; //this last entry is for the consumer
*/
   
   matrixMultProcess = PR__create_process( &matrix_mult__seed_Fn, 
                                                workUnits );
   
   PR__wait_for_process_to_end(matrixMultProcess);
   PR__wait_for_all_activity_to_end();
   fflush(stdout);
   PR__shutdown();
   
   exit(0);
 }

// =============================================================================

/*Initializes the performance counters, and opens the file descriptors used
 * to read from the performance counters
 */
void set_up_performance_counters() {
	int i;

#ifdef MEASURE_PERF
	//setup performance counters
	struct perf_event_attr hw_event;
	memset(&hw_event,0,sizeof(hw_event));
	hw_event.type = PERF_TYPE_HARDWARE;
	hw_event.size = sizeof(hw_event);
	hw_event.disabled = 0;
	hw_event.freq = 0;
	hw_event.inherit = 1; /* children inherit it   */
	hw_event.pinned = 1; /* must always be on PMU */
	hw_event.exclusive = 0; /* only group on PMU     */
	hw_event.exclude_user = 0; /* don't count user      */
	hw_event.exclude_kernel = 1; /* ditto kernel          */
	hw_event.exclude_hv = 1; /* ditto hypervisor      */
	hw_event.exclude_idle = 1; /* don't count when idle */
	hw_event.mmap = 0; /* include mmap data     */
	hw_event.comm = 0; /* include comm data     */

	for( i = 0; i < NUM_CORES; i++ )
	{
		hw_event.config = PERF_COUNT_HW_CPU_CYCLES; //cycles
		cycles_counter_fd[i] = syscall(__NR_perf_event_open, &hw_event,
				0,//pid_t pid,
				i,//int cpu,
				-1,//int group_fd,
				0//unsigned long flags
		);
		if (cycles_counter_fd[i]<0) {
			fprintf(stderr,"On core %d: ",i);
			perror("Failed to open cycles counter");
		}
	}

	int cycles_counter_main_fd;
	hw_event.config = PERF_COUNT_HW_CPU_CYCLES; //cycles
	hw_event.exclude_kernel=0;
	cycles_counter_main_fd = syscall(__NR_perf_event_open, &hw_event,
			0,//pid_t pid,
			-1,//int cpu,
			-1,//int group_fd,
			0//unsigned long flags
	);
	if (cycles_counter_main_fd<0) {
		perror("Failed to open main cycles counter");
	}

#endif
}

