//
// 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/.
//

#include "BatteryPhyLayer.h"
#include "NicEntry.h"
#include "Distance.h"

Define_Module(BatteryPhyLayer);

void BatteryPhyLayer::initialize(int stage) {
	PhyLayer::initialize(stage);

	if(stage==0)
	{
		transmissions = 0;

		txEnergy = par("txEnergy");
		rxEnergy = par("rxEnergy");
		amplifierEnergy = par("amplifierEnergy");

		EV<< "tx: " << txEnergy << " joules, rx: " << rxEnergy << " joules, amplifier: " << amplifierEnergy << " joules" << endl;
	}

	//if(stage==1 && !(this->findHost()->par("isBS")))
	if(stage==1)
		registerWithBattery("PhyLayer", 2);
}

void BatteryPhyLayer::handleUpperMessage(cMessage* msg) {
	if (utility->getHostState().get() == HostState::FAILED || utility->getHostState().get() == HostState::SLEEP) {
		EV<< "host has FAILED or SLEEP, dropping msg " << msg->getName() << endl;
		delete msg;
		return;
	}

	// check if Radio is in TX state
	if (radio->getCurrentState() != Radio::TX)
	{
        delete msg;
        msg = 0;
		opp_error("Error: message for sending received, but radio not in state TX");
	}

	// check if not already sending
	if(txOverTimer->isScheduled())
	{
        delete msg;
        msg = 0;
		opp_error("Error: message for sending received, but radio already sending");
	}

	// build the AirFrame to send
	assert(dynamic_cast<cPacket*>(msg) != 0);

	AirFrame* frame = encapsMsg(static_cast<cPacket*>(msg));

	// make sure there is no self message of kind TX_OVER scheduled
	// and schedule the actual one
	assert (!txOverTimer->isScheduled());
	sendSelfMessage(txOverTimer, simTime() + frame->getDuration());

	//Drawing energy... Equation from LEACH definition
	int k = frame->getBitLength();
	int src = (static_cast<MacPkt*>(msg))->getSrcAddr();
	int dest = (static_cast<MacPkt*>(msg))->getDestAddr();
	double d;

	switch(dest)
	{
	case L2BS:
		d = Distance::getDistanceBSByMacAddr(src);
		break;

	case L2BROADCAST:
		d = Distance::getAverageDistanceMacAddr(src);
		break;

	default:
		d = Distance::getDistanceByMacAddr(src, dest);
		break;
	}

	double energyValue = txEnergy * k + amplifierEnergy * k * (d*d);

	drawEnergy(energyValue, TX_ACCT);

	transmissions++;
	sendMessageDown(frame);
	//TODO: Implementation for direct transmission tests. Please remove line 102 when finished
	switchHostState(HostState::SLEEP);

}

void BatteryPhyLayer::handleAirFrame(cMessage* msg) {
	int myState = utility->getHostState().get();
	if ( myState == HostState::FAILED || myState == HostState::SLEEP) {
		EV<< "host has FAILED or SLEEP, dropping msg " << msg->getName() << endl;
		delete msg;
		return;
	}

	PhyLayer::handleAirFrame(msg);
}

void BatteryPhyLayer::handleHostState(const HostState& state) {
	// handles only battery consumption

	HostState::States hostState = state.get();

	switch (hostState) {
	case HostState::FAILED:
		EV<< "t = " << simTime() << " host state FAILED" << endl;
		// it would be good to create a radioState OFF, as well
		break;
	default:
		break;
	}
}

void BatteryPhyLayer::sendUp(AirFrame* frame, DeciderResult* result) {

	//Drawing energy... Equation from LEACH definition
	drawEnergy(rxEnergy * frame->getBitLength(), RX_ACCT);


	PhyLayer::sendUp(frame, result);
}


void BatteryPhyLayer::finish()
{
	recordScalar("transmissions", transmissions);
}







