#!/usr/bin/env python

import os
import re
import sys
from Tkinter import *

# Ghostscript options:
margin = 10
resolution = 90

if sys.platform[:3] == 'win':
    # EDIT THIS FOR THE CORRECT PATHS:
    MAPRGB = r'"C:\Program Files\RuG-L04\bin\maprgb.exe"'
    GS = r'"C:\Program Files\gs\gs8.54\bin\gswin32c.exe"'
    fixed = 'courier 10'
else:
    MAPRGB = r'maprgb'
    GS = r'gs'
    fixed = 'courier 10'

if len(sys.argv) != 3:
    print >> sys.stderr, '''
Usage: %s config_file difference_file
''' % os.path.basename(sys.argv[0])
    sys.exit()

def getline(fp):
    while True:
        i = fp.readline()
        if not i:
            return False
        i = i.strip()
        if i and i[0] != '#':
            return i

def unquote(s):
    if s[0] == '"' and s[-1] == '"':
        s = s[1:-1]
        s = re.sub(r'\\(.)', r'\1', s)
    return s

colorlines1 = '''
1 1 0.9979133
1 1 0.951178
1 1 0.904911
1 1 0.8595787
0.9936441 0.9975165 0.8157186
0.9766634 0.9908928 0.7751566
0.9537174 0.9819685 0.7408325
0.9261637 0.971293 0.7157217
0.8940859 0.9588669 0.7015761
0.853902 0.9431143 0.6966288
0.8013755 0.9221774 0.698484
0.7328502 0.8944876 0.7048092
0.6514241 0.8618412 0.7140098
0.5645519 0.8282048 0.7249673
0.4797463 0.797567 0.7365706
0.4021244 0.7716024 0.7476638
0.3317804 0.7471322 0.7569964
0.268176 0.7203667 0.7633064
0.2111159 0.6878304 0.76541
0.1631302 0.6485423 0.7627474
0.1280489 0.6027108 0.7550558
0.1096479 0.5505687 0.7420829
0.1079946 0.4933524 0.7240725
0.1174074 0.4338541 0.7020381
0.1317104 0.3750003 0.6770591
0.1449949 0.3195292 0.6498102
0.1528481 0.2691288 0.6187012
0.1513770 0.2251213 0.5813547
0.1368118 0.1887735 0.5354694
0.1090943 0.1596755 0.481082
0.07245721 0.1354784 0.4209323
0.03137255 0.1137255 0.3579104
'''

colorlines2 = '''
1.00000000 1.0000000 1.0000000
1.00000000 1.0000000 0.8509804
0.92941176 0.9725490 0.6941176
0.78039216 0.9137255 0.7058824
0.49803922 0.8039216 0.7333333
0.25490196 0.7137255 0.7686275
0.11372549 0.5686275 0.7529412
0.13333333 0.3686275 0.6588235
0.14509804 0.2039216 0.5803922
0.03137255 0.1137255 0.3450980
'''

colors1 = []
for i in colorlines1.split('\n'):
    i = i.strip()
    if i:
        colors1.append(i)
n_colors1 = len(colors1)

colors2 = []
for i in colorlines2.split('\n'):
    i = i.strip()
    if i:
        colors2.append(i)
n_colors2 = len(colors2)

colors = colors1
n_colors = n_colors1

configfile = sys.argv[1]
infile = sys.argv[2]
tmpfile = 'maprefs.tmp'
tmp2file = 'maprefs2.tmp'

fp = open(configfile, 'r')
for line in fp:
    if line[:12] == 'boundingbox:':
        x1, y1, x2, y2 = [int(i) for i in line[12:].split()]
        x1 -= margin
        y1 -= margin
        x2 += margin
        y2 += margin
    elif line[:7] == 'labels:':
        lblfile = line[7:].strip()
fp.close()

lblidx = {}
n_locations = 0
fp = open(lblfile, 'r')
while True:
    line = getline(fp)
    if not line:
        break
    m = re.match(r'(\d+)\s+(.*)',line)
    i = int(m.group(1))
    lblidx[unquote(m.group(2))] = i - 1
    if i > n_locations:
        n_locations = i
fp.close()
used = [False for i in range(n_locations)]
lbls = ['' for i in range(n_locations)]
for k in lblidx:
    lbls[lblidx[k]] = k

idxs = []
fp = open(infile, 'r')
n_loc = int(getline(fp))
for i in range(n_loc):
    j = lblidx[unquote(getline(fp))]
    idxs.append(j)
    used[j] = True

diffs = [None for i in range(n_locations)]
for i in range(n_locations):
    diffs[i] = [None for j in range(n_locations)]

for i in range(n_loc):
    ii = idxs[i]
    for j in range(i):
        jj = idxs[j]
        diffs[ii][jj] = diffs[jj][ii] = float(getline(fp))

fp.close()

fp = open(tmpfile, 'w')
fp.write('3\n')
for i in range(n_locations):
    if used[i]:
        fp.write(lbls[i] + '\n1\n0\n0\n')
fp.close()

Head = ['initgraphics %i %i translate\n' % (-x1, -y1)]
PP = []
PPpat = []
Tail = []

os.system('%s -r -o %s %s %s' % (MAPRGB, tmp2file, configfile, tmpfile))
fp = open(tmp2file, 'r')
state = 1
for line in fp:
    l = line.strip()
    if not l or l[0] == '%':
        continue
    if state == 1:
        Head.append(line)
        if line[:3] == '/PP':
            state = 2
    elif state == 2:
        if line[:5] == '] def':
            state = 3
            Tail.append(line)
        else:
            PP.append(line)
            PPpat.append(re.sub(r'(\S+\s+\S+\s+\S+\s+Ord\s+\]|0 0 0 \])', '%s Ord ]', line))
    elif state == 3:
        if line[:8] != 'showpage':
            Tail.append(line)
fp.close()
Tail.append('flushpage\n')
os.remove(tmpfile)
os.remove(tmp2file)

fpgs = os.popen('%s -g%ix%i -r%i -q -' % (
    GS,
    int((x2 - x1) / 72.0 * resolution + 1),
    int((y2 - y1) / 72.0 * resolution + 1),
    resolution), 'w')

used_locations = [i for i in range(n_locations) if used[i]]

def paintit(i):
    global idx
    idx = i
    v = [diffs[idx][i] for i in used_locations if i != idx]
    maximum = max(v) ** power
    if min0:
        minimum = 0
    else:
        minimum = min(v) ** power
    d = maximum - minimum

    for i in used_locations:
        if i == idx:
            PP[i] = PPpat[i] % '1 0 0'
        else:
            c = int((diffs[idx][i] ** power - minimum) / d * n_colors)
            if c >= n_colors:
                c = n_colors - 1
            PP[i] = PPpat[i] % colors[c]

    for line in Head:
        fpgs.write(line)
    for line in PP:
        fpgs.write(line)
    for line in Tail:
        fpgs.write(line)
    fpgs.flush()

previous = -1
doprev = False
def paint(idx):
    global previous, doprev
    if idx >= 0 and idx < n_locations:
        print '%4i  %s' % (idx + 1, lbls[idx])
        if not used[idx]:
            print 'No data'

    if idx < 0 or idx >= n_locations or not used[idx]:
        return

    previous = idx
    doprev = True

    paintit(idx)

def repaint():
    if doprev:
        paintit(idx)

mark1 = -1
mark2 = -1
def marks(m1, m2):
    global mark1, mark2
    if m1 != mark1:
        if mark1 >= 0:
            lst1.itemconfigure(mark1, {'background': 'white'})
        lst1.itemconfigure(m1, {'background': 'grey'})
        mark1 = m1
    if m2 != mark2:
        if mark2 >= 0:
            lst2.itemconfigure(mark2, {'background': 'white'})
        lst2.itemconfigure(m2, {'background': 'grey'})
        mark2 = m2

def comView1(event=None):
    i1 = lst1.nearest(event.y)
    i = int(lst1.get(i1).split()[0]) - 1
    i2 = lst2idx[i]
    lst2.see(i2)
    marks(i1, i2)
    if i != previous:
        paint(i)

def comView2(event=None):
    i2 = lst2.nearest(event.y)
    i = int(lst2.get(i2).split()[0]) - 1
    i1 = lst1idx[i]
    lst1.see(i1)
    marks(i1, i2)
    if i != previous:
        paint(i)

def zero(event=None):
    global min0
    if not min0:
        min0 = True
        repaint()

def nonzero(event=None):
    global min0
    if min0:
        min0 = False
        repaint()

def linear(event=None):
    global power
    if power != 1:
        power = 1
        repaint()

def quadratic(event=None):
    global power
    if power != 2:
        power = 2
        repaint()

def manycols(event=None):
    global colors, n_colors
    if n_colors != n_colors1:
        n_colors = n_colors1
        colors = colors1
        repaint()

def fewcols(event=None):
    global colors, n_colors
    if n_colors != n_colors2:
        n_colors = n_colors2
        colors = colors2
        repaint()

def quit():
    fpgs.write('quit\n')
    fpgs.close()
    root.quit()

power = 1
min0 = False
idx = 0

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

root = Tk()
root.title('RuG/L04 - differences with a single location')

b = Button(root, text='EXIT', command=quit)
b.pack(side=BOTTOM, padx='2m', pady='2m')

f = Frame(root, relief='groove', borderwidth='1m')
f.pack(side=BOTTOM, expand=YES, fill=X, padx='2m', pady='0m')
colgrad = StringVar()
colgrad.set('m')
Radiobutton(f, text='many colours', variable=colgrad, value='m', command=manycols).pack(anchor=NW)
Radiobutton(f, text='few colours', variable=colgrad, value='f', command=fewcols).pack(anchor=NW)

f = Frame(root, relief='groove', borderwidth='1m')
f.pack(side=BOTTOM, expand=YES, fill=X, padx='2m', pady='2m')
distances = StringVar()
distances.set('l')
Radiobutton(f, text='linear', variable=distances, value='l', command=linear).pack(anchor=NW)
Radiobutton(f, text='quadratic', variable=distances, value='q', command=quadratic).pack(anchor=NW)

f = Frame(root, relief='groove', borderwidth='1m')
f.pack(side=BOTTOM, expand=YES, fill=X, padx='2m', pady='0m')
method = StringVar()
method.set('nz')
Radiobutton(f, text='non-zero based', variable=method, value='nz', command=nonzero).pack(anchor=NW)
Radiobutton(f, text='zero based', variable=method, value='z', command=zero).pack(anchor=NW)

f = Frame(root)
f.pack(side=TOP, expand=YES, fill=BOTH)

f.l1 = Frame(f)
f.l2 = Frame(f)
f.l1.pack(side=LEFT, expand=YES, fill=BOTH, padx='2m', pady='2m')
f.l2.pack(side=LEFT, expand=YES, fill=BOTH, padx='2m', pady='2m')

xbar1 = Scrollbar(f.l1, orient="horizontal")
ybar1 = Scrollbar(f.l1)
lst1 = Listbox(f.l1, font=fixed, width=24, height=24)
xbar1.config(command=lst1.xview)
ybar1.config(command=lst1.yview)
lst1.config(yscrollcommand=ybar1.set, xscrollcommand=xbar1.set, background='white')
xbar1.pack(side=BOTTOM, fill=X)
ybar1.pack(side=RIGHT, fill=Y)
lst1.pack(side=LEFT, expand=YES, fill=BOTH)
lst1.bind('<Button-1>', comView1)

lst1idx = [0 for i in range(n_locations)]
n = 0
for i in used_locations:
    lst1.insert(END, '%4i  %s' % (i + 1, lbls[i]))
    lst1idx[i] = n
    n += 1

xbar2 = Scrollbar(f.l2, orient="horizontal")
ybar2 = Scrollbar(f.l2)
lst2 = Listbox(f.l2, font=fixed, width=24, height=24)
xbar2.config(command=lst2.xview)
ybar2.config(command=lst2.yview)
lst2.config(yscrollcommand=ybar2.set, xscrollcommand=xbar2.set, background='white')
xbar2.pack(side=BOTTOM, fill=X)
ybar2.pack(side=RIGHT, fill=Y)
lst2.pack(side=LEFT, expand=YES, fill=BOTH)
lst2.bind('<Button-1>', comView2)

lst2idx = [0 for i in range(n_locations)]
n = 0
s = [(lbls[i], i) for i in used_locations]
s.sort()
for l, i in s:
    lst2.insert(END, '%4i  %s' % (i + 1, l))
    lst2idx[i] = n
    n += 1

root.protocol("WM_DELETE_WINDOW", quit)
root.mainloop()
