#!/usr/bin/env python
"""
    Very simple client script used to get nearest squid server using the RESTful API.
"""
import urllib2
import sys
# import json
import re
import os
import logging
import time

from shoal_client import config as config

from optparse import OptionParser
from urllib2 import urlopen

server = config.shoal_server_url
cvmfs_config = config.cvmfs_config
default_http_proxy = config.default_squid_proxy

data = None
dump = False 
closest_http_proxy = ''
cvmfs_http_proxy = "CVMFS_HTTP_PROXY="

logging.basicConfig(filename="shoal_client.log", format='%(asctime)s %(message)s')

def get_args():
    """
        gets server and dump variables from command line arguments
    """
    global server
    global dump

    p = OptionParser()
    p.add_option("-s", "--server", action="store", type="string", dest="server", 
                 help="Also needs string for specifying the shoal server to use. " +
                      "Takes presedence over the option in config file")
    p.add_option("-d", "--dump", action="store_true", dest="dump",
                 help="Print closest proxies to terminal for debugging "+ 
                      "instead of over writing the CVMFS config file")
    (options, args) = p.parse_args()

    if options.server:
        server = options.server
    if options.dump:
        dump = True

def convertServerData(val):
    """
        converts val to digits if it's not already or else return None
    """
    if val.isdigit():
        return int(val)
    else:
        try:
            return float(val)
        except:
            if "null" in val:
                return None
            else:
                return unicode(val.strip("\""))

# TODO is this parser sufficient or should a full JSON parser be implemented?
# Seperating out the list of properties should be done but does support
#  for arbitrary json strings add anything?
def parseServerData(jsonStr):
    """
        creates a multidimensional server data dictionary indexed by
        unicode integers with dataTypes, geo_data and geoDataTypes. Each
        respective entry holds the appropriate dataTypes and geoDataTypes
        found in jsonStr
    """

    # TODO should load this from a config file as it has to match the server
    # Nested properties (i.e geo_data) needs to be handled separately
    dataTypes    = ["load",        "distance", "squid_port", "last_active", "created",      \
                    "external_ip", "hostname", "public_ip",  "private_ip"]

    geoDataTypes = ["city",       "region_name",   "area_code",    "time_zone", "dma_code", \
                    "metro_code", "country_code3", "latitude",     "postal_code",           \
                    "longitude",  "country_code",  "country_name", "continent"]

    # don't really care about data here
    # it is just a simple way to get number of nearest squids
    p = re.compile("\"" + dataTypes[0] + "\": ([^,}]+)")
    numNearestSquids = len(p.findall(jsonStr))
    ## compiles regex "load": ([^,}]+), although it doesn't really matter that fact that it's a load
    ## this will find the number of above matches in jsonStr and return into numNearestSquids  
    ## therefore each match in json is a 'nearest' squid

    # initalize the dictionaries
    outDict = {}
    for i in range(0, numNearestSquids):
        outDict[unicode(str(i))] = {}
        outDict[unicode(str(i))][unicode("geo_data")] = {}
    ## creates a multidimensional dict with each key 1 being u'i' (i in unicode)
    ## and key 2 being "geo_data" for all entries

    # TODO probably don't need seperate regexes
    # test using geodata one for both
    ## for each item in dataTypes, compile a regex for that item and find all the matches with jsonStr
    ## and put those matches in dataList.
    for dataType in dataTypes:
        p = re.compile("\"" + dataType + "\": ([^,]+)[,|}]")
        dataList = p.findall(jsonStr)
        for i, val in enumerate(dataList):
            outDict[unicode(str(i))][unicode(dataType)] = convertServerData(val)
    ## outDict is a multidimensional dict that now holds a val in each dataType per i
    ## same as above just for geoDataTypes
    for geoDataType in geoDataTypes:
        p = re.compile("\"" + geoDataType + "\": (\"[^\"]*|[^,]*)")
        dataList = p.findall(jsonStr)
        for i, val in enumerate(dataList):
            outDict[unicode(str(i))][unicode("geo_data")][unicode(geoDataType)] = convertServerData(val)
    ## outDict in geo_data for each geoDataType holds a val
    return outDict

get_args()

"""
    opens up a url to a server, parses server data from there
    from that data, creates addresses to each squid, stores in cvmfs_http_proxy
    overwrites cvmfs config file with cvmfs_http_proxy if dump is not specified
"""
## CVMFS = CERN Virtual Machine File System

## reads in server data (if it can be read in) into a dictionary called data
try:
    f = urlopen(server)
    # data = json.loads(f.read())
    data = parseServerData(f.read())
except (urllib2.URLError,ValueError), e:
    logging.error("Unable to open url. %s" % e)
    data = None

if data:
    ## iterates through the data dict and uses all hostname and squid_port keys
    ## to create addresses for each squid in closest_http_proxy
    for i in range(0, len(data)):
        try:
            closest_http_proxy += 'http://%s:%s;' % (data['%s'%i]['hostname'], data['%s'%i]['squid_port'])
        except KeyError, e:
            logging.error("The data returned from '%s' was missing the key: %s. Please ensure the url is running the latest Shoal-Server." % (server, e))
            sys.exit(1)

cvmfs_http_proxy += "\"" + closest_http_proxy + default_http_proxy + "\"\n"

## if dump -> don't overwrite CVMFS config file
if dump:
    print "%s would have been written to the CVMFS config file", cvmfs_http_proxy
else:
    # attempt to read the cvmfs_config file, no point in continuing if it can't be read
    try:
        f = open(cvmfs_config)
        lines = f.readlines()
    except:
        logging.error("Could not open and read the CVMFS config file")
        sys.exit(1)
    f.close()
    # create a list of tuples of lines/line numbers that have "CVMFS_HTTP_PROXY" 
    CVMFS_proxy_lines = [t for t in enumerate(lines) if "CVMFS_HTTP_PROXY" in t[1]]
    # if there is only one can just replace it with the new proxy
    if len(CVMFS_proxy_lines) == 1:
        lines[CVMFS_proxy_lines[0][0]] = cvmfs_http_proxy
    # add a line if it doesn't exist
    elif len(CVMFS_proxy_lines) == 0:
        lines += cvmfs_http_proxy
    # something is wrong with the CVMFS config; there are multiple entries
    # fixing it by changing the first and removing the extras
    else:
        logging.error("CVMFS file had duplicate CVMFS_HTTP_PROXY entries;" +
                      " writing the first deleting the rest")
        lines[CVMFS_proxy_lines[0][0]] = cvmfs_http_proxy
        for line in CVMFS_proxy_lines[1:]:
            lines[line[0]] = ""
    # open the file again this time for writing and replace its contents with the modified lines
    try:
        try:
            f = open(cvmfs_config, "w")
            f.writelines(lines)
        except Exception:
            logging.error("Could not write CVMFS config file")
    finally:
            f.close()

