#include "SystemInformation.h"
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <cassert>


//Last modified: 18/11/12 19:13:35(CET) by Fabian Holler
#include <stdlib.h> 
#include <sys/types.h>
#include <stdio.h>
#include <string.h>

struct pstat {
  long unsigned int utime_ticks;
  long int cutime_ticks;
  long unsigned int stime_ticks;
  long int cstime_ticks;
  long unsigned int vsize; // virtual memory size in bytes
  long unsigned int rss; //Resident  Set  Size in bytes
  long unsigned int cpu_total_time;
};

/*
 * read /proc data into the passed struct pstat
 * returns 0 on success, -1 on error
 */
int get_usage(const pid_t pid, struct pstat* result) {
  //convert  pid to string
  char pid_s[20];
  snprintf(pid_s, sizeof(pid_s), "%d", pid);
  char stat_filepath[30] = "/proc/"; strncat(stat_filepath, pid_s,
      sizeof(stat_filepath) - strlen(stat_filepath) -1);
  strncat(stat_filepath, "/stat", sizeof(stat_filepath) -
      strlen(stat_filepath) -1);

  FILE *fpstat = fopen(stat_filepath, "r");
  if (fpstat == NULL) {
    perror("FOPEN ERROR ");
    return -1;
  }

  FILE *fstat = fopen("/proc/stat", "r");
  if (fstat == NULL) {
    perror("FOPEN ERROR ");
    fclose(fstat);
    return -1;
  }

  //read values from /proc/pid/stat
  bzero(result, sizeof(struct pstat));
  long int rss;
  if (fscanf(fpstat, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu "
        "%lu %ld %ld %*d %*d %*d %*d %*u %lu %ld",
        &result->utime_ticks, &result->stime_ticks,
        &result->cutime_ticks, &result->cstime_ticks, &result->vsize,
        &rss) == EOF) {
    fclose(fpstat);
    return -1;
  }
  fclose(fpstat);
  result->rss = rss * getpagesize();

  //read+calc cpu total time from /proc/stat
  long unsigned int cpu_time[10];
  bzero(cpu_time, sizeof(cpu_time));
  if (fscanf(fstat, "%*s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
        &cpu_time[0], &cpu_time[1], &cpu_time[2], &cpu_time[3],
        &cpu_time[4], &cpu_time[5], &cpu_time[6], &cpu_time[7],
        &cpu_time[8], &cpu_time[9]) == EOF) {
    fclose(fstat);
    return -1;
  }

  fclose(fstat);

  for(int i=0; i < 10;i++)
    result->cpu_total_time += cpu_time[i];

  return 0;
}

/*
 * calculates the elapsed CPU usage between 2 measuring points. in percent
 */
void calc_cpu_usage_pct(const struct pstat* cur_usage,
    const struct pstat* last_usage,
    double* ucpu_usage, double* scpu_usage)
{
  const long unsigned int total_time_diff = cur_usage->cpu_total_time -
    last_usage->cpu_total_time;

  *ucpu_usage = 100 * (((cur_usage->utime_ticks + cur_usage->cutime_ticks)
        - (last_usage->utime_ticks + last_usage->cutime_ticks))
      / (double) total_time_diff);

  *scpu_usage = 100 * ((((cur_usage->stime_ticks + cur_usage->cstime_ticks)
          - (last_usage->stime_ticks + last_usage->cstime_ticks))) /
      (double) total_time_diff);
}




SystemInformation::SystemInformation()
{
    m_lastFrameTimePoint = GameTime::now();
}


SystemInformation::~SystemInformation()
{
}


//
// http://stackoverflow.com/questions/8223348/ios-get-cpu-usage-from-application
//
struct pstat cpuinfo;
float SystemInformation::getCpuUsage()
{
   get_usage(getpid(), &cpuinfo);
   static struct pstat last_cpuinfo = cpuinfo;
   double userTime, sysTime;
   calc_cpu_usage_pct(&cpuinfo, &last_cpuinfo, &userTime, &sysTime);
   last_cpuinfo = cpuinfo;
/*
  struct rusage usage;
  struct rlimit limit;
  int r1 = getrusage(RUSAGE_SELF, &usage);
  int r2 = getrlimit(RLIMIT_CPU,  &limit);
  float perc = (r1 * 100.0) / r2;
  return tot_cpu;
*/
   return userTime;
}


//
// http://stackoverflow.com/questions/7989864/watching-memory-usage-in-ios
//
size_t SystemInformation::getMemoryUsage()
{
  //return cpuinfo.vsize;
  return cpuinfo.rss;
  /*
    struct task_basic_info info;
    mach_msg_type_number_t size = sizeof(info);
    kern_return_t kr = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size);
    assert(kr == KERN_SUCCESS);
    return info.resident_size; // size in bytes
    */
}


float SystemInformation::getFrameTime()
{
    return m_lastFrameDuration.count() * 0.000000001f; // nanosecs to seconds as a float
}


float SystemInformation::getFps()
{
    return m_fps;
}


float SystemInformation::getAverageFps()
{
    return m_averageFps;
}


float SystemInformation::getDrawTime()
{
    return m_postDrawFrameDuration.count() * 0.000000001f; // nanosecs to seconds as a float
}


void SystemInformation::preDraw()
{
    m_preDrawTimePoint = GameTime::now();
}


//
// fps calculations
// http://stackoverflow.com/questions/1738315/calculate-fps-frames-per-second-for-iphone-app
//
void SystemInformation::frameDrawn()
{
    GameTime::TimePoint thisFrameTimePoint = GameTime::now();
    m_lastFrameDuration = thisFrameTimePoint - m_lastFrameTimePoint;
    m_postDrawFrameDuration = thisFrameTimePoint - m_preDrawTimePoint;
    m_lastFrameTimePoint = thisFrameTimePoint;

    uint64_t nanoSecs = m_lastFrameDuration.count();
    m_fps = (nanoSecs == 0) ? 0 : 1000000000 / float(nanoSecs);
    m_averageFpsCounter++;
    m_averageFpsSum += m_fps;
    if (m_averageFpsCounter >= 15) // calculate average FPS over 15 frames
    {
        m_averageFps = m_averageFpsSum / m_averageFpsCounter;
        m_averageFpsCounter = 0;
        m_averageFpsSum = 0;
    }
}


