//
//  Copyright (c) 1994, 1995, 2006 by Mike Romberg ( mike.romberg@noaa.gov )
//
//  This file may be distributed under terms of the GPL
//
// $Id$
//
#include "memmeter.h"
#include "xosview.h"
#include <fstream>
#include <sstream>
#include <stdlib.h>

static const char MEMFILENAME[] = "/proc/meminfo";
static const char MEMSTATFNAME[] = "/proc/memstat";

MemMeter::MemMeter( XOSView *parent )
: FieldMeterGraph( parent, 4, "MEM", "USED+SHAR/BUFF/CACHE/FREE" ){
  _shAdj = -1;

  // Check and see if the memstat module has been loaded
  std::ifstream test(MEMSTATFNAME);
  if (test){
    setNumFields(5);
    legend("USED/SHAR/BUFF/CACHE/FREE");
    _shAdj = 0;
  }

  _MSlineInfos = NULL;
  _MIlineInfos = NULL;
  initLineInfo();
}

MemMeter::~MemMeter( void ){
  delete[] _MIlineInfos;
  delete[] _MSlineInfos;
}

void MemMeter::checkResources( void ){
  FieldMeterGraph::checkResources();

  setfieldcolor( 0, parent_->getResource( "memUsedColor" ) );
  if (_shAdj == 0)
    setfieldcolor( 1, parent_->getResource( "memSharedColor" ) );
  setfieldcolor( 2 + _shAdj, parent_->getResource( "memBufferColor" ) );
  setfieldcolor( 3 + _shAdj, parent_->getResource( "memCacheColor" ) );
  setfieldcolor( 4 + _shAdj, parent_->getResource( "memFreeColor" ) );
  priority_ = atoi (parent_->getResource( "memPriority" ) );
  dodecay_ = parent_->isResourceTrue( "memDecay" );
  useGraph_ = parent_->isResourceTrue( "memGraph" );
  SetUsedFormat (parent_->getResource("memUsedFormat"));
}

void MemMeter::checkevent( void ){
  getmeminfo();
  /* for debugging (see below)
  printf("t %4.1f used %4.1f share %4.1f buffer %4.1f cache %4.1f free %4.1f\n",
         total_/1024.0/1024.0,
         fields_[0]/1024.0/1024.0, fields_[1]/1024.0/1024.0,
	 fields_[2]/1024.0/1024.0, fields_[3]/1024.0/1024.0,
	 fields_[4]/1024.0/1024.0);
  */
  drawfields();
}

// FIXME: /proc/memstat and /proc/meminfo don't seem to correspond
// maybe it is time to fix this in the kernel and get real infos ...

void MemMeter::getmeminfo( void ){
  getmemstat(MEMFILENAME, _MIlineInfos, _numMIlineInfos);
  if (_shAdj == 0)
    getmemstat(MEMSTATFNAME, _MSlineInfos, _numMSlineInfos);

  if (_shAdj == 0){
    fields_[3] -= fields_[1]; // cache size seems to contain shared size !?
                              // without this fix "used" sometimes gets < 0 !
    fields_[0] = total_ - fields_[4] - fields_[3] - fields_[2] - fields_[1];
  }else{
    fields_[0] = total_ - fields_[3] - fields_[2] - fields_[1];
  }

  if (total_)
    FieldMeterDecay::setUsed (total_ - fields_[4 + _shAdj], total_);
}

MemMeter::LineInfo *MemMeter::findLines(LineInfo *tmplate, int len,
                                             const char *fname){
  std::ifstream meminfo(fname);
  if (!meminfo){
    std::cerr << "Can not open file : " << fname << std::endl;
    exit(1);
  }

  LineInfo *rval = new LineInfo[len];

  char buf[256];

  // Get the info from the "standard" meminfo file.
  int lineNum = 0;
  int inum = 0;  // which info are we going to insert
  while (!meminfo.eof()){
    meminfo.getline(buf, 256);
    lineNum++;

    for (int i = 0 ; i < len ; i++)
      if(!strncmp(tmplate[i].id(), buf, tmplate[i].idlen())){
        rval[inum] = tmplate[i];
        rval[inum].line(lineNum);
        inum++;
      }
  }

  return rval;
}

void MemMeter::initLineInfo(void){
  static LineInfo infos[] = {
    LineInfo("MemTotal", &total_),
    LineInfo("MemFree", &fields_[4 + _shAdj]),
    LineInfo("Buffers", &fields_[2 + _shAdj]),
    LineInfo("Cached", &fields_[3 + _shAdj])
  };
  _numMIlineInfos = sizeof(infos) / sizeof(LineInfo);

  _MIlineInfos = findLines(infos, _numMIlineInfos, MEMFILENAME);

  static LineInfo msinfos[] = {
    LineInfo("Shared", &fields_[1])
  };
  _numMSlineInfos = sizeof(msinfos) / sizeof(LineInfo);

  if (_shAdj == 0)
    _MSlineInfos = findLines(msinfos, _numMSlineInfos, MEMSTATFNAME);
}

void MemMeter::getmemstat(const char *fname, LineInfo *infos, int ninfos){
  std::ifstream meminfo(fname);
  if (!meminfo){
    std::cerr << "Can not open file : " << fname << std::endl;
    exit(1);
  }

  char buf[256];

  // Get the info from the "standard" meminfo file.
  int lineNum = 0;
  int inum = 0;
  while (!meminfo.eof()){
    meminfo.getline(buf, 256);
    lineNum++;
    if (lineNum != infos[inum].line())
      continue;

    std::string sline(buf, 256);
    std::istringstream line(sline);
    unsigned long val;
    std::string ignore;
    line >> ignore >> val;
    /*  All stats are in KB.  */
    infos[inum].setVal(val*1024.0);	/*  Multiply by 1024 bytes per K  */

    inum++;
    if (inum >= ninfos)
      break;
  }
}
