#!/usr/bin/env python
## Free Libraries Gopher Client (see version number in below tuple)
## Jul 2012
## License: Public Domain
## Very Beta

geo = (80, 24, "<-' go <- back ^ v scroll    Free Libraries Gopher Client 0.2 (Public Domain)")
import curses
from os import popen
from os import system
from sys import argv

## changelog:
##
## 0.2:
## now targets both python 2 and 3 compatibility (print() / curses work in both)
## light support for url proxies (gopherurl://path/proxy?proxiedgopherurl)
## any movement of selection bar shows current or hovered url at bottom
## not new: using "back" autoscrolls to top... new: except for single step back
## (makes it a little less tedious to explore trees with minimal usage change)
## may have fixed cosmetic line width bug
## slightly better support of 40 columns

if geo[0] < 80:
    geo = (geo[0], geo[1], "<-back ^v scr FLGC 0.2(Public Domain)")
if geo[0] < 40:
    print("FLGC 0.2")
    print("(Public Domain)")
    print ()
    print("This should probably")
    print("be run with at least")
    print("40 columns. hit enter")
    print("to try it anyway.")
    print()
    try:
        nul = input()
    except:
        pass

yescurl = int(popen("which curl | wc -l").readline())
if not yescurl:
    print("This program will not work without curl.")
    print()
    print("Suggestion: sudo apt-get install curl (for Debian/Ubuntu/Trisquel/GNewSense)")
    print("                sudo yum install curl (for Fedora/Freedora/etc)")
    print("")
    exit()
text = ""

url = ""
try:
    url = argv[1]
except:
    pass
if url == "":
    #try:
    #    print("enter the name of a gopher site:", end=" ") ## python 3
    #except SyntaxError:
    #    print("enter the name of a gopher site:"),
    ## that failed, so bash will do it:
    system("echo -n \"enter the name of a gopher site: \"")
    try:
        url = raw_input().strip() ## python 2
    except NameError:
        url = input().strip()

def c(p):
    global url
    spaghetti = 0 ## this is related to the "back" function, sorry
    if not "gopher://" in url.lower():
        url = "gopher://" + url
    if url.lower()[:9] != "gopher://":
        url = "gopher://" + url    
    global text # for debugging output
    text = url
    history = []
    yescaca = int(popen("which cacaview | wc -l").readline()) 
    oneselected = 0
    keeponescrolllocation = 0 ## save cursor position history (one deep)
                              ## could do fully, but it really should reset    
    x = 0
    while x not in [27, ord("x"), ord("q"), ord("Q"), ord("X")]:
        yindex = 0
        yib = 0
        global geo
        thistype = "1"
        rurl = url
        if rurl[:9].lower() == "gopher://":
            rurl = rurl[9:]
        try:
            thistype = rurl.split("/")[1]
            text = thistype
        except:
            pass
        proxy = "gopher://127.0.0.1/1/cgi-bin/proxy?"
        proxy = "" ## remove or set this if you want to use the proxy feature
        f = list(popen("curl -s \"" + proxy + url + "\" 2> /dev/null").readlines())
        history += [url]
        if len(history) > 50: 
            history = history[-50:] ## conservative ; try 1000 or even remove
        for lenf in range(3):
            f = [""] + f
        if len(f) < geo[1]:
            while len(f) < geo[1]:
                f += [""]
        for lenf in range(geo[1]):
            f += [""]
        x = 0
        while x not in [27, curses.KEY_ENTER, 10, ord("x"), ord("X"), ord("q"), ord("Q")]:
            ## because curses.KEY_ENTER wasn't being trapped; LF was, go figure
            if x == curses.KEY_PPAGE:
                oneselected = 1
                yindex -= (geo[1] - 1)
                if yindex < 0: 
                    yindex = 0            
            if x == curses.KEY_NPAGE:
                oneselected = 1
                yindex += geo[1] - 1
                if yindex > len(f) - geo[1]: 
                    yindex = len(f) - geo[1] 
            if x == curses.KEY_UP:
                oneselected = 1
                yindex -= 1
                if yindex < 0: 
                    yindex = 0            
            if x == curses.KEY_DOWN:
                oneselected = 1
                yindex += 1
                if yindex > len(f) - geo[1]: 
                    yindex = len(f) - geo[1] 
            if x == curses.KEY_HOME:
                oneselected = 1
                yindex = 0
            if x == curses.KEY_LEFT:
                if len(history) > 1:
                    try: ## lazy
                        history = history[:-1]
                        newurl = history[len(history) - 1]
                        history = history[:-1]
                        url = newurl
                        text = newurl
                    except: 
                        pass 
                else:
                    x = 126
            if x == curses.KEY_END:
                oneselected = 1
                yindex = len(f) - geo[1] -1
            if spaghetti: ## if page is from url history
                yindex = keeponescrolllocation
                keeponescrolllocation = 0 ## otherwise it keeps using it
            if yindex != yib or yib == 0:
                hoverurl = ""
                try:
                    hoverurl = rurl ## first try url that's being viewed...
                    if thistype == "1" and f[yindex + 3][0] in "0I1":
                        hoverurl = f[yindex + 3].split("\t")[2] + ":" + f[yindex + 3].split("\t")[3].rstrip() + "/" + f[yindex + 3][0] + f[yindex + 3].split("\t")[1]
                    ## ...then if possible / applicable, url that's highlighted by user
                except:
                    pass
                p.clear()
                for pp in range(0, geo[1] - 1):
                    thisline = f[pp + yindex].rstrip()
                    if thistype == "1" and len(thisline):
                        hint = ""
                        if thisline[0] == "0": 
                            hint = "(TEXT) "
                        elif thisline[0] == "1":
                            hint = " (DIR) "
                        elif thisline[0] == "i":
                            hint = ""
                        elif thisline[0] == "I": 
                            hint = " (IMG) "
                        if thisline[0] in "01iI":
                            thisline = hint + thisline.split("\t")[0][1:]
                    ntab = " " * 4 ## this might fix the line width issue
                    if pp == 3:
                        bar = ntab.join(str(thisline + (" " * geo[0])).split("\t"))
                        ## for whatever reason, this doesn't exactly
                        p.addstr(pp, 0, bar[:geo[0]], curses.A_REVERSE)
                        p.addstr(pp + 1, 0, " " * geo[0])
                    else:    
                        p.addstr(pp, 0, ntab.join(thisline.split("\t"))[:geo[0]])
                yib = yindex
                p.refresh()
            p.addstr(geo[1] - 1, 0, " " * (geo[0] - 1), curses.A_REVERSE)
            if hoverurl and oneselected: ### and int(geo[0]) > 79: ## 79 regardless of screen dim
                hoverurl = hoverurl[:geo[0] - 2]
                urlstatus = hoverurl + " " * (geo[0] - 2 - len(hoverurl))
                p.addstr(geo[1] - 1, 0, " " + urlstatus, curses.A_REVERSE)
            else:
                p.addstr(geo[1] - 1, 0, " " + geo[2], curses.A_REVERSE)
            p.addstr(geo[1] - 1, geo[0] - 1, "", curses.A_REVERSE)
            p.refresh()  
            spaghetti = 0
            if x != curses.KEY_LEFT: 
                x = p.getch()
            else:
                spaghetti = 1 ## make an exception for the back function
                x = 10
            if x == 10 or x == curses.KEY_ENTER:
                oneselected = 0 ## fix this line if you want status ready on new pages
                if spaghetti == 0:                    
                    rstr = f[yindex + 3] ## the contents of the selected line
                    keeponescrolllocation = yindex                    
                    typifany = ""
                    ## typifany: derived from link you're hovering over
                    ## thistype: type / page you're already viewing
                    if len(rstr) and thistype == "1":
                        ## if thistype isn't 1,  ^ parsed line doesn't count
                        ## (i.e. because you're viewing type 0)
                        typifany = rstr[0] ## get gophertype, (or " ", or "")
                    if typifany == "0":
                        if len(rstr.split("\t")) > 3:
                            typs = "/" + typifany
                            desc = rstr.split("\t")[0][1:]
                            rsel = rstr.split("\t")[1]
                            serv = rstr.split("\t")[2]
                            ## port field is often last, so remove \r\n
                            port = rstr.split("\t")[3].rstrip()
                            newurl = "gopher://" + serv + ":" + port + typs + rsel
                            url = newurl
                    elif typifany == "1":
                        if len(rstr.split("\t")) > 3:
                            typs = "/" + typifany
                            desc = rstr.split("\t")[0][1:]
                            rsel = rstr.split("\t")[1]
                            serv = rstr.split("\t")[2]
                            ## port field is often last, so remove \r\n
                            port = rstr.split("\t")[3].rstrip()
                            newurl = "gopher://" + serv + ":" + port + typs + rsel
                            url = newurl
                    elif typifany == "I":
                        if len(rstr.split("\t")) > 3:
                            typs = "/" + typifany
                            desc = rstr.split("\t")[0][1:]
                            rsel = rstr.split("\t")[1]
                            serv = rstr.split("\t")[2]
                            ## port field is often last, so remove \r\n
                            port = rstr.split("\t")[3].rstrip()
                            newurl = "gopher://" + serv + ":" + port + typs + rsel
                            system("curl -s \"" + newurl + "\" > /tmp/cacav 2> /dev/null ; cacaview /tmp/cacav 2> /dev/null")                        
                        else:
                            x = 126 ## pretend enter wasn't pressed 
                    else:
                        x = 126 ## no plans to map this
curses.wrapper(c)