#!/usr/bin/env python
"""This is a small class that tries to arrange a list of characters into a
circle.  I'm writing this to help visualize information.  It's also a fun
program because it works with the curses library."""

from math import sin, cos, pi
from string import lowercase
import curses

def rotate(L, n):
    """Rotate a list nondestructively to the left."""
    L = list(L)
    left = L[0:n]
    right = L[n:len(L)]
    return right + left

def getDividedAngles(n):
    """Given n, return back a list of angles that divides the unit
    circle up."""
    unit = (2 * pi) / n
    return [unit * i for i in range(n)]

def getCartesianCoords(r, theta):
    """Given r and theta, return back the (x,y) coordinate."""
    return (r*cos(theta), r*sin(theta))

def arrangeCircularly(L, radius):
    results = {}
    angles = getDividedAngles(len(L))
    for i in range(len(L)):
        results[L[i]] = getCartesianCoords(radius, angles[i])
    return results


def _displayChars(stdscr, chars_and_coords, centerx, centery):
    for item in chars_and_coords.keys():
        x, y = chars_and_coords[item]
        try:
            stdscr.addch(y + centery, x + centerx, ord(item))
        except curses.error: pass

def _displayHelp(stdscr):
    doc = """
] to increase radius
[ to decrease radius
hjkl to shift center with vi keys
q to quit
"""
    stdscr.addstr(0, 0, doc)

                  
def _driver(stdscr):
    radius = 7  # yes, this is hardcoded.
    centery, centerx = [n / 2 for n in stdscr.getmaxyx()]
    ch = 'sentinel'
    while ch != ord('q'):
        # I know, I know this code is terrible.  I must fix it sometime.
        if ch == ord(']'): radius += 1
        elif ch == ord('['): radius -= 1
        elif ch == ord('h'): centerx -= 1
        elif ch == ord('j'): centery += 1
        elif ch == ord('k'): centery -= 1
        elif ch == ord('l'): centerx += 1
        chars_and_coords = arrangeCircularly(lowercase[0:26], radius)
        chars_and_coords2 = arrangeCircularly(rotate(lowercase[0:26], 13), radius - 3)
        _displayChars(stdscr, chars_and_coords, centerx, centery)
        _displayChars(stdscr, chars_and_coords2, centerx, centery)
        _displayHelp(stdscr)
        ch = stdscr.getch()
        stdscr.clear()

if __name__ == "__main__":
    curses.wrapper(_driver)

