#pragma once

extern std::vector<uint16_t> melody_steps;
void sendToApp(String jsonMessage);

void PublishMqtt(const char * data);
bool addMelody(JsonVariant doc);

class Player {
public:
	uint16_t id;						  							// The (internal) ID of the selected melody. Not specificly used anywhere atm. Might be used later.
	std::string name = "melody1";      			// Name of the Melody saved. Will be used to read the file: /name.bin
	std::string uid = "x";									// The UID of the melody, from Firestore
	std::string url = "-";									// The URL of the binary, from Firestore
  uint16_t noteAssignments[16] = {0}; 		// The list of Note Assignments for the melody
	uint16_t speed = 500;               		// Time to wait per beat. (In Miliseconds)
  uint32_t segment_duration = 15000;  		// Duration of the playback per segment (per "loop")
	uint32_t pause_duration = 0;						// Duration of the pauses between segments (loops)
	uint32_t total_duration = 0;        		// The Total Run Duration of the program. Including all loops
	uint64_t segmentCmpltTime = 0;					// The time of completion of the last Segment
	uint64_t segmentStartTime = 0;					// The time-point the current segment started Playing
	uint64_t startTime = 0;									// The time-point the Melody started Playing
	uint64_t pauseTime = 0;									// The time-point the melody paused
	bool continuous_loop = false;						// Indicates if the Loop should Run 1-Take or Continuous with Intervals
	bool infinite_play = false;         		// Infinite Loop Indicator (If True the melody will loop forever or until stoped, with pauses of "interval" in between loops)
	bool isPlaying = false;									// Indicates if the Melody is actually Playing right now.
	bool isPaused = false;									// If playing, indicates if the Melody is Paused
	bool hardStop = false;									// Flags a hardstop, immediately.


	void play() {
		isPlaying = true;
		hardStop = false;
		startTime = segmentStartTime = millis();
		LOG_DEBUG("Plbck: PLAY");
	}

	void forceStop() {
		hardStop = true;
		isPlaying = false;
		LOG_DEBUG("Plbck: FORCE STOP");
	}

	void stop() {
		hardStop = false;
		isPlaying = false;
		LOG_DEBUG("Plbck: STOP");

		StaticJsonDocument<128> doc;
		doc["status"] = "NOTIFY";
		doc["type"] = "playback";
		doc["payload"] = "stop";

		String output;
		serializeJson(doc, output);
		sendToApp(output);

	}

	void pause() {
		isPaused = true;
		LOG_DEBUG("Plbck: PAUSE");
	}

	void unpause() {
		isPaused = false;
		segmentStartTime = millis();
		LOG_DEBUG("Plbck: RESUME");
	}

	// Handles Incoming Commands to PLAY or STOP
	void command(JsonVariant data, AsyncWebSocketClient *client = nullptr){

		setMelodyAttributes(data);
		loadMelodyInRAM(melody_steps);

		String action = data["action"];
    LOG_DEBUG("Incoming Command: %s", action);

		// Play or Stop Logic
    if (action == "play") {
      play();
    } else if (action == "stop") {
      forceStop();
    } else {
			return;
		}

		// Prepare JSON response
	  StaticJsonDocument<128> response;
	  response["status"] = "OK";
	  response["type"] = action;         // "play" or "stop"
	  response["payload"] = nullptr;     // Use null in JSON

	  // Serialize JSON to char buffer
	  char jsonOut[256];
	  serializeJson(response, jsonOut);

	  // Send via MQTT
	  PublishMqtt(jsonOut);

	  // Optionally send via WebSocket
	  if (client) {
	    client->text(jsonOut);
	  }

  }

	// Sets incoming Attributes for the Melody, into the class' variables.
	void setMelodyAttributes(JsonVariant doc){

	  	if (doc.containsKey("name")) {
	  			name = doc["name"].as<const char*>();
	  	}
			if (doc.containsKey("uid")) {
					uid = doc["uid"].as<const char*>();
			}
			if (doc.containsKey("url")) {
					url = doc["url"].as<const char*>();
			}
			if (doc.containsKey("speed")) {
					speed = doc["speed"].as<uint16_t>();
			}
			if (doc.containsKey("note_assignments")) {
			    JsonArray noteArray = doc["note_assignments"];
			    size_t arraySize = min(noteArray.size(), (size_t)16);
			    for (size_t i = 0; i < arraySize; i++) {
			        noteAssignments[i] = noteArray[i];
			    }
			}
			if (doc.containsKey("segment_duration")) {
	  			segment_duration = doc["segment_duration"].as<uint32_t>();
	  	}
			if (doc.containsKey("pause_duration")) {
	  			pause_duration = doc["pause_duration"].as<uint32_t>();
	  	}
			if (doc.containsKey("total_duration")) {
					total_duration = doc["total_duration"].as<uint32_t>();
			}
	  	if (doc.containsKey("continuous_loop")) {
	  			continuous_loop = doc["continuous_loop"].as<bool>();
	  	}

			if (continuous_loop && total_duration == 0){
				infinite_play = true;
			}

			if (!continuous_loop) {
				total_duration = segment_duration;
			}

			// Print Just for Debugging Purposes
	  	LOG_DEBUG("Set Melody Vars / Name: %s, UID: %s",
	  		name.c_str(),
	  		uid.c_str()
	  		);
			// Print Just for Debugging Purposes
		  LOG_DEBUG("URL: %s",
				url.c_str()
		  	);
			// Print Just for Debugging Purposes
			 LOG_DEBUG("Speed: %d, Per Segment Duration: %lu, Pause Duration: %lu, Total Duration:  %d, Continuous: %s, Infinite: %s",
			  speed,
			  segment_duration,
			  pause_duration,
			  total_duration,
			  continuous_loop ? "true" : "false",
				infinite_play ? "true" : "false"
			  );

	}


  // Loads the Selected melody from a .bin file on SD into RAM
  void loadMelodyInRAM(std::vector<uint16_t> &melody_steps) {
    String filePath = "/melodies/" + String(uid.c_str());

    File bin_file = SD.open(filePath.c_str(), FILE_READ);
    if (!bin_file) {
        LOG_ERROR("Failed to open file: %s", filePath.c_str());
				LOG_ERROR("Check Servers for the File...");
				StaticJsonDocument<128> doc;
				doc["download_url"] = url;
				doc["melodys_uid"] = uid;
				if (!addMelody(doc)){
					LOG_ERROR("Failed to Download File. Check Internet Connection");
					return;
				} else {
					bin_file = SD.open(filePath.c_str(), FILE_READ);
				}
    }

    size_t fileSize = bin_file.size();
    if (fileSize % 2 != 0) {
        LOG_ERROR("Invalid file size: %u (not a multiple of 2)", fileSize);
        bin_file.close();
        return;
    }

    melody_steps.resize(fileSize / 2);

    for (size_t i = 0; i < melody_steps.size(); i++) {
        uint8_t high = bin_file.read();
        uint8_t low = bin_file.read();
        melody_steps[i] = (high << 8) | low;
    }

    LOG_INFO("Melody Load Successful");

    bin_file.close();
  }

};
