#!/usr/bin/python
#
# Copyright (C) 2012  Miroslav Lichvar <mlichvar@redhat.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import sys, pygame
import collections
import math

freq_file = open(sys.argv[1], 'r')
offset_file = open(sys.argv[2], 'r')
delay_file = open(sys.argv[3], 'r')

(maxx, maxy) = (640, 480)

pygame.init()
window = pygame.display.set_mode((maxx, maxy))
font = pygame.font.SysFont("monospace", 12)

pygame.time.set_timer(pygame.USEREVENT, 1000 / 30)

white = (255, 255, 255)
black = (0, 0, 0)
blue = (50, 50, 255)
lightblue = (150, 150, 255)
red = (255, 0, 0)
green = (0, 255, 0)

freqs = collections.deque()
offsets = collections.deque()
delays = []

freq_avg = 0.0
time = -1
xscale = 2e-1
yscale = 1e6
frame_skip = 10

offset_rms = [ 0, 0, 0, 0 ]
offset_lock = 0
delays_shown = 3
eof = False
paused = False
game_mode = False

delay_lines = []
while True:
    line = delay_file.readline()
    if line == "":
        break
    line = line.split()
    if line[2] == "1":
        idx = int(line[1])
    elif line[1] == "1":
        idx = int(line[2])
    else:
        continue
    while len(delay_lines) < idx + 1:
        delay_lines.append([])
    delay_lines[idx].append(line)

delay_file.close()

for i in range(len(delay_lines)):
    delays.append([])

    last_line = []
    for line in delay_lines[i]:
        if len(last_line) == 4 and len(line) == 4 and last_line[2] == "1" and line[1] == "1":
            delay1 = float(last_line[3])
            delay2 = float(line[3])
            delays[i].append((int(float(line[0])), (delay1 - delay2) / 2, (delay1 + delay2) / 2))
        last_line = line

while True:
    event = pygame.event.wait()
    if event.type == pygame.QUIT:
        break
    if event.type == pygame.KEYDOWN:
        if event.key == pygame.K_SPACE or event.key == pygame.K_p:
            paused = not paused
        elif event.key == pygame.K_q:
            break
        elif event.key == pygame.K_g:
            game_mode = not game_mode
            pygame.event.set_grab(game_mode)
            pygame.mouse.set_visible(not game_mode)
        elif event.key == pygame.K_l:
            offset_lock += 1
            offset_lock %= len(offset_rms)
            delays_shown = offset_lock + 1
            if delays_shown == 1:
                delays_shown = 3
        elif event.key == pygame.K_PAGEUP:
            frame_skip *= 2
        elif event.key == pygame.K_PAGEDOWN:
            frame_skip /= 2
            if frame_skip <= 5:
                frame_skip = 5
        elif event.key == pygame.K_UP:
            yscale *= 2
        elif event.key == pygame.K_DOWN:
            yscale /= 2
        elif event.key == pygame.K_LEFT:
            xscale *= 2
        elif event.key == pygame.K_RIGHT:
            xscale /= 2
    elif event.type == pygame.MOUSEMOTION:
        rel = pygame.mouse.get_rel()
        if game_mode and rel != (0, 0):
            freq_avg += rel[1] / 1e9

    pygame.event.clear(pygame.USEREVENT)

    if event.type != pygame.USEREVENT or paused:
        continue

    while not eof:
        histsize = maxx / xscale

        freq = freq_file.readline()
        if freq == "":
            eof = True
            break
        freqs.appendleft(float(freq))
        while len(freqs) > histsize:
            freqs.pop()

        offset = offset_file.readline()
        if offset == "":
            eof = True
            break
        offsets.appendleft(map(float, offset.split()))
        while len(offsets) > histsize:
            offsets.pop()

        if not game_mode:
            freq_avg += 0.001 * (freqs[0] - freq_avg)
        else:
            buttons = pygame.mouse.get_pressed()
            if buttons == (1, 0, 0):
                slew = 1e-6
            elif buttons == (0, 0, 1):
                slew = -1e-6
            else:
                slew = 0.0
            offsets[0][0] = offsets[1][0] - (freq_avg - freqs[0] + slew)

        offset_rms = [r + 0.001 * (o * o - r) for r, o in zip(offset_rms, offsets[0])]

        time += 1
        if time % frame_skip == 0:
            break

    if len(offsets) == 0:
        continue

    window.fill(black)
    last_off = []
    x = maxx
    y = maxy / 2 + offsets[0][offset_lock] * yscale

    def get_delays(time):
        d = delays[delays_shown]
        idx = len(d) - 1
        while time >= 0 and idx >= 0:
            while d[idx][0] > time and idx > 0:
                idx -= 1
            if d[idx][0] != time:
                yield (False, 0, 0)
            else:
                yield (True, d[idx][1], d[idx][2])
            time -= 1

    for freq, offset, (delay_valid, delay_center, delay_size) in zip(freqs, offsets, get_delays(time)):
        x -= xscale
        y -= (freq - freq_avg) * yscale
        if int(x + xscale) != int(x):
            for i, (off, col) in enumerate(zip(offset, [white, red, green, blue])):
                oy = y - off * yscale
                if len(last_off) > i:
                    pygame.draw.aaline(window, col, last_off[i], (x, oy))
                else:
                    last_off.append(())
                last_off[i] = (x, oy)
                if game_mode:
                    break

        if delay_valid:
            pygame.draw.line(window, blue, (x, y - (delay_center - delay_size) * yscale), (x, y - (delay_center + delay_size) * yscale))
            pygame.draw.line(window, lightblue, (x - 3, y - delay_center * yscale), (x + 3, y - delay_center * yscale))

    window.blit(font.render("time = %d rms = %s xscale = %.1e yscale = %.1e" % (time, ["%1.6f" % math.sqrt(o) for o in offset_rms], xscale, yscale), False, white, black), (5, 0))
    window.blit(font.render("q:Quit  p:Pause  PgDn:Slow down  PgUp:Speed up  g:Game mode  l:Switch lock  Arrows:Scale", False, white, black), (5, maxy - 15))
    pygame.display.flip()

    #pygame.image.save(window, "visclocks%06d.png" % time)

freq_file.close()
offset_file.close()
