#!/usr/bin/env python

import Tkinter
import os
import re
import sys

def tryNet(netinfo):
	script = ('sudo iwconfig eth1 ap %(ap)s;'
		'sudo iwconfig essid "%(essid)s";'
		'sudo dhclient eth1')%{
			"ap": netinfo.apMac,
			"essid": netinfo.essid,
		}
	os.system("xterm -e '%s'"%script)

class NetView(Tkinter.Frame):
	def __init__(self, master, info, barWidth, *args, **kwargs):
		Tkinter.Frame.__init__(self, master, *args, **kwargs)
		self._makeWidgets(master, info)
		self.barWidth = barWidth
	
	def _makeWidgets(self, master, info):
		if info.encFlag:
			bgColor = bgcolor="#FF0000"
		else:
			bgColor = bgcolor="#00ff00"
		self.levelWidget = Tkinter.Label(master, bitmap="gray12",
			background=bgColor)
		self.ssidWidget = Tkinter.Label(master, text=info.essid)

	def getWidgets(self):
		return self.ssidWidget, self.levelWidget

	def update(self, info):
		relBeacon = 1-min(1, max(0, (time.time()-info.lastBeacon)/10))
		txCol = "#"+("%02x"%int(255-relBeacon*255))*3
		self.ssidWidget.configure(foreground=txCol)
		if relBeacon==0:
			self.levelWidget.configure(width=0)
			self.levelWidget.configure(text="")
		else:
			self.levelWidget.configure(width=int(relBeacon*self.barWidth))


class ScanView(Tkinter.Tk):
	def __init__(self):
		Tkinter.Tk.__init__(self)
		self.labelWidth = 200
		self.barWidth = 100
		self.totalWidth = self.labelWidth+self.barWidth
		self.cellHeight = 20
		self.geometry("%dx%d"%(self.totalWidth, self.cellHeight))
		self.infos = {}
		self.updateFunc = None
		self.curRow = 0
		self.after(1000, self._timerFunc)
		self.bind("q", lambda ev: self.quit())
	
	def addNet(self, netinfo):
		newInfo = NetView(self, netinfo, self.barWidth)
		ssidWidget, levelWidget = newInfo.getWidgets()
		levelWidget.place(x=self.labelWidth, y=self.curRow*self.cellHeight)
		ssidWidget.place(x=0, y=self.curRow*self.cellHeight)
		ssidWidget.bind("<Button-3>", lambda ev: tryNet(netinfo))
		self.curRow += 1
		self.geometry("%dx%d"%(self.totalWidth, self.cellHeight*self.curRow))
		self.infos[netinfo] = newInfo
	
	def updateNet(self, netinfo):
		self.infos[netinfo].update(netinfo)

	def setUpdateFunc(self, updateFunc):
		self.updateFunc = updateFunc

	def _timerFunc(self):
		try:
			if self.updateFunc:
				self.updateFunc()
		finally:
			self.after(500, self._timerFunc)


class DumpView:
	def addNet(self, net):
		print "New net:", net.apMac, net.essid
	
	def updateNet(self, net):
		print "Values: ", net.quality, net.beacon


class NetInfo:
	def __init__(self, apMac, essid, encFlag, quality, beacon):
		self.apMac, self.essid, self.encFlag, self.quality, self.beacon = \
			apMac, essid, encFlag, quality, beacon
		self.ping(beacon)

	def ping(self, dt):
		if isinstance(dt, NetInfo):
			self.lastBeacon  = dt.lastBeacon
		else:
			self.lastBeacon = time.time()-int(dt)/1000.

	def __hash__(self):
		return hash(self.apMac)

	def __cmp__(self, other):
		return cmp(self.apMac, other.apMac)


class WlanModel:
	def __init__(self, view):
		self.netInfos = {}
		self.view = view
		self.cellRe = re.compile("Cell \d\d - ")
		self.blockRe = re.compile(
			r'(?s)Address:\s*([0-9a-fA-F:]*).*ESSID:"?([^"]*)'
			r'.*Encryption key:(\w*).*Quality[=:](\d+).*'
			r'Last beacon: (\d+)')

	def _updateNetInfos(self, curNetInfos):
		for curNet in curNetInfos:
			if not curNet in self.netInfos:
				self.netInfos[curNet] = curNet
				self.view.addNet(curNet)
			self.netInfos[curNet].ping(curNet)
		for net in self.netInfos:
			self.view.updateNet(net)

	def _parseBlock(self, rawBlock):
		mat = self.blockRe.search(rawBlock)
		if not mat:
			print ">>>> Bad scan result:\n", repr(rawBlock)
			return None
		return (mat.group(1), mat.group(2), mat.group(3)=="on", int(mat.group(4)),
			int(mat.group(5)))

	def _parse(self, scanOutput):
		for block in self.cellRe.split(scanOutput)[1:]:
			yield self._parseBlock(block)

	def update(self):
		curNetInfos = set()
		nets = os.popen("iwlist eth1 scan 2>/dev/null").read()
#		nets = open("/tmp/fake").read()
		for netPars in self._parse(nets):
			if netPars:
				curNetInfos.add(NetInfo(*netPars))
	 	self._updateNetInfos(curNetInfos)


if __name__=="__main__":
	import time
	view = ScanView()
	w = WlanModel(view)
	view.setUpdateFunc(w.update)
	view.mainloop()
