/*  This file is part of lsnet. 
    Copyright (C) 2009-2010  Sterling Pickens
    Lsnet 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 3 of the License, or
    (at your option) any later version.

    Lsnet is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with lsnet.  If not, see <http://www.gnu.org/licenses/>.
*/


#include "cpuinfo.h"

struct cpu_data cpu;

void Cpu_allocate(){
	unsigned int count_cores = 0;
	unsigned int count_polls = 0;
	unsigned int count_entries = 0;

	//allocate global space
	cpu.poll_one = malloc(sizeof(double **)*2);
	for(count_entries=0; count_entries < 2; count_entries++){
		cpu.poll_one[count_entries] = malloc(sizeof(double *)*cpu.cores);
		for(count_cores=0; count_cores<cpu.cores; count_cores++){
			cpu.poll_one[count_entries][count_cores] = malloc(sizeof(double)*7);
		}
	}
	cpu.poll_all = malloc(sizeof(double **)*cpu.polls);
	for(count_polls=0; count_polls<cpu.polls; count_polls++){
		cpu.poll_all[count_polls] = malloc(sizeof(double *)*cpu.cores);
		for(count_cores=0; count_cores<cpu.cores; count_cores++){
			cpu.poll_all[count_polls][count_cores] = malloc(sizeof(double)*8);
		}
	}
	cpu.min_one = malloc(sizeof(double *)*cpu.cores);
	for(count_cores=0; count_cores<cpu.cores; count_cores++){
		cpu.min_one[count_cores] = malloc(sizeof(double)*7);
	}
	//printf("cores: %u polls: %u\n", cpu.cores, cpu.polls);

}

void Cpu_ten_min(){

	time_t t = time(NULL);
	struct tm *local = localtime(&t);
	int ret_val = 0;
	//char *one_str; //date + space + processes int(1) + space/plus + cores*( spaces(6) + percentages(7) ) + newline + null
	char *tmp_str;
	char *ten_str; // one_str's (10) - 9 extra nulls
	FILE* fp;
	unsigned int count_minutes = 0;
	unsigned int count_cores = 0;	
	unsigned int count_entries = 0;


	//Wed Jul 28 08:57:30 2010 136 1.766667 97.900000 0.316667 0.000000 0.000000 0.016667 0.000000+0.750250 99.149717 0.083361 0.000000 0.000000 0.016672 0.000000

	unsigned int one_str_size = strlen(asctime(local)) - 1 + 1 + 10 + cpu.cores*(6 + 10*7) + 1 + 1;

	unsigned int ten_str_size = one_str_size*10 - 9;

	//one_str = malloc( one_str_size );

	ten_str = malloc( ten_str_size );

	tmp_str = malloc( one_str_size );


	while (1 == 1){
		memset ((void*) ten_str, '\0', ten_str_size);

		for(count_minutes = 0; count_minutes < 10; count_minutes++){
			//memset ((void*) one_str, '\0', one_str_size);
			Cpu_one_min();
			t = time(NULL);
			local = localtime(&t);

			strcpy(tmp_str, asctime(local)); //appends newline and null
			tmp_str[strlen(tmp_str)-1]='\0'; //newline gone
			strcat(ten_str, (const char *)tmp_str);
			if( sprintf(tmp_str, " %d", processes()) < 1 ){
				//failure
				printf("Error: sprintf failed!\n");
				exit(1);
			}
			strcat(ten_str, (const char *)tmp_str);

			for (count_cores = 0; count_cores < cpu.cores; count_cores++){
				if(count_cores == 0){
					strcat(ten_str, " ");
				}else{
					strcat(ten_str, "+");
				}
				for (count_entries = 0; count_entries < 7; count_entries++){
					if(count_entries != 0){
						if(sprintf( tmp_str, " %lf", cpu.min_one[count_cores][count_entries]) < 1){
							//failure
							printf("Error: sprintf failed!\n");
							exit(1);
						}
						
					}else{
						if(sprintf( tmp_str, "%lf", cpu.min_one[count_cores][count_entries]) < 1){
							//failure
							printf("Error: sprintf failed!\n");
							exit(1);
						}

					}
					strcat(ten_str, (const char *)tmp_str);
				}
			}
			//if(count_minutes != 9)
			strcat(ten_str, "\n");
			//strcat(ten_str, (const char *)one_str);
		}
		if (fp = fopen(global_strs.cpu_log, "a")){
			fprintf(fp, "%s", ten_str);
		}
		fclose(fp);
	}
}

void Cpu_one_min(){
	//int user, nice, system, idle, iowait, irq, softirq, totalcycles;
	//int count1, count2, count3;
	int c_count, p_count, e_count;
	//const int poll = 60 / cpufrequency;
	double *walltime = malloc(sizeof(double)*cpu.polls);
	double walltimetotal = 0;
	double te0;
	//memset( (void *) cpu.poll_all, 0, sizeof(double) * cpu.polls * 8 * cpu.cores);
	walltimetotal = 0;
	for (p_count = 0; p_count < cpu.polls; p_count++){
		te0 = Get_cpuinfo();
		for (c_count = 0; c_count < cpu.cores; c_count++){
			        //poll cpu variable      //run two minus run one, cycles for those variables
			cpu.poll_all[p_count][c_count][0] = (cpu.poll_one[1][c_count][0] - cpu.poll_one[0][c_count][0]);
			cpu.poll_all[p_count][c_count][1] = (cpu.poll_one[1][c_count][1] - cpu.poll_one[0][c_count][1]);
			cpu.poll_all[p_count][c_count][2] = (cpu.poll_one[1][c_count][2] - cpu.poll_one[0][c_count][2]);
			cpu.poll_all[p_count][c_count][3] = (cpu.poll_one[1][c_count][3] - cpu.poll_one[0][c_count][3]);
			cpu.poll_all[p_count][c_count][4] = (cpu.poll_one[1][c_count][4] - cpu.poll_one[0][c_count][4]);
			cpu.poll_all[p_count][c_count][5] = (cpu.poll_one[1][c_count][5] - cpu.poll_one[0][c_count][5]);
			cpu.poll_all[p_count][c_count][6] = (cpu.poll_one[1][c_count][6] - cpu.poll_one[0][c_count][6]);
			cpu.poll_all[p_count][c_count][7] = 0;

			for (e_count = 0; e_count < 7; e_count++){
				cpu.poll_all[p_count][c_count][7] += cpu.poll_all[p_count][c_count][e_count];
			}
			//translate cycles into percentage use
			cpu.poll_all[p_count][c_count][0] /= cpu.poll_all[p_count][c_count][7];
			cpu.poll_all[p_count][c_count][0] *= 100;
			cpu.poll_all[p_count][c_count][1] /= cpu.poll_all[p_count][c_count][7];
			cpu.poll_all[p_count][c_count][1] *= 100;
			cpu.poll_all[p_count][c_count][2] /= cpu.poll_all[p_count][c_count][7];
			cpu.poll_all[p_count][c_count][2] *= 100;
			cpu.poll_all[p_count][c_count][3] /= cpu.poll_all[p_count][c_count][7];
			cpu.poll_all[p_count][c_count][3] *= 100;
			cpu.poll_all[p_count][c_count][4] /= cpu.poll_all[p_count][c_count][7];
			cpu.poll_all[p_count][c_count][4] *= 100;
			cpu.poll_all[p_count][c_count][5] /= cpu.poll_all[p_count][c_count][7];
			cpu.poll_all[p_count][c_count][5] *= 100;
			cpu.poll_all[p_count][c_count][6] /= cpu.poll_all[p_count][c_count][7];
			cpu.poll_all[p_count][c_count][6] *= 100;
		}
		//the runtime between reads for that polling
		walltime[p_count] = te0;
		//that total
		walltimetotal += te0;
	}
	
	for (c_count = 0; c_count < cpu.cores; c_count++){
		for (e_count = 0; e_count < 7; e_count++){
			cpu.min_one[c_count][e_count] = 0;
		}
	}

	for (p_count = 0; p_count < cpu.polls; p_count++){
		for (c_count = 0; c_count < cpu.cores; c_count++){
			for (e_count = 0; e_count < 7; e_count++){
				cpu.min_one[c_count][e_count] += cpu.poll_all[p_count][c_count][e_count] * (walltime[p_count] / walltimetotal);
				//Replace NaN values with 0 if they are detected (rare, but can happen)
				if(cpu.min_one[c_count][e_count] != cpu.min_one[c_count][e_count]){
					cpu.min_one[c_count][e_count] = 0;
				}
			}
		}
	}
}

double Get_cpuinfo(){
        FILE* fp;
	//double Ret_Time = 0;
	//memset( (void *) cpu.poll_one, '\0', sizeof(double) * 2 * cpu.cores * 8);

	struct timeval starttime,endtime;
        int count;

	//cpu0 44700259 64117230 211381 3600086 4328 9204 11988 0 0 0	
	//maximum # of bytes for relevant lines in /proc/stat : 3+log(cores) + log(Hz)*10 + 10 spaces + newline + null

	//const unsigned int buf_size = 3 + ceil(log10(cpu.cores)) + ceil(log10(cpu.mhz * 1000 * 1000))*10 + 10 + 2;  
	
	char buf[(size_t)ceil(3 + log10(cpu.cores) + log10(cpu.mhz * 1000 * 1000)*10 + 10 + 2)];
	//buf = (char *)malloc(buf_size);
	//char buf[buf_size];

	char *token;
	char *line;
	char *search = " ";

	int linenum;
	linenum = -1;

        if( ( fp = fopen( "/proc/stat", "r" ) ) == NULL ) {
                fprintf( stderr, "Error opening file.\n" );
                exit( 1 );
        }

	gettimeofday(&starttime, NULL);
	//"$user $nice $system $idle $iowait $irq $softirq"
        while(fgets(buf, sizeof(buf), fp) != NULL)
        {
		if (linenum < cpu.cores && linenum != -1)
		{
			//cpu1 51520599 71100564 216077 2823524 10463 9462 9412 0 0 0
			count = 0;
			line = buf;
			token = strtok(line, search);
			token = strtok(NULL, search);
				cpu.poll_one[0][linenum][count] = atof(token);
				count++;	
			token = strtok(NULL, search);
                                cpu.poll_one[0][linenum][count] = atof(token);
				count++;
			token = strtok(NULL, search);
                                cpu.poll_one[0][linenum][count] = atof(token);
                                count++;
			token = strtok(NULL, search);
                                cpu.poll_one[0][linenum][count] = atof(token);
                                count++;
			token = strtok(NULL, search);
                                cpu.poll_one[0][linenum][count] = atof(token);
                                count++;
			token = strtok(NULL, search);
                                cpu.poll_one[0][linenum][count] = atof(token);
                                count++;
			token = strtok(NULL, search);
                                cpu.poll_one[0][linenum][count] = atof(token);
                                //count++;
		}
		linenum++;			
	}
	fclose( fp );
	linenum = -1;

	sleep(cpu.frequency);

        if( ( fp = fopen( "/proc/stat", "r" ) ) == NULL ) {
		fprintf( stderr, "Error opening file.\n" );
                exit( 1 );
        }
	gettimeofday(&endtime, NULL);
        while( fgets(buf, sizeof(buf), fp) != NULL)
        {
                if (linenum < cpu.cores && linenum != -1)
                {
			count = 0;
                        line = buf;
                        token = strtok(line, search);
                        token = strtok(NULL, search);
                        cpu.poll_one[1][linenum][count] = atof(token);
                        count++;
                        token = strtok(NULL, search);
                        cpu.poll_one[1][linenum][count] = atof(token);
                        count++;
                        token = strtok(NULL, search);
                        cpu.poll_one[1][linenum][count] = atof(token);
                        count++;
                        token = strtok(NULL, search);
                        cpu.poll_one[1][linenum][count] = atof(token);
                        count++;
                        token = strtok(NULL, search);                        
			cpu.poll_one[1][linenum][count] = atof(token);
                        count++;
                        token = strtok(NULL, search);
                        cpu.poll_one[1][linenum][count] = atof(token);
                        count++;			
                        token = strtok(NULL, search);
                        cpu.poll_one[1][linenum][count] = atof(token);
                        //count++;
                }
                linenum++;
        }
        fclose( fp );
	return( T_DIFF(starttime, endtime) );
	//return(  ((double)(endtime.tv_sec*1000000-starttime.tv_sec*1000000+endtime.tv_usec-starttime.tv_usec))/1000000 );
}
