/*
    lsadl is a frontend/wrapper to the adl_sdk using gtk
    Copyright (C) 2011  Sterling Pickens

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <dlfcn.h>
#include "config.h"
#include "Globals.h"
#ifdef HAVE_PTHREAD_H
#include <pthread.h>
#elif HAVE_PTHREADS_H
#include <pthreads.h>
#endif
#include "adl_api.h"
#include "ls_gtk.h"
#include "ls_pth.h"

extern const char *ADL_Func_Names[LSADL_FUNCS];
extern char ADL_Func_Enabled[LSADL_FUNCS];
extern char ADL_Func_Notes[LSADL_FUNCS];
struct ADLMemoryInfo lpADLMemoryInfo;
struct AdapterInfo *lpAdapterInfo;
struct ADLThermalControllerInfo lpThermalControllerInfo;
extern int iNumberAdapters;
extern ADLTemperature iTemperature;
extern int iAdapterIndex;
//extern struct OverDrive5s od5;
extern int No_Exit;
extern int Disable_Temp;
extern pthread_t Gtk_thread_ptr;
extern pthread_t Refresh_thread_ptr;
extern pthread_mutex_t Mutex1;

void Usage(void){
	printf("lsadl [adapter]\n");
    printf("\t[optional]\n");
	printf("\tadapter\t= adapter int 0 to last unique adapter\n");
	printf("\t\tdefaults to first adapter\n");
}

int main(int argc, char **argv){
	printf("\t%s\n\thttp:/", PACKAGE_STRING);
	printf("/%s\n\t%s\n\n", PACKAGE_URL, PACKAGE_BUGREPORT);
	printf("WARNING: This can be Dangerous Software!\n");
	printf("\tUse at your own risk!\n");
	printf("To select adapter:\n");
	printf("\t./lsadl [adapter-num]\n");
	if(argc > 2){
		Usage();
		printf("Exiting!\n");
	}
	void *so_handle = dlopen( "libatiadlxx.so", RTLD_LAZY|RTLD_GLOBAL);
	int num1 = 0;
	int adapter_not_found = 1;
	int sel_adapt = 0;
	int ret_val = 0;
	int cur_adapt = 0;
	int lpAdapterID = 0;
	int last_adapter = -1;
	No_Exit = 1;
	(void)DL_and_Check(so_handle);

	//if(argc != 1)
	//	num1 = atoi(argv[1]);

	ret_val = LSADL_Main_Control_Create(ADL_Main_Memory_Alloc, 1);
	if(ret_val != ADL_OK){
		printf("ADL Initialization Error!\n");
		Print_Code(ret_val);
		printf("Exiting!\n");
		dlclose(so_handle);
		exit(1);
	}

	ret_val = LSADL_Adapter_NumberOfAdapters_Get(&iNumberAdapters);
	if(ret_val != ADL_OK){
		printf("Cannot get the number of adapters!\n");
		Print_Code(ret_val);
		printf("Exiting!\n");
		LSADL_Main_Control_Destroy();
		dlclose(so_handle);
		exit(1);
	}

	lpAdapterInfo = malloc(sizeof(struct AdapterInfo)*iNumberAdapters);
	(void)memset((void *)lpAdapterInfo,'\0', sizeof(struct AdapterInfo)*iNumberAdapters);
	(void)memset((void *)&lpADLMemoryInfo,'\0', sizeof(struct ADLMemoryInfo));

	ret_val = LSADL_Adapter_AdapterInfo_Get(lpAdapterInfo, sizeof(struct AdapterInfo)*iNumberAdapters);
	if(ret_val != ADL_OK){
		printf("Cannot get the number of adapter info!\n");
		Print_Code(ret_val);
		printf("Exiting!\n");
		free(lpAdapterInfo);
		LSADL_Main_Control_Destroy();
		dlclose(so_handle);
		exit(1);
	}

	printf("Adapters(non-unique): %d\n", iNumberAdapters);
	if(argc == 2){
		sel_adapt = atoi(argv[1]);
		printf("User selected adapter %d\n", sel_adapt);

		for(num1=0; num1<iNumberAdapters; num1++){
			iAdapterIndex = lpAdapterInfo[num1].iAdapterIndex;
			ret_val = LSADL_Adapter_ID_Get(iAdapterIndex, &lpAdapterID);
			if(ret_val != ADL_OK){
				printf("Cannot get adapter ID for iAdapterIndex %d!\n", iAdapterIndex);
				Print_Code(ret_val);
			}
			//if(lpAdapterID == ){
			//	iAdapterIndex = lpAdapterInfo[num1].iAdapterIndex;
			//	break;
			//}
			if(lpAdapterID == last_adapter)
				continue;
			last_adapter = lpAdapterID;
			cur_adapt++;
			if(cur_adapt == sel_adapt){
				adapter_not_found = 0;
				break;
			}
			//if(last_adapter == num1)
			//	break;
		}

		if(adapter_not_found){
			printf("Error: Adapter not found!\n");
			printf("Exiting!\n");
			free(lpAdapterInfo);
			LSADL_Main_Control_Destroy();
			dlclose(so_handle);
			exit(1);
		}
		//iAdapterIndex = lpAdapterInfo[num1].iAdapterIndex;
	}else{
		printf("Defaulting to adapter %d\n", sel_adapt);
	}
	ret_val = LSADL_Adapter_MemoryInfo_Get(iAdapterIndex, &lpADLMemoryInfo);
	if(ret_val != ADL_OK){
		printf("Cannot get the number of memory info!\n");
		Print_Code(ret_val);
		printf("Exiting!\n");
		free(lpAdapterInfo);
		LSADL_Main_Control_Destroy();
		dlclose(so_handle);
		exit(1);
	}

	ret_val = LSADL_OD5_ThermalDevices_Enum(iAdapterIndex, 0, &lpThermalControllerInfo);
	if(ret_val != ADL_OK){
		printf("Error: ADL_Overdrive5_ThermalDevices_Enum!\n");
		Print_Code(ret_val);
		Disable_Temp = 1;
		//printf("Exiting!\n");
		//free(lpAdapterInfo);
		//LSADL_Main_Control_Destroy();
		//dlclose(so_handle);
		//exit(1);
	}else{
		printf("Thermal Controller domain index %d\n", lpThermalControllerInfo.iDomainIndex);
		//GPU 0, 1, etc.
		if(lpThermalControllerInfo.iThermalDomain == ADL_DL_THERMAL_DOMAIN_OTHER)
			printf("\tADL_DL_THERMAL_DOMAIN_OTHER\n");
		if(lpThermalControllerInfo.iThermalDomain == ADL_DL_THERMAL_DOMAIN_GPU)
			printf("\tADL_DL_THERMAL_DOMAIN_GPU\n");

		if(lpThermalControllerInfo.iFlags == ADL_DL_THERMAL_FLAG_INTERRUPT)
			printf("\tADL_DL_THERMAL_FLAG_INTERRUPT\n");
		if(lpThermalControllerInfo.iFlags == ADL_DL_THERMAL_FLAG_FANCONTROL)
			printf("\tADL_DL_THERMAL_FLAG_FANCONTROL\n");
	}

	ret_val = LSADL_OD5_Temperature_Get(iAdapterIndex, 0, &iTemperature);
	if(ret_val != ADL_OK){
		printf("Cannot get od5 temperature!\n");
		Print_Code(ret_val);
		Disable_Temp = 1;
		//printf("Exiting!\n");
		//free(lpAdapterInfo);
		//LSADL_Main_Control_Destroy();
		//dlclose(so_handle);
		//exit(1);
	}
	if(!Disable_Temp)
		printf("Temperature: %.02lfC\n", (double)iTemperature.iTemperature/1000);

	printf("Mem:\n");
	printf("\tSize:      %lld bytes\n", lpADLMemoryInfo.iMemorySize);
	printf("\tType:      %s\n", lpADLMemoryInfo.strMemoryType);
	printf("\tBandwidth: %lld MB/sec\n", lpADLMemoryInfo.iMemoryBandwidth);

	if(Perf_Alloc() == FALSE){
		printf("Perf_Alloc: memory allocation error!\n");
		printf("Exiting!\n");
		free(lpAdapterInfo);
		LSADL_Main_Control_Destroy();
		dlclose(so_handle);
		exit(1);
	}
#ifdef HAVE_GTK_G_THREAD_INIT
	g_thread_init(NULL);
#endif
	gdk_threads_init();
	gdk_threads_enter();
	gtk_init(&argc, &argv);

	if(pthread_create(&Gtk_thread_ptr, NULL, GTK_Func, NULL) != 0){
		printf("Error: pthread_create failed to launch Gtk thread !\n");
		printf("Exiting!\n");
		free(lpAdapterInfo);
		LSADL_Main_Control_Destroy();
		dlclose(so_handle);
		exit(1);
	}
	if(pthread_create(&Refresh_thread_ptr, NULL, Refresh_Func, NULL) != 0){
		printf("Error: pthread_create failed to launch Refresh thread !\n");
		printf("Exiting!\n");
		//fixme kill other thread
		free(lpAdapterInfo);
		LSADL_Main_Control_Destroy();
		dlclose(so_handle);
		exit(1);
	}
	if(pthread_join(Gtk_thread_ptr, NULL) != 0){
		printf("Error: pthread_join failed to join Gtk thread!\n");
		printf("\tExiting!\n");
		free(lpAdapterInfo);
		LSADL_Main_Control_Destroy();
		dlclose(so_handle);
		exit(1);
	}
	gdk_threads_leave();
	if(pthread_join(Refresh_thread_ptr, NULL) != 0){
		printf("Error: pthread_join failed to join Refresh thread!\n");
		printf("\tExiting!\n");
		free(lpAdapterInfo);
		LSADL_Main_Control_Destroy();
		dlclose(so_handle);
		exit(1);
	}
	if(Perf_Dealloc() == FALSE){
		printf("Perf_Delloc: memory allocation error!\n");
	}

	LSADL_Main_Control_Destroy();
	free(lpAdapterInfo);
	dlclose(so_handle);
	return 0;
}