#include <stdexcept>
#include <iostream>
#include <csignal>
#include <fstream>
#include <vector>
#include <numeric>
#include <iostream>
#include <chrono>
#include <math.h>


#include <AvroPackets/S22Packet.h>
#include <Configurators/ConsumerConfigurator.h>
#include "Encoder.hh"
#include "Decoder.hh"
#include "ValidSchema.hh"
#include "Compiler.hh"
#include "DataFile.hh"
#include <cppkafka/configuration.h>
#include <cppkafka/cppkafka.h>

bool running = true;

int main(int argc, char *argv[]) {

	ConsumerConfigurator consConf(argc, argv);
	std::cout << "Starting cosumer with params: " << std::endl;
	std::cout << consConf.toString() << std::endl;

	// Stop processing on SIGINT
	signal(SIGINT, [](int) {
		running = false;
		std::cout << "Terminated by CTRL-C " << std::endl;
	});

	// Construct the configuration
	cppkafka::Configuration config = { { "metadata.broker.list",
			consConf.getKafkaIP().append(":").append(consConf.getKafkaPort()) },
			{ "group.id", consConf.getGroupID() },
			{ "auto.offset.reset" , "earliest"},
			{ "enable.auto.offset.store" , true},
			{ "enable.auto.commit", false } };

	// Create the consumer
	cppkafka::Consumer consumer(config);


	// Subscribe to the topic
	std::string topic = consConf.getKafkaTopic();

	// Print the assigned partitions on assignment
	consumer.set_assignment_callback(
			[](const cppkafka::TopicPartitionList &partitions) {
				std::cout << "Got assigned: " << partitions << std::endl;
			});
	// Print the revoked partitions on revocation
	consumer.set_revocation_callback(
			[](const cppkafka::TopicPartitionList &partitions) {
				std::cout << "Got revoked: " << partitions << std::endl;
			});


	//consumer.assign();
	consumer.subscribe( { topic });
	std::cout << "Consuming messages from topic " << topic << std::endl;


	// Now read messages from topic
	std::cout << "running..." << std::endl;
	auto start = std::chrono::steady_clock::now(); // start timer
	int count = 0; // numero di pacchetti elaborati
	bool first = true;
	cppkafka::Message msg;
	while (running) {
		// Try to consume a message
		msg = consumer.poll();

		if (msg) {
			// If we managed to get a message
			if (msg.get_error()) {
				if (!msg.is_eof()) {
					std::cout << "[+] Received error notification: "
							<< msg.get_error() << std::endl;
				} else{
					std::cout << "EOF" << std::endl;
				}
			} else {
				
				count += 1;
				//AVRO DECONDE INIT
				std::unique_ptr<avro::InputStream> in = avro::memoryInputStream(
						(const uint8_t*) msg.get_payload().get_data(),
						msg.get_payload().get_size());
				avro::DecoderPtr d = avro::binaryDecoder();
				d->init(*in);

				S22::S22 packet;
				avro::decode(*d, packet);

				if(first){
				    std::cout << "Primo ricevuto" << std::endl;
				    first = false;
				    std::string printed =
				        std::string("PACKET:").append("\n\tCRC Flag: ").append(
					std::to_string(packet.type)).append(
					"\n\tSubType: ").append(
					std::to_string(packet.subType)).append(
					"\n\tTelescopeID: ").append(
					std::to_string(packet.telescopeID)).append(
					"\n\tSSC: ").append(std::to_string(packet.ssc));
				    std::cout << printed << std::endl;
				}
			
				if (count % 5000 == 0) {
				    	std::cout << "Tot Packets : " << count << std::endl;
				}
			}
		} else {
			std::cout << "NO MESSAGE" << std::endl;
		}
	
	}
	if(msg) consumer.commit(msg);
	auto end = std::chrono::steady_clock::now(); //end timer

	unsigned int elps = std::chrono::duration_cast
			< std::chrono::microseconds > (end - start).count();

	std::cout << "Elapsed time in seconds: " << elps/pow(10,6) << " s" << std::endl;
	std::cout << "Tot Packets : " << count << std::endl;
	std::cout << "Packets to second : " << (count/(elps/pow(10,6))) << std::endl;
}
