#!/usr/bin/python3
import sys, socket, subprocess, re, traceback

history = []

host = 'ake.crabdance.com'
port = 70
path = ''
mode = 'cat'

GOPHER_SCHEMA = re.compile(r'^(?:gopher:\/\/)([^:\/]+)(?::(\d+))*\/([01])(.*)$')

def download_file(link):
       print('Download file from {}:{} to current directory\nleave blank to abort'.format(link['host'], link['path']))
       filename = input('filename: ')
       if filename == '':
               return
       with open(filename, 'wb') as f:
               s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
               s.connect((link['host'], link['port']))
               s.sendall('{}\r\n'.format(link['path']).encode('utf-8'))
               resp = b''
               while True:
                       packet = s.recv(1024)
                       if not packet:
                               break
                       resp += packet
               s.close()
               f.write(resp)
       print('Saved to {}'.format(filename))

while True:
       try:
               s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
               s.connect((host, port))
               s.sendall('{}\r\n'.format(path).encode('utf-8'))
               resp = b''
               while True:
                       packet = s.recv(1024)
                       if not packet:
                               break
                       resp += packet
       except:
               traceback.print_exc()
               resp = '3An error occured during page loading\t\tnone\t0\r\n1Back to previous page\t{3}\t{1}\t{2}\r\n'.format(*history[-1]).encode('utf-8')
       finally:
               s.close()
       sys.stdout.write('\033[2J')
       if mode == 'doc':
               sys.stdout.write(resp.decode('utf-8'))
       else:
               entries = [entry.rstrip().split('\t') for entry in resp.decode('utf-8').split('\n')]
               links = []
               for line in entries:
                       if len(line[0]) == 0:
                               continue
                       if line[0][0] == 'i':
                               print('\t' + line[0][1:])
                       elif line[0][0] == '0':
                               links.append(dict(
                                       type='doc', host=line[2], port=int(line[3]), path=line[1]
                               ))
                               print('[{}]\t\033[4;33m{}\033[0m'.format(len(links), line[0][1:]))
                       elif line[0][0] == '1':
                               links.append(dict(
                                       type='cat', host=line[2], port=int(line[3]), path=line[1]
                               ))
                               print('[{}] +\t\033[1;34m{}{}\033[0m'.format(len(links), line[0][1:], ' \033[1;32m(external link)' if line[2] != host else ''))
                       elif line[0][0] == '7':
                               links.append(dict(
                                       type='find', host=line[2], port=int(line[3]), path=line[1]
                               ))
                               print('[{}] ?\t\033[7;32m{}\033[0m'.format(len(links), line[0][1:]))
                       elif line[0][0] == '9':
                               links.append(dict(
                                       type='bin', host=line[2], port=int(line[3]), path=line[1]
                               ))
                               print('[{}] *\t\033[1;35m{}\033[0m'.format(len(links), line[0][1:]))
                       elif line[0][0] == 'h':
                               links.append(dict(
                                       type='link', host=line[2], port=int(line[3]), path=line[1]
                               ))
                               print('[{}] >\t\033[4;36m{}\033[0m'.format(len(links), line[0][1:]))
                       elif line[0][0] == 'I':
                               print('\t[picture: {}]'.format(line[0][1:]))
                       elif line[0][0] == '3':
                               print('\t\033[7;1;31m{}\033[0m'.format(line[0][1:]))
                       elif line[0][0] == '.':
                               print('[done]')
                       else:
                               print('\tNot supported: {}'.format(line[0]))
       action = input('\033[1;31m{}:{}\033[0m> '.format(host, path)).rstrip()
       if action == 'exit':
               break
       elif action == 'back':
               mode, host, port, path = history.pop()
       elif action == 'up':
               history.append((mode, host, port, path))
               path = path[:path.rfind('/')]
               mode = 'cat'
       elif action == 'home':
               history.append((mode, host, port, path))
               path = ''
               mode = 'cat'
       elif action[0] == '@':
               history.append((mode, host, port, path))
               host = action[1:]
               path = ''
               mode = 'cat'
       elif action.isdigit():
               link_index = int(action) - 1
               if len(links) <= link_index:
                       print('No such link')
               elif links[link_index]['type'] == 'bin':
                       download_file(links[link_index])
               elif links[link_index]['type'] == 'link':
                       print(repr(links[link_index]))
                       subprocess.call(['xdg-open', links[link_index]['path'][4:]])
               else:
                       history.append((mode, host, port, path))
                       mode = links[link_index]['type']
                       host = links[link_index]['host']
                       port = links[link_index]['port']
                       path = links[link_index]['path']
                       if mode == 'find':
                               query = input('Search query: ')
                               path += '\t' + query
       elif action.startswith('gopher:'):
               result = GOPHER_SCHEMA.match(action)
               if result is not None:
                       history.append((mode, host, port, path))
                       host = result.group(1)
                       port = int(result.group(2)) if result.group(2) else 70
                       mode = 'cat' if result.group(3) == '1' else 'doc'
                       path = result.group(4)
       else:
               history.append((mode, host, port, path))
               path = action