#include <stdio.h>
#include <stdlib.h>
#include "processes.h"

typedef unsigned int uint;
typedef unsigned long int ulong;

const ulong INIT_CAP = 1024;

struct process {
	uint pid;
	uint ppid;
	char user[9];
	uint priority;
	float cpu;
	ulong resident_size;
	ulong size;
	ulong virtual_size;
	char command[16];
};

struct processes {
	ulong size;
	ulong capacity;
	struct process* procs;
};

struct query_result {
	ulong size;
	ulong i;
	struct process** procs;
};

struct processes* new_processes() {
	struct processes* p = malloc(sizeof(struct processes));
	if (!p) {
		return NULL;
	}

	struct process* p_arr = malloc(INIT_CAP * sizeof(struct process));
	if (!p_arr) {
		free(p);
		return NULL;
	}

	p->size = 0;
	p->capacity = INIT_CAP;
	p->procs = p_arr;
	return p;
}

void delete(struct processes* p) {
	free(p->procs);
	free(p);
}

int add_from_file(struct processes* ps, const char* filename) {
	FILE* f = fopen(filename, "r");
	if (!f) {
		return 0;
	}

	struct process p;
	while (fscanf(f, "%u %u %8s %u %f %lu %lu %lu %15s\n", &p.pid, &p.ppid,
			p.user, &p.priority, &p.cpu, &p.resident_size, &p.size,
			&p.virtual_size, p.command) == 8) {

		if (ps->size == ps->capacity) {
			ps->capacity *= 2;
			struct process* new_procs = realloc(ps->procs,
				ps->capacity * sizeof(struct process));
			if (!new_procs) {
				return 0;
			}
			ps->procs = new_procs;
		}

		ps->procs[ps->size] = p;
		ps->size++;
	}

	fclose(f);
	return 1;
}

void clear(struct processes* p) {
	p->size = 0;
	// may shrink p->procs here
}

struct query_result* search(struct processes* ps, const struct query* q) {
	ulong capacity = INIT_CAP;
	struct query_result* res = malloc(sizeof(struct query_result));
	if (!res) {
		return NULL;
	}

	res->size = 0;
	res->i = 0;
	res->procs = malloc(sizeof(struct process*) * capacity);
	if (!res->procs) {
		free(res);
		return NULL;
	}

	for (ulong i = 0; i < ps->size; i++) {
		int add;

		struct process* p = &(ps->procs[i]);

		add = (q->priority == 0 || (q->priority < 0 &&
			(uint)(q->priority * -1) < p->priority) ||
			(q->priority > 0 && (uint) q->priority == p->priority));
		add = add && (q->rss == 0 || (q->rss < 0 &&
			(ulong)(q->rss * -1) < p->resident_size) ||
			(q->rss > 0 && (ulong) q->rss == p->resident_size));
		add = (q->size == 0 || (q->size < 0 &&
			(ulong)(q->size * -1) < p->size) ||
			(q->size > 0 && (ulong) q->size == p->size));
		add = (q->vsize == 0 || (q->vsize < 0 &&
			(ulong)(q->vsize * -1) < p->virtual_size) ||
			(q->vsize > 0 && (ulong) q->vsize == p->virtual_size));
		add = (q->cpu_usage == 0 || (q->cpu_usage < 0 &&
			q->cpu_usage * -1 < p->cpu) ||
			(q->cpu_usage > 0 && q->cpu_usage == p->cpu));

		if (add) {
			res->procs[res->size] = p;
			res->size++;
			if (res->size == capacity) {
				capacity *= 2;
				struct process** new_procs = realloc(res->procs,
					sizeof(struct process*) * capacity);
				if (!new_procs) {
					free(res->procs);
					free(res);
					return NULL;
				}
				res->procs = new_procs;
			}
		}
	}

	return res;
}

struct query_result* next(struct query_result* q) {
	if (q->i == q->size) {
		terminate_query(q);
		return NULL;
	}
	q->i++;
	return q;
}

void terminate_query(struct query_result* q) {
	free(q->procs);
	free(q);
}
