#include <cstdio>
#include <chrono>
#include <Processing.NDI.Lib.h>
#include <opencv2/opencv.hpp>

#ifdef _WIN32
#ifdef _WIN64
#pragma comment(lib, "Processing.NDI.Lib.x64.lib")
#else // _WIN64
#pragma comment(lib, "Processing.NDI.Lib.x86.lib")
#endif // _WIN64
#endif // _WIN32

int main(int argc, char* argv[])
{	// Not required, but "correct" (see the SDK documentation.
	if (!NDIlib_initialize()) return 0;
	
	// Create a finder
	NDIlib_find_instance_t pNDI_find = NDIlib_find_create_v2();
	if (!pNDI_find) return 0;

	// Wait until there is one source
	uint32_t no_sources = 0;
	const NDIlib_source_t* p_sources = NULL;
	while (!no_sources)
	{	// Wait until the sources on the nwtork have changed
		printf("Looking for sources ...\n");
		NDIlib_find_wait_for_sources(pNDI_find, 1000/* One second */);
		p_sources = NDIlib_find_get_current_sources(pNDI_find, &no_sources);
	}

	// We now have at least one source, so we create a receiver to look at it.
    NDIlib_recv_create_t pNDI_recv_parameters = { *p_sources, NDIlib_recv_color_format_e_BGRX_BGRA, NDIlib_recv_bandwidth_highest, true };
	NDIlib_recv_instance_t pNDI_recv = NDIlib_recv_create_v2(&pNDI_recv_parameters);
	if (!pNDI_recv) return 0;

	// Connect to our sources
	NDIlib_recv_connect(pNDI_recv, p_sources + 0);

	// Destroy the NDI finder. We needed to have access to the pointers to p_sources[0]
	NDIlib_find_destroy(pNDI_find);	

	// Run for one minute
	using namespace std::chrono;
//    uchar* p_data = (uchar*)malloc(1920 * 1080 * 2);
	for (const auto start = high_resolution_clock::now(); high_resolution_clock::now() - start < minutes(5);)
	{	// The descriptors
		NDIlib_video_frame_v2_t video_frame;
//		NDIlib_audio_frame_v2_t audio_frame;

		switch (NDIlib_recv_capture_v2(pNDI_recv, &video_frame, NULL, NULL, 1000))
		{	// No data
			case NDIlib_frame_type_none:
				printf("No data received.\n");
				break;

			// Video data
			case NDIlib_frame_type_video:
				printf("Video data received (%dx%d).\n", video_frame.xres, video_frame.yres);
                if (video_frame.p_data != nullptr){
//                    memcpy(p_data, (uchar*)video_frame.p_data, video_frame.xres * video_frame.yres * 2 * sizeof(uchar));

                    // A bit of testing of the received data.
                    std::cout << "The first 100 elements received: " << std::endl;
                    for (int i = 0; i < 100; i++){
                        std::cout << (int)video_frame.p_data[i] << " ";
                    }
                    std::cout << std::endl;

                    // A bit of testing of the received data.
                    for (int i = 0; i < 100; i++){
                        std::cout << "[ " << (int)video_frame.p_data[i*video_frame.xres*3] << " ";
                        std::cout << (int)video_frame.p_data[i*video_frame.xres*3 + 1] << " ";
                        std::cout << (int)video_frame.p_data[i*video_frame.xres*3 + 2] << " ] ";
//                        std::cout << (int)video_frame.p_data[i*video_frame.xres*4 + 3] << " ] ";
                    }
                    std::cout << std::endl;

//                    cv::Mat frame(video_frame.yres, video_frame.xres, CV_8UC4, video_frame.p_data);
                    cv::Mat frame = cv::Mat::zeros(cv::Size(video_frame.xres-1, video_frame.yres), CV_8UC4);
                    std::cout << std::endl;
                    memcpy(frame.data, video_frame.p_data, (double)video_frame.xres * (double)video_frame.yres * (double)4);
                    for (int i = 0; i < 100; i++){
                        std::cout << frame.at<cv::Vec4b>(0, i) << " ";
                    }
                    std::cout << std::endl;

                    //                    cv::cvtColor(frame, frame, cv::COLOR_YUV2BGR_UYVY, 0);
//                memcpy(frame.data, video_frame.p_data, video_frame.xres * video_frame.yres * 4 * sizeof(uint8_t));
                    cv::imshow("received image", frame);
                    char c = (char)cv::waitKey(0);
//                    if (c==27){
//                            break;
//                    }
//                    free(p_data);
                }
				NDIlib_recv_free_video_v2(pNDI_recv, &video_frame);
				break;
			// Audio data
			case NDIlib_frame_type_audio:
//				printf("Audio data received (%d samples).\n", audio_frame.no_samples);
//				NDIlib_recv_free_audio_v2(pNDI_recv, &audio_frame);
				break;
		}
	}

	// Destroy the receiver
	NDIlib_recv_destroy(pNDI_recv);

	// Not required, but nice
	NDIlib_destroy();

	// Finished
	return 0;
}
