#include "defines.h"

Task Tlist[MAXTASKS];
int ShmCount = 0;
int semID;
struct sembuf sops;

int Deps[MAXDEPTASKS];

int CheckID()
{
//////////////////////////////////////
}

int CheckCorrect()
{
//////////////////////////////////////
}


///ОБРАБОТКА ОТРИЦАТЕЛЬНЫХ ЗНАЧЕНИЙ
//ПРОВЕРКА СУЩЕСТВОВАНИЯ ЗАВИСИМЫХ ЗАДАЧ
int Read()
{
	int ID, ltime, wtime, IDDep[MAXDEPTASKS];
	char comm[MAXLENCOMM];
	char str[MAXLENCOMM+100];
	int num = 0, test, count = 0, i, j;
	char sym, *pos;

	memset(comm, '\0', MAXLENCOMM);
	for (i = 0; i < MAXTASKS; i++)
	{
		count = 0;
		memset(str, '\0', MAXLENCOMM+100);
		sym = getchar();
		while (sym != '\n')
		{
			if (count >= MAXLENCOMM + 100)
			{
				fprintf(stderr, "Превышение размера строки!");
				break;
			}
			str[count] = sym;
			++count;
			sym = getchar();
		}
		if (count == 0) break;            //выход если пусто

		if ( sscanf(str, "%d %d %d", &ID, &ltime, &wtime) < 3 )
		{
			fprintf(stderr, "Некорректный ввод!\n");
			--i;
			continue;
		}
		if ( (pos = strchr(str, '>')) == NULL )
		{	
			j = 0;
			while ( (str[j] >= '0') && (str[j] <= '9') || (str[j] == ' ') ) j++;
			strcpy(comm, &(str[j]));
		}
		else
		{	
			
			printf("DEP TASKS!\n");
		}
		Tlist[i].ID = ID;
		Tlist[i].launchtime = ltime;
		Tlist[i].worktime = wtime;	
		strcpy(Tlist[i].command, comm);
		++num;                                 //увеличение счетчика количества команд
	}
	return num;
}

shmTask* Connect(int *shmID, int num)
{
	key_t key;
	shmTask *Tsk;
	key = ftok("/usr/tasks", 's');
	if ( (semID = semget(key, 1, 0644 | IPC_CREAT)) == -1 )
	{
		fprintf(stderr, "Can't create semophore!\n");
		return NULL;
	}
	semctl(semID, 0, SETVAL, (int) 0);
	if ( (*shmID = shmget(key, 2 * MAXTASKS * sizeof(shmTask), 0644 | IPC_CREAT)) < 0 )
	{
		fprintf(stderr, "Can't create shared memory segment!\n");
		return NULL;
	}
	if ( (Tsk = (shmTask*) shmat( *shmID, NULL, 0 )) == -1 )
	{	
		fprintf(stderr, "Can't attach to shared memory!\n");
		return NULL;
	}	
	sops.sem_num = 0;
	sops.sem_flg = 0;	
	return Tsk;
}

void AddToShm(shmTask *Tsk, int ID, int time, int flag, char command[MAXLENCOMM], int depID[MAXDEPTASKS])
{
	semctl(semID, 0, SETVAL, (int) 0); //блокируем для программы-монитора
	Tsk[ShmCount].ID = ID;
	Tsk[ShmCount].time = time;
	if (flag == 0) 
		Tsk[ShmCount].cmd = TSK_LAUNCH;
	else if (flag == 1)
		Tsk[ShmCount].cmd = TSK_KILL;
	strcpy(Tsk[ShmCount].str, command);
	AssignDeps(Tsk[ShmCount].depID, depID);
	Tsk[ShmCount].PID = TSK_NOPID;
	Tsk[ShmCount].status = TSK_NOTEXEC;
	++ShmCount;
	semctl(semID, 0, SETVAL, (int) 1); //разблокируем для программы-монитора
}


//мы просматриваем тут ВЕСЬ список независимо от того полны ли они все. ПРЕДУСМОТРЕТЬ ПОТОМ ЭТО!
//ПО ИДЕЕ НЕ ОБРАБАТЫВАЮТСЯ ОДНОВРЕМЕННЫЕ СОБЫТИЯ!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
void SortTasks(shmTask *Tsk, int num)
{
	int i, j, oldmin = 0, oldk = -1, oldm = -1, cur = 32767, k = -1, m = -1;

	for (j=0; j < 2 * num; j++)
	{
	
	for (i = 0; i < num; i++)
	{
		if ( (Tlist[i].launchtime < cur) && (Tlist[i].launchtime >= oldmin) )
		{
			if ( ((Tlist[i].launchtime == oldmin) && ((oldk < i) || (oldk == i) && (oldm == 1))) || (Tlist[i].launchtime > oldmin) )
			{
				cur = Tlist[i].launchtime;
				k = i;
				m = 0;
			}
		}
		if ( ((Tlist[i].launchtime + Tlist[i].worktime) < cur) && ((Tlist[i].launchtime + Tlist[i].worktime) >= oldmin) )
		{
			if ( (((Tlist[i].launchtime + Tlist[i].worktime) == oldmin) && ((oldk < i) || (oldk == i) && (oldm == 0))) || ((Tlist[i].launchtime + Tlist[i].worktime) > oldmin) )
			cur = Tlist[i].launchtime + Tlist[i].worktime;
			k = i;
			m = 1;
		}

	}
	AddToShm(Tsk, Tlist[k].ID, cur, m, Tlist[k].command, Tlist[k].depID);	
	oldmin = cur;
	oldk = k;
	oldm = m;
	cur = 32767;
	}
}

int InsertPID( shmTask *Tsk, const int ID, const int PID, const int num )
{
	int i, count=0;
	semctl(semID, 0, SETVAL, (int) 1);
	for (i=0; i<2*num; i++)
		if (Tsk[i].ID == ID)
		{
			Tsk[i].PID = PID;
			++count;
			if (count == 2) break;
		}
	semctl(semID, 0, SETVAL, (int) 2);
	return 0;
}

void HandleChild(int s)
{
	printf("Child was ended!\n");	
	signal( SIGCHLD, SIG_IGN);
}

void ExecProg( char str[MAXLENCOMM] )
{
	int i = 0, k = 0, j = 0;
	char pos[MAXLENCOMM];
	char* argv[MAXPARAMS];

	memset(pos, '\0', MAXLENCOMM);
			while (str[i] != '\0')
			{
				if (str[i] == ' ')
				{
					argv[k] = (char*)malloc(j);
   				strcpy(argv[k], pos);
					j = 0;
					++i; ++k;
					memset(pos, '\0', MAXLENCOMM);
				}
				else
				{
					pos[j] = str[i];
					++i; ++j;
				}
				if (str[i-1] != ' ')
				{
					argv[k] = (char*)malloc(j);
   				strcpy(argv[k], pos);
				}
			}		
	argv[k+1] = NULL;
	execv(argv[0], argv);
}

int Handle( shmTask *Tsk, int num )
{
	int i, ID, event_time, cmd;
	char str[MAXLENCOMM];
	int depID[MAXDEPTASKS];
	pid_t PID;
	int status;
	time_t start_time;
	start_time = time(NULL);	
	for (i=0; i<2*num; i++)
	{
		sops.sem_op = -1;
		semop(semID, &sops, 1);
		ReadFromShm(Tsk, i, &ID, &event_time, &cmd, str, depID, &PID, &status);
		sops.sem_op = 1;
		semop(semID, &sops, 1);
		sleep( event_time - (time(NULL) - start_time) );
		if ( (cmd == TSK_LAUNCH) && (depID[0] == 0) )
			if (PID = fork()) //родитель
			{
				sops.sem_op = -1;
				semop(semID, &sops, 1);
				InsertPID(Tsk, ID, PID, num);
				Tsk[i].status = TSK_EXEC;
				sops.sem_op = 1;
				semop(semID, &sops, 1);
			}
			else //сын
			{
				fprintf(stderr, "*** %d - Запуск ID = %d, программа: %s\n", (int)(time(NULL)-start_time), ID, str);
				ExecProg(str);
				fprintf(stderr, "Error!\n");
				return -1;
			}
		else if ( cmd == TSK_KILL )
			{
				sops.sem_op = -1;
				semop(semID, &sops, 1);
				InsertPID(Tsk, ID, TSK_NOPID, num);
				Tsk[i].status = TSK_EXEC;
				sops.sem_op = 1;
				semop(semID, &sops, 1);
				if ( !(waitpid(PID, NULL, WNOHANG)) )
				{					
					fprintf(stderr, "*** %d - Принудительное завершение ID = %d, программа: %s\n", (int)(time(NULL)-start_time), ID, str);
					kill(PID, SIGKILL);
				}
			}
	}	

	return 0;
}


int main()
{
	shmTask *Tsk, *shm;
	int shmID, i, num;

	num = Read();
	if ( (Tsk = Connect(&shmID, num)) == NULL )
		return -1;	
	SortTasks(Tsk, num);
	Handle(Tsk, num);

	shmdt(Tsk);
	semctl(semID, 0, IPC_RMID, NULL);
	shmctl(shmID, IPC_RMID, NULL);
	return 0;
}
