//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see http://www.gnu.org/licenses/.
//

#ifndef LEACH_H_
#define LEACH_H_

#include <BaseNetwLayer.h>

#include "BaseArp.h"
#include "NetwPkt_m.h"
#include <SimpleAddress.h>
#include "ExtendedAddress.h"
//------------------------
#include "BaseNetwLayer.h"
#include "NetwControlInfo.h"
#include "MacControlInfo.h"
#include "BaseMacLayer.h"

#include <cassert>

class LEACH: public BaseNetwLayer {
public:
	enum BaseNetwMessageKinds {
		CH_STATUS_BROADCAST = LAST_BASE_NETW_MESSAGE_KIND,
		JOIN_CH,
		TDMA_SCHEDULE,
	};
	enum BaseNetwControlKinds {
		PACKET_DROPPED = LAST_BASE_NETW_CONTROL_KIND,
	};

	//Timers to go to different phases
	enum SelfMessages {
		TIMER_NEW_ROUND,
		TIMER_JOIN_CH,
		TIMER_CREATE_TDMA_SCHEDULE,
		TIMER_SEND_DATA,
	};

	//LEACH phases
	enum Phases {
		ADVERTISEMENT,
		CLUSTER_SETUP,
		SCHEDULE_CREATION,
		DATA_TRANSMISSION,
	};


protected:
    /**
     * @brief Length of the NetwPkt header
     * Read from omnetpp.ini
     **/
    int headerLength;

    /** @brief Pointer to the arp module*/
    BaseArp* arp;

    /** @brief cached variable of my networ address */
    int myNetwAddr;

    //------------------------------------
    //	LEACH variables
    //------------------------------------

	/** @brief Percentage of Cluster Heads */
    double P;

    /** @brief Round number */
    int currentRound;

    /** @brief Last round when the node was Cluster Head (-1 indicates 'never') */
    int lastRoundCH;

    /** @brief Time duration of a LEACH round (in seconds) */
    double roundTime;

    /** @brief Time duration of a packet transmission slot for Non-CHs (in seconds) */
    double slotTime;

    /** @brief Percentage of data compression for cluster heads*/
    double compressionIndex;

    /** @brief Maximum time to receive packets before going to the next phase */
    double waitingTime;

    /** @brief Maximum number of cluster members */
    int MAX_CLUSTER_SIZE;

    /** @brief Index of the current phase*/
    int currentPhase;

    /** @brief Cluster Head address of 'this' node */
    int myCH;

    /** @brief Pointer to the next packet to send to the BS */
    NetwPkt* packetToSend;

    /** @brief For Non-CHs: Map with the nearby Cluster Heads and distance to them */
    typedef std::map<int, double> DistanceToCHsMap;
    DistanceToCHsMap distanceToCHs;

    /** @brief For CHs: Addresses list of cluster members */
    typedef std::list<int> MembersCHList;
    MembersCHList membersCH; //stores source addresses of cluster members

    /** @brief For CHs: List of packets to send to the BS during current round */
    typedef std::list<NetwPkt*> NetwQueueList;
    NetwQueueList netwQueue;

    /** @brief Timer to go to the next round */
    cMessage* timerNextRound;

    /** @brief Timer to go to the next stage */
    cMessage* timerNextStage;

    /** @brief Number of MAC packets dropped */
    int droppedMsgs;


public:

    /** @brief Initialization of the module and some variables*/
    virtual void initialize(int);

    /** @brief Record some values */
    virtual void finish();


protected:

	/** @brief Handle self messages */
	void handleSelfMsg(cMessage* msg);

    /** @brief Handle messages from upper layer */
    virtual void handleUpperMsg(cMessage* msg);

	/** @brief Handle messages from lower layer */
	void handleLowerMsg(cMessage* msg);

    /** @brief Encapsulate higher layer packet into an NetwPkt*/
    virtual NetwPkt* encapsMsg(cPacket*);

    /** @brief handleHostState does nothing */
    void handleHostState(const HostState& state) {}

    /** @brief Handle control messages from lower layer */
    virtual void handleLowerControl(cMessage* msg);


private:

	/** @brief Calculates the threshold value to be Cluster Head */
	double calculateThreshold();

	/** @brief Executes the Advertisement Phase steps */
	void advertisementPhase();

	/** @brief Executes the Cluster Set-Up Phase steps */
	void clusterSetUpPhase();

	/** @brief Executes the Schedule Creation Phase steps */
	void scheduleCreation();

	/** @brief Executes the Data Transmission Phase steps */
	void dataTransmission();

	/** @brief Sets the MAC transmission power to high range*/
	void setHighTxPower();

	/** @brief Sets the MAC transmission power to low range*/
	void setLowTxPower();
};


#endif /* LEACH_H_ */
