#ifndef SMON
#define SMON

#include <iosfwd>
#include <string>

namespace smon {
	const char RED[13] = "\x1b[0m\x1b[01;31m";
	const char GREEN[13] = "\x1b[0m\x1b[01;32m";
	const char BLACK[6] = "\x1b[0m";

	inline int percent(int numerator, int denominator) {
		return static_cast<int>(numerator*100.0/denominator);
	}

	class Base {
	public:
		virtual void read(std::istream& is) =0;
		virtual void gauge();
		virtual ~Base();
		virtual bool red() =0;
		virtual bool green() =0;
		int getPercent() const { return _all; }
	protected:
		std::string _name;
		int _all;
	};

	class Cpu : public Base {
	public:
		Cpu();
		virtual void read(std::istream& is);
		virtual bool red();
		virtual bool green();
	private:
		int _user, _nice, _sys, _free;
		int _user1, _nice1, _sys1, _free1;
		bool _inited;
	};

	class Memory : public Base {
	public:
		explicit Memory(bool swap);
		virtual void read(std::istream& is);
		virtual bool red();
		virtual bool green();
	private:
		enum {mem, swap} _type;
	};

	class Disk : public Base {
	public:
		explicit Disk();
		virtual void read(std::istream& is);
		virtual bool red();
		virtual bool green();
	private:
		enum {mem, swap} _type;
	};

	class Apm : public Base {
	public:
		Apm();
		virtual void read(std::istream& is);
		virtual bool red();
		virtual bool green();
	private:
	};

}

#endif // SMON

#include <string>
#include <ctime>
#include <strstream>
#include <fstream>

using namespace smon;
using namespace std;

Cpu::Cpu() {
  _inited = false;
  _all = 0;
}

void Cpu::read(std::istream& is) {
  int user2, nice2, sys2, free2;
  if (_inited) {
    user2 = _user1;
    nice2 = _nice1;
    sys2 = _sys1;
    free2 = _free1;
  }

  is >> _name >> _user1 >> _nice1 >> _sys1 >> _free1;

  if (!_inited) {
    _inited = true;
    return;
  }

  int user, nice, sys, free, all;
  user = _user1 - user2;
  nice = _nice1 - nice2;
  sys = _sys1 - sys2;
  free = _free1 - free2;
  all = user + nice + sys + free;

  if (0 == all) return;

  _user = percent(user, all);
  _nice = percent(nice, all);
  _sys = percent(sys, all);
  _free = percent(free, all);
  _all = percent(user+nice+sys, all);
}

Memory::Memory(bool sw) {
  if (sw) _type = swap;
  else _type = mem;
}

void Memory::read(istream& is) {
  int all, used, free, shared, buffers, cached, calc;
  is >> _name >> all >> used >> free;
  if (mem == _type) is >> shared >> buffers >> cached;

  if (swap == _type) calc = used;
  else calc = all - (free+shared+buffers+cached);

  _all = percent(calc, all);
}

bool Cpu::red() {
  return _all > 90;
}

bool Memory::red() {
  return _all > 90;
}

bool Apm::red() {
  return _all < 20;
}

bool Cpu::green() {
  return _all > 50;
}

bool Memory::green() {
  return _all > 50;
}

bool Apm::green() {
  return _all < 50;
}

void Base::gauge() {
  std::string out;
  for (int i = 1; i < _all/5; i++) {
    out += "#";
  }
  if (red()) std::cout << RED;
  else if (green()) std::cout << GREEN;
  std::cout << _name << '\t' << _all << '%' << '\t' << out;
  if (red() || green()) std::cout << BLACK;
}

Base::~Base() {
}

Apm::Apm() {
  _name = "apm";
}

void Apm::read(std::istream& is) {
  std::string all;
  unsigned int per;
  while (std::string::npos == (per = all.find("%")) && !is.eof()) {
    is >> all;
  }
  all.erase(per, 1);

  std::istrstream ss(all.c_str());
  ss >> _all;
}


int main(int argc, char* argv[]) {
  timespec ts;
  ts.tv_sec = 1;

  Cpu cpu;
  Memory mem(false), swap(true);
//  Apm apm;

  std::fstream is;

  char dust[256];

  int times = 0;
  if (2 == argc) {
    std::istrstream ss(argv[1]);
    ss >> times;
  }

  int t = 0;
  while (times == 0 || times != t++) {
    is.open("/proc/stat", ios::in);
    cpu.read(is);

    std::string buf;
    while (is >> buf) {
      if (buf == "disk_io:") {
	    break;
	  }
	}

    is.close();

    is.open("/proc/meminfo", ios::in);
    is.getline(dust, 256);
    mem.read(is);
    swap.read(is);
    is.close();

//    is.open("/proc/apm", ios::in);
//    apm.read(is);
//    is.close();

    std::cout << '\n';
    const time_t t = time(0);
    std::cout << asctime(localtime(&t));
    cpu.gauge();
    std::cout << '\n';
    mem.gauge();
    std::cout << '\n';
    swap.gauge();
//    std::cout << '\n';
//    apm.gauge();
    std::cout.flush();

    nanosleep(&ts, 0);
  }
  return 0;

}
