/*
 *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "cpu_linux.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

namespace webrtc {
CpuLinux::CpuLinux()
    : m_oldBusyTime(0),
      m_oldIdleTime(0),
      m_oldBusyTimeMulti(NULL),
      m_oldIdleTimeMulti(NULL),
      m_idleArray(NULL),
      m_busyArray(NULL),
      m_resultArray(NULL),
      m_numCores(0) {
    const int result = GetNumCores();
    if (result != -1) {
      m_numCores = result;
      m_oldBusyTimeMulti = new long long[m_numCores];
      memset(m_oldBusyTimeMulti, 0, sizeof(long long) * m_numCores);
      m_oldIdleTimeMulti = new long long[m_numCores];
      memset(m_oldIdleTimeMulti, 0, sizeof(long long) * m_numCores);
      m_idleArray = new long long[m_numCores];
      memset(m_idleArray, 0, sizeof(long long) * m_numCores);
      m_busyArray = new long long[m_numCores];
      memset(m_busyArray, 0, sizeof(long long) * m_numCores);
      m_resultArray = new WebRtc_UWord32[m_numCores];

      GetData(m_oldBusyTime, m_oldIdleTime, m_busyArray, m_idleArray);
    }
}

CpuLinux::~CpuLinux()
{
    delete [] m_oldBusyTimeMulti;
    delete [] m_oldIdleTimeMulti;
    delete [] m_idleArray;
    delete [] m_busyArray;
    delete [] m_resultArray;
}

WebRtc_Word32 CpuLinux::CpuUsage()
{
    WebRtc_UWord32 dummy = 0;
    WebRtc_UWord32* dummyArray = NULL;
    return CpuUsageMultiCore(dummy, dummyArray);
}

WebRtc_Word32 CpuLinux::CpuUsageMultiCore(WebRtc_UWord32& numCores,
                                          WebRtc_UWord32*& coreArray)
{
    coreArray = m_resultArray;
    numCores = m_numCores;
    long long busy = 0;
    long long idle = 0;
    if (GetData(busy, idle, m_busyArray, m_idleArray) != 0)
        return -1;

    long long deltaBusy = busy - m_oldBusyTime;
    long long deltaIdle = idle - m_oldIdleTime;
    m_oldBusyTime = busy;
    m_oldIdleTime = idle;

    int retVal = -1;
    if (deltaBusy + deltaIdle == 0)
    {
        retVal = 0;
    }
    else
    {
        retVal = (int)(100 * (deltaBusy) / (deltaBusy + deltaIdle));
    }

    if (coreArray == NULL)
    {
      return retVal;
    }

    for (WebRtc_UWord32 i = 0; i < m_numCores; i++)
    {
        deltaBusy = m_busyArray[i] - m_oldBusyTimeMulti[i];
        deltaIdle = m_idleArray[i] - m_oldIdleTimeMulti[i];
        m_oldBusyTimeMulti[i] = m_busyArray[i];
        m_oldIdleTimeMulti[i] = m_idleArray[i];
        if(deltaBusy + deltaIdle == 0)
        {
            coreArray[i] = 0;
        }
        else
        {
            coreArray[i] = (int)(100 * (deltaBusy) / (deltaBusy+deltaIdle));
        }
    }
    return retVal;
}


int CpuLinux::GetData(long long& busy, long long& idle, long long*& busyArray,
                      long long*& idleArray)
{
    FILE* fp = fopen("/proc/stat", "r");
    if (!fp)
    {
        return -1;
    }

    char line[100];
    if (fgets(line, 100, fp) == NULL) {
        fclose(fp);
        return -1;
    }
    char firstWord[100];
    if (sscanf(line, "%s ", firstWord) != 1) {
        fclose(fp);
        return -1;
    }
    if (strncmp(firstWord, "cpu", 3) != 0) {
        fclose(fp);
        return -1;
    }
    char sUser[100];
    char sNice[100];
    char sSystem[100];
    char sIdle[100];
    if (sscanf(line, "%s %s %s %s %s ",
               firstWord, sUser, sNice, sSystem, sIdle) != 5) {
        fclose(fp);
        return -1;
    }
    long long luser = atoll(sUser);
    long long lnice = atoll(sNice);
    long long lsystem = atoll(sSystem);
    long long lidle = atoll (sIdle);

    busy = luser + lnice + lsystem;
    idle = lidle;
    for (WebRtc_UWord32 i = 0; i < m_numCores; i++)
    {
        if (fgets(line, 100, fp) == NULL) {
            fclose(fp);
            return -1;
        }
        if (sscanf(line, "%s %s %s %s %s ", firstWord, sUser, sNice, sSystem,
                   sIdle) != 5) {
            fclose(fp);
            return -1;
        }
        luser = atoll(sUser);
        lnice = atoll(sNice);
        lsystem = atoll(sSystem);
        lidle = atoll (sIdle);
        busyArray[i] = luser + lnice + lsystem;
        idleArray[i] = lidle;
    }
    fclose(fp);
    return 0;
}

int CpuLinux::GetNumCores()
{
    FILE* fp = fopen("/proc/stat", "r");
    if (!fp)
    {
        return -1;
    }
    // Skip first line
    char line[100];
    if (!fgets(line, 100, fp))
    {
        fclose(fp);
        return -1;
    }
    int numCores = -1;
    char firstWord[100];
    do
    {
        numCores++;
        if (fgets(line, 100, fp))
        {
            if (sscanf(line, "%s ", firstWord) != 1) {
                firstWord[0] = '\0';
            }
        } else {
            break;
        }
    } while (strncmp(firstWord, "cpu", 3) == 0);
    fclose(fp);
    return numCores;
}
} // namespace webrtc
