import sys
from edu.uoregon.tau.perfdmf import Trial
from rules import RuleHarness
from glue import PerformanceResult
from glue import Utilities
from glue import TrialResult
from glue import BasicStatisticsOperation
from glue import AbstractResult
from glue import ExtractNonCallpathEventOperation
from glue import ExtractEventOperation
from glue import DeriveMetricOperation
from glue import ScaleMetricOperation
from glue import MergeTrialsOperation
from glue import DerivedMetrics
from glue import TopXEvents
from glue import MeanEventFact

###################################################################

True = 1
False = 0
mainEvent = ""

###################################################################

def deriveMetric(input, first, second, oper):
	# derive the metric
	derivor = DeriveMetricOperation(input, first, second, oper)
	derived = derivor.processData().get(0)
	newName = derived.getMetrics().toArray()[0]
	# merge new metric with the trial
	merger = MergeTrialsOperation(input)
	merger.addInput(derived)
	merged = merger.processData().get(0)
	#print "new metric: " + newName
	return merged, newName

def scaleMetric(input, metric, value, oper):
	# derive the metric
	scaler = ScaleMetricOperation(input, metric, value, oper)
	scaled = scaler.processData().get(0)
	newName = scaled.getMetrics().toArray()[0]
	# merge new metric with the trial
	merger = MergeTrialsOperation(input)
	merger.addInput(scaled)
	merged = merger.processData().get(0)
	#print "new metric: " + newName
	return merged, newName

###################################################################

def getPowerModel(input):
	# set some metric names
	totalCycles = "PAPI_TOT_CYC"
	totalInstructions = "PAPI_TOT_INS"
	L1references = "PAPI_L1_TCA"
	L2references = "PAPI_L2_TCA"
	L3references = "PAPI_L3_TCA"
	global mainEvent

	# derive the CPU Power term
	input, CPUPower = deriveMetric(input, totalInstructions, totalCycles, DeriveMetricOperation.DIVIDE)
	input, CPUPower = scaleMetric(input, CPUPower, (0.0459 * 122), ScaleMetricOperation.MULTIPLY)
	# derive the L1 Power term
	input, L1Power = deriveMetric(input, L1references, totalCycles, DeriveMetricOperation.DIVIDE)
	input, L1Power = scaleMetric(input, L1Power, (0.0017 * 122), ScaleMetricOperation.MULTIPLY)
	# derive the L2 Power term
	input, L2Power = deriveMetric(input, L2references, totalCycles, DeriveMetricOperation.DIVIDE)
	input, L2Power = scaleMetric(input, L2Power, (0.0171 * 122), ScaleMetricOperation.MULTIPLY)
	# derive the L3 Power term
	input, L3Power = deriveMetric(input, L3references, totalCycles, DeriveMetricOperation.DIVIDE)
	input, L3Power = scaleMetric(input, L3Power, (0.935 * 122), ScaleMetricOperation.MULTIPLY)

	# sum them all up!
	input, PowerPerProc = deriveMetric(input, CPUPower, L1Power, DeriveMetricOperation.ADD)
	input, PowerPerProc = deriveMetric(input, PowerPerProc, L2Power, DeriveMetricOperation.ADD)
	input, PowerPerProc = deriveMetric(input, PowerPerProc, L3Power, DeriveMetricOperation.ADD)
	input, PowerPerProc = scaleMetric(input, PowerPerProc, 97.66, ScaleMetricOperation.ADD)

	# return the trial, and the new derived metric name
	return input, PowerPerProc

def getEnergy(input, PowerPerProc):
	global mainEvent
	time = "LINUX_TIMERS"

	# convert time to seconds
	input, seconds = scaleMetric(input, time, (1/1000000.0), ScaleMetricOperation.MULTIPLY)
	input, joules = deriveMetric(input, PowerPerProc, seconds, DeriveMetricOperation.MULTIPLY)

	# return the trial, and the new derived metric name
	return input, joules

def getFlopsPerJoule(input, EnergyPerProc):
	global mainEvent
	fpOps = "PAPI_FP_OPS"

	input, flopsPerJoule = deriveMetric(input, fpOps, EnergyPerProc, DeriveMetricOperation.DIVIDE)

	# return the trial, and the new derived metric name
	return input, flopsPerJoule

def getIPC(input):
	global mainEvent
	totalInstructions = "PAPI_TOT_INS"
	totalCycles = "PAPI_TOT_CYC"

	input, IPC = deriveMetric(input, totalInstructions, totalCycles, DeriveMetricOperation.DIVIDE)

	# return the trial, and the new derived metric name
	return input, IPC

def getIssuedPerCycle(input):
	global mainEvent
	instructionsIssued = "PAPI_TOT_IIS"
	totalCycles = "PAPI_TOT_CYC"

	input, issuedPerCycle = deriveMetric(input, instructionsIssued, totalCycles, DeriveMetricOperation.DIVIDE)

	# return the trial, and the new derived metric name
	return input, issuedPerCycle

###################################################################

def main():
	global mainEvent
	global True
	global False
	
	print "--------------- JPython test script start ------------"
	print "--- Calculating Power Models --- "

	# create a rulebase for processing
	#print "Loading Rules..."
	#ruleHarness = RuleHarness.useGlobalRules("openuh/OpenUHRules.drl")

	# load the trial
	print "loading the data..."

	# check to see if the user has selected a trial
	tmp = Utilities.getCurrentTrial()
	if tmp != None:
		trial = TrialResult(tmp)
		print 
	else:
		# remove these two lines to bypass this and use the default trial
		print "No trial selected - script exiting"
		return

	# extract the non-callpath events from the trial
	print "extracting non-callpath..."
	extractor = ExtractNonCallpathEventOperation(trial)
	extracted = extractor.processData().get(0)

	# get basic statistics
	print "computing mean..."
	statMaker = BasicStatisticsOperation(extracted, False)
	stats = statMaker.processData()
	means = stats.get(BasicStatisticsOperation.MEAN)

	# get main event
	mainEvent = means.getMainEvent()
	print "Main Event: ", mainEvent

	# calculate all derived metrics
	print
	print "Deriving power metric..."
	derived, PowerPerProc = getPowerModel(means)

	# get the top 10 events
	top10er = TopXEvents(derived, derived.getTimeMetric(), AbstractResult.EXCLUSIVE, 10)
	top10 = top10er.processData().get(0)

	# just one thread
	thread = 0

	# iterate over events, output inefficiency derived metric
	print
	print "Top 10 Average", PowerPerProc, "values per thread for this trial:"
	for event in top10er.getSortedEventNames():
		print event, derived.getExclusive(thread, event, PowerPerProc)
	print
	print mainEvent, "INCLUSIVE: ", derived.getInclusive(thread, mainEvent, PowerPerProc)
	print

	# compute the energy consumed by each event
	print "Computing joules consumed..."
	derived, EnergyPerProc = getEnergy(derived, PowerPerProc)

	# iterate over events, output inefficiency derived metric
	print
	print "Top 10 Average", EnergyPerProc, "values per thread for this trial:"
	for event in top10er.getSortedEventNames():
		print event, derived.getExclusive(thread, event, EnergyPerProc)
	print
	print mainEvent, "INCLUSIVE: ", derived.getInclusive(thread, mainEvent, EnergyPerProc)
	print

	# compute the floating point operations per joule per event
	print "Computing FP_OPS/joule..."
	derived, FlopsPerJoule = getFlopsPerJoule(derived, EnergyPerProc)

	# iterate over events, output inefficiency derived metric
	print
	print "Top 10 Average", FlopsPerJoule, "values per thread for this trial:"
	for event in top10er.getSortedEventNames():
		print event, derived.getExclusive(thread, event, FlopsPerJoule)
	print
	print mainEvent, "INCLUSIVE: ", derived.getInclusive(thread, mainEvent, FlopsPerJoule)
	print

	# compute the floating point operations per joule per event
	print "Computing Instructions Per Cycle..."
	derived, IPC = getIPC(derived)

	# iterate over events, output inefficiency derived metric
	print
	print "Top 10 Average", IPC, "values per thread for this trial:"
	for event in top10er.getSortedEventNames():
		print event, derived.getExclusive(thread, event, IPC)
	print
	print mainEvent, "INCLUSIVE: ", derived.getInclusive(thread, mainEvent, IPC)
	print mainEvent, "INCLUSIVE: ", derived.getInclusive(thread, mainEvent, "PAPI_TOT_INS")
	print

	# compute the floating point operations per joule per event
	print "Computing Issued Per Cycle..."
	derived, issuedPerCycle = getIssuedPerCycle(derived)

	# iterate over events, output inefficiency derived metric
	print
	print "Top 10 Average", issuedPerCycle, "values per thread for this trial:"
	for event in top10er.getSortedEventNames():
		print event, derived.getExclusive(thread, event, issuedPerCycle)
	print

	print mainEvent, "INCLUSIVE: ", derived.getInclusive(thread, mainEvent, issuedPerCycle)
	print mainEvent, "INCLUSIVE: ", derived.getInclusive(thread, mainEvent, "PAPI_TOT_IIS")
	print

	print "Time to completion..."
	print
	for event in top10er.getSortedEventNames():
		print event, derived.getExclusive(thread, event, derived.getTimeMetric())/1000000
	print

	print mainEvent, "INCLUSIVE: ", derived.getInclusive(thread, mainEvent, derived.getTimeMetric())/1000000

	# process the rules
	#RuleHarness.getInstance().processRules()

	print "---------------- JPython test script end -------------"

if __name__ == "__main__":
	main()
