#pragma once
#include <psapi.h>
#include "windows.h"

#define OLV_TASKS_MAXCONJROW	100
#define OLV_TASKS_PORCMEM32		0.9
#define OLV_TASKS_PORCMEM64		0.9
#define OLV_TASKS_PORCCOLADJ	0.25
#define OLV_TASKS_MAXMEMNOMULTI	600*1024*1024
#define OLV_TASKS_MAXMEM32		1200*1024*1024
#define OLV_TASKS_MARGENMEM		20*1024*1024
#define TASKS_APP_NAME			"OliviaTasks.exe"
#define TASKS_FOLDER_NAME		"OliviaTasks"
#define OLV_DLL_NAME			"Olivia.dll"
#define TASKS_PORT				19996
#define TASKS_NOMB_COSTCONJ		"costconj"
#define TASKS_NOMB_COSTAMB		"costamb"
#define TASKS_NOMB_COSTANG		"costang"
#define TASKS_NOMB_IASO			"iaso"
#define TASKS_EXT_MAT			"otk"
#define TASKS_EXT_DIS			"dis"
#define TASKS_PARAM_SEP			" "

typedef enum TareasMultitask
{
	CALC_COST_AMB=1,
}TareasMultitask;

typedef struct HeadInfoAso
{
	int		nint;	//número de enteros guardados (3xnamb)
}HeadInfoAso;

typedef struct InfoIniTask
{
	char	path_temp[MAX_PATH];
	char	path_cconj[MAX_PATH];
	char	path_ang[MAX_PATH];
	char	path_costamb[MAX_PATH];
	char	file_iaso[MAX_PATH];
	char	ext[8];
	bool	usa_angs;
	int		KK;
	int		namb;//numero de ambitos
	int		nthr;//numero de threads
	int		ntsks;//numero de tasks
	int		n;//numero de nodos
	int		id_instal;
	int		id_planta;
}InfoIniTask;

class CoptiMemo
{
	
public:
	//*********************************
	//Variables
	int div;	//es 8 si es multitask, porque la mat de visto_ang ocupa n*n/8, si no es 1, porque ocupa n*n
	INT64 memo;
	double porc;
	//*********************************
	//Métodos
	CoptiMemo(BOOL mm=FALSE)
	{
		if(mm)
		{
			div=8;
			memo = dame_AvailPhysMem64();
			porc = OLV_TASKS_PORCMEM64;
		}
		else
		{
			div=1;
			memo= dame_AvailPhysMem64();
			porc = OLV_TASKS_PORCMEM32;
		}
	}
	~CoptiMemo(void)
	{
	}	
	//*************************************************************************************
	/*
	 * Pide la memoria disponible del sistema
	 */
	inline INT64 CoptiMemo::dame_AvailPhysMem64()
	{
		MEMORYSTATUSEX status;
		status.dwLength = sizeof(status);
		GlobalMemoryStatusEx(&status);
		return (INT64)status.ullAvailPhys;
	}
	//*************************************************************************************
	/*
	 * Pide la memoria disponible en 32, que es 1,2 GB aprox menos la usada por el proceso
	 */
	inline INT64 CoptiMemo::dame_AvailPhysMem32()
	{
		return (INT64)OLV_TASKS_MAXMEM32-dame_UsedPhysMem();
	}
	//*************************************************************************************
	/*
	 * Pide la memoria usada por el proceso
	 */
	inline INT64 CoptiMemo::dame_UsedPhysMem()
	{
		PROCESS_MEMORY_COUNTERS_EX pmc;
		GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, (DWORD)sizeof(pmc));
		//SIZE_T virtualMemUsedByMe = pmc.PrivateUsage;

		return (INT64) pmc.WorkingSetSize;
	}
	//*************************************************************************************
	/*
	 * Calcula el número de filas de mapmatrix que pueden ser vector en el master
	 */
	inline int CoptiMemo::calc_nvect_master(int nconj)
	{
		//lo calcula en base a lo que van a ocupar aprox las matrices
		/*mem_cost=(nvectxnconj+(nconj-nvect)*OLV_TASKS_MAXCONJROW)*sizeof(float)
		mem_tip=(nvectxnconj+(nconj-nvect)*OLV_TASKS_MAXCONJROW)*sizeof(int)
		mem_ang=nconj*OLV_TASKS_MAXCONJROW*sizeof(BYTE)
		mem_cost_amb, se va a calcular, cuando llegue el momento, si no cabe y se hace multitask
		margen=OLV_TASKS_MARGENMEM
		mem_cost+mem_tip+mem_ang+margen<=mem_memo*porc*/
		INT64 a,b,c,d;
		INT64 nvect;
		int sf, si;
		sf=sizeof(float);
		si=sizeof(int);

		if(memo<0)
			return -1;

		a=(INT64)((double)memo*porc);
		b=(INT64)nconj*OLV_TASKS_MAXCONJROW*(1+sf+si);
		c=(INT64)OLV_TASKS_MARGENMEM;
		d=(INT64)(sf+si)*(nconj-OLV_TASKS_MAXCONJROW);
		nvect = (a-b-c)/d;

		nvect=min(nconj,nvect);
		nvect=max(nvect,0);

		return (int)nvect;
	}
	//*************************************************************************************
	/*
	 * Calcula el número de filas de mapmatrix que pueden ser vector en multitask
	 * se tiene que tener en cuenta que luego se va a pedir para nthr el cálculo de cost_amb
	 */
	inline int CoptiMemo::calc_nvect_multi(int nconj, int nthr, int nbcola)
	{
		//lo calcula en base a lo que van a ocupar aprox las matrices
		/*mem_cost=(nvectxnconj+(nconj-nvect)*OLV_TASKS_MAXCONJROW)*sizeof(float)
		mem_tip=(nvectxnconj+(nconj-nvect)*OLV_TASKS_MAXCONJROW)*sizeof(int)
		mem_ang=nconj*OLV_TASKS_MAXCONJROW*sizeof(BYTE)
		mem_cost_amb=nthr*(nconj*nconj*sizeof(byte)*1/8+nconj*OLV_TASKS_PORCCOLADJ*sizeof(cola))
		margen=OLV_TASKS_MARGENMEM
		mem_cost+mem_tip+mem_ang+margen<=mem_disp*porc*/
		INT64 a,b,c,d, e;
		INT64 nvect;
		int sf, si;
		sf=sizeof(float);
		si=sizeof(int);

		if(memo<0)
			return -1;

		a=(INT64)((double)memo*porc);
		b=((INT64)nconj)*OLV_TASKS_MAXCONJROW*(1+sf+si);
		c=(INT64)OLV_TASKS_MARGENMEM;
		e=(INT64)((INT64)nthr)*((INT64)nconj)*((INT64)(nconj*1.0/div+OLV_TASKS_PORCCOLADJ*nbcola));
		d=(INT64)(sf+si)*(nconj-OLV_TASKS_MAXCONJROW);
		nvect = (a-b-c-e)/d;

		nvect=min(nconj,nvect);
		nvect=max(nvect,0);

		return (int)nvect;
	}
	//*************************************************************************************
	/*
	 * Calcula si se va a poder hacer en monotask o no
	 * para ello, deben caber en memoria al menos los 8 thr de cálculo de dj
	 */
	inline int is_multi(int nconj, int nthr, int nbcola)
	{
		//mem_cost_amb=nthr*(nconj*nconj*sizeof(byte)+nconj*OLV_TASKS_PORCCOLADJ*sizeof(cola))
		//mem_cost_amb+margen<=mem_disp*porc
		INT64 a,b,c;

		if(memo<0)
			return FALSE;

		a=(INT64)((double)memo*porc);
		b=(INT64)OLV_TASKS_MARGENMEM;
		c=(INT64)((INT64)nthr)*((INT64)nconj)*((INT64)(nconj+OLV_TASKS_PORCCOLADJ*nbcola));

		return (a-b-c<0);
	}
};