#!/usr/bin/env python

# Imports
import sys
import csv
import os

# Search commands
from splunklib.searchcommands import dispatch, ReportingCommand, Configuration, Option, validators

# Identify the bit where a difference in characters first occurs in two strings
def findBit(str1, str2):

	# Loop each character in str1 and str2
	for i, (c1, c2) in enumerate(zip(str1, str2)):

		# If it's different, we found our flipped character
		if c1 != c2:

			# Identify the bit where the flip occured
			return '{:08b}'.format(ord(c1) ^ ord(c2)).index('1')

	# Return not found
	return -1  

@Configuration()
class BitCommand(ReportingCommand):

	# Reduce the results to a nice result set
	def reduce(self, records):
		
		# Store an object for each qNameUnflipped and within it the bits
		flips = {}
		probs = {}

		# Get the path to the lookup file
		lookup_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "lookups", "bf_dns_qName.csv")

		# Open the lookup for reading
		with open(lookup_path) as f:

			# Parse it using a DictReader
			lookup = csv.DictReader(f)

			# For each row in the lookups
			for row in lookup:

				# Add the key if needed to both probs and flips
				if row["qNameUnflipped"] not in probs:
					probs[row["qNameUnflipped"]] = [0, 0, 0, 0, 0, 0, 0, 0]
					flips[row["qNameUnflipped"]] = [0, 0, 0, 0, 0, 0, 0, 0]

				# Lookup the bit where the error occured and increment the count for it
				probs[row["qNameUnflipped"]][findBit(row["qName"][1:], row["qNameUnflipped"])] += 1

		# Loop each record
		for record in records:

			# Identify which bit the flip occured in and increment that count for the given qNameUnflipped
			flips[record["qNameUnflipped"]][findBit(record["qNameUnflipped"], record["domain"])] += int(record["count"])

		# Loop each flips for the reporting
		for qNameUnflipped, bits in flips.iteritems():

			# Loop each bit for the qNameUnflipped
			for i, count in enumerate(bits):

				# Yield a record
				yield { "qNameUnflipped": qNameUnflipped, "bit": i, "count": count, "possibilities": probs[qNameUnflipped][i], "totalPossibilities": sum(probs[qNameUnflipped]) }

# Tell splunk we exist
dispatch(BitCommand, sys.argv, sys.stdin, sys.stdout, __name__)