#!/usr/bin/env python

# The Gopher CGI Library v0.2
# Provides functions for creating moles for the gopher protocol in python
#
# Copyright (c) 2011 by Christopher Yealy (octotep at sdf.lonestar.org)
# Permission is granted for the user to use, modify, copy, and publish
# any modifications however they like.
# Note that the software is provided "as is" with no warranty whatsoever;
# implied or otherwise.
#
# Documentation can be found at:
# gopher://sdf.org/1/users/octotep/downloads/gophercgi

import textwrap
import re

def print_text(text, line_length=67):
   """
   Prints a line of text by formatting it as a gopher selector and
   word-wrapping the text to the 'line_length' variable.
   """

   string = textwrap.fill(text, line_length)
   for line in string.split('\n'):
       print "i%s\t\terror.host\t0" % (line)

def print_para(text, line_length=67):
   """
   Prints a paragraph(s) of text by word-wrapping each paragraph, while
   preserving any newline characters. Word-wraps to the 'line_length'
   variable.
   """
   for para in text.split("\n"):
       print_text(para, line_length)

def print_error(text, line_length=67):
   """
   Prints a string as an itemtype-3 gopher selector (error), while
   wrapping the text to the 'line_length' variable.
   """
   string = textwrap.fill(text, line_length)
   for line in string.split('\n'):
       print "3%s\t\terror.host\t0" % (line)

def print_link(link_type, link_text, dir_path, host, port=70):
   """
   Prints a gopher selector link using the arguments provided.
   """
   print "%s%s\t%s\t%s\t%s" % (link_type, link_text, dir_path,
                               host, port)

def print_telnet(link_text, host, port=23):
   """
   Prints a telnet link, using the arguments provided.
   """
   print "8%s\t\t%s\t%s" % (link_text, host, port)

def print_special(link_text, special_url):
   """
   Prints an external link to any url, not just gopher.
   """
   print "h%s\tURL:%s\tspecial.link\t70" % (link_text, special_url)

def print_line():
   """
   Prints a blank line.
   """
   print "i            error.host      0"

def get_arg_list(args_str, separator, prefix):
   """
   Converts a string containing arguments given to the mole
   into a list for easy use.

   'args_str' is a string contaning the arguments.
   'seperator' is the character that separates each argument
   'prefix' is a part at the beginning of the string which contains
       no arguments and will be removed from the results.

   Returns a list of the arguments from the string.
   """
   if args_str.startswith(prefix):
       pre_len = len(prefix)
       args = args_str[pre_len:]
       args_list = args.split(separator)
   else:
       args = args_str
       args_list = args.split(separator)
   return args_list

def get_arg_dict(args_str, separator, prefix, dict_separator="="):
   """
   Converts a string containing key-value pairs given to the mole
   into a dictionary for easy use.

   'args_str' is a string containing the arguments.
   'seperator' is the character which separates the pairs
       from each other.
   'prefix' is a part at the beginning of the string which contains
       no arguments and will be removed from the results.
   'dict_separator' is the character which separates each key from its
       respective value

   Returns a dictionary of the arguments from the string.
   """
   if args_str.startswith(prefix):
       pre_len = len(prefix)
       args = args_str[pre_len:]
       args_list = args.split(separator)
   else:
       args = args_str
       args_list = args.split(separator)
   args_dict = {}
   for part in args_list:
       key, value = part.split(dict_separator)
       args_dict[key] = value
   return args_dict


def tabs_to_spaces(text, tab_size=8):
   """
   Converts all the tab characters in 'text' to spaces.

   'tab_size' indicates the size of the tab stops.

   Returns the converted string.
   """
   text_arr = []
   for line in text.split("\n"):
       string = ""
       counter = 0
       for letter in line:
           if (letter != "\t"):
               string = "%s%s" % (string, letter)
               counter += 1
           else:
               indent = tab_size - (counter % tab_size)
               space = " " * indent
               string = "%s%s" % (string, space)
               counter += indent
       text_arr.append(string)
   return "\n".join(text_arr)

def gs_to_text(gm_text, show_prefixes=True, show_links=True):
   """
   Converts text formatted as a gopher selectors into plain text.
   Bears similarities to `lynx -dump`

   'show_prefixes' controls whether each selector that isn't
       text gets a label to identify what type of link it is.
   'show_links' controls whether a list of URL's is printed
       at the bottom of the output.

   Returns the converted text.
   """
   final_list = []
   link_list = []
   counter = 0
   gm_lines = gm_text.splitlines()
   for line in gm_lines:
       if len(line) == "":
           line = "i\t\terror.host\t0"
       itemtype = line[:1]
       prefix = ""
       suffix = ""
       tabs = line.split("\t")
       text = tabs.pop(0)
       if show_links:
           if (itemtype != 'i') and (itemtype != '3'):
               counter += 1
               suffix = " [%s]" % (str(counter))
               path = tabs.pop(0)
               host = tabs.pop(0)
               port = tabs.pop(0)
               url = _return_url(itemtype, host, path, port)
               link_list.append("%s. %s" % (suffix, url))
       if show_prefixes:
           prefix = _return_prefix(itemtype)
       final_list.append("%s\t%s%s" % (prefix, text[1:], suffix))
   final_str = "\n".join(final_list)
   if show_links:
       link_str = "\n".join(link_list)
       final_str = "%s\n\n%s" % (final_str, link_str)
   return final_str

def gm_to_gs(menu_text):
   """
   Converts text formatted as a gophermap into gopher selectors.

   Returns the converted text.
   """
   regex = re.compile('[\w\+].*?\t.*?\t.*?\t\d.*')
   final_list = []
   text_lines = menu_text.splitlines()
   for line in text_lines:
       match = regex.match(line)
       if match:
           final_list.append(line)
       else:
           new_line = "i%s\t\terror.host\t0" % (line)
           final_list.append(new_line)
   return "\n".join(final_list)

def _return_url(itemtype, host, path, port):
   """
   Takes parameters and creates a URL based on the
   itemtype.

   Used internally by the library.

   Returns the URL string.
   """
   if len(path) != 0:
       if path[0] == '/':
           tmp_path = path[1:]
           path = tmp_path
   if str(itemtype) == '8':
       return "telnet://%s:%s/%s" % (host, port, path)
   elif str(itemtype) == 'h':
       # Check for URL:
       if path[:4] == 'URL:':
           return path[4:]
       else:
           return "gopher://%s:%s/%s/%s" % (host, port,
                                            itemtype, path)
   else:
       return "gopher://%s:%s/%s/%s" % (host, port, itemtype, path)

def _return_prefix(itemtype):
   """
   Returns a text label based on itemtype.

   Used internally by the library.
   """
   if itemtype == 'i':
       return ""
   elif itemtype == '0':
       return "Text:"
   elif itemtype == '1':
       return "Dir:"
   elif itemtype == '2':
       return "CSO:"
   elif itemtype == '3':
       return "!!!:"
   elif itemtype == '4':
       return "File:"
   elif itemtype == '5':
       return "File:"
   elif itemtype == '6':
       return "File:"
   elif itemtype == '7':
       return "?:"
   elif itemtype == '8':
       return "Tel:"
   elif itemtype == '9':
       return "File:"
   elif itemtype == 'g':
       return "GIF:"
   elif itemtype == 'I':
       return "Image:"
   elif itemtype == 'T':
       return "Tel:"
   elif itemtype == 's':
       return "Snd:"
   elif itemtype == 'p':
       return "PNG:"
   elif itemtype == 'd':
       return "PDF:"
   elif itemtype == 'x':
       return "XML:"
   elif itemtype == 'c':
       return "CSS:"
   elif itemtype == 'h':
       return "HTML:"
   else:
       return "UnKN:"