import json
import re


def pathSegmentParse(path):
    '''
    Parses a ppath string into logical segments that 
    can be applied individually. 
    There are 2 kinds of segments, paths and filters.
    Paths indicate a path through the structure.
    Filters evaluate an array of substructures to only 
    those that match
    '''
    segments = []
    parts = re.split('\[|\]',path)
    for part in parts:
        if not part: 
            continue
        seg = {}
        if part[0]=='.':
            part = part[1:]
        if '=' in part:
            sub_parts = part.split('=')
            seg['path'] = sub_parts[0]
            seg['type'] = 'filter'
            seg['value'] = part.split('=')[1]
        else:
            seg['type'] = 'path'
            seg['path'] = part
        segments.append(seg)
    return segments

def applyPath(segment,pointers):
    '''
    Takes an array of pointers and a ppath segment and returns
    a new array of pointers that match the given path
    '''
    path = segment['path']
    parts = path.split('.')
    for part in parts:
        new_pointers = []
        for i in range(0,len(pointers)):
            if isinstance(pointers[i], (dict,list)): #then we're not at a simple value
                if pointers[i].get(part):
                    if isinstance(pointers[i][part], list):
                        for p in pointers[i][part]:
                            new_pointers.append(p)
                    else:
                        new_pointers.append(pointers[i][part])
        pointers = new_pointers
    return pointers

def applyFilter(segment,pointers):
    '''
    Takes a filter segment of a ppath and applied it
    to each pointer and returns an array of pointers
    that resolve to the value given in the segment
    '''
    new_pointers = []
    for p in pointers:
        value = applyPath(segment,[p])
        if value:
            if segment['value'] in value:
                new_pointers.append(p)
    return new_pointers


def getFromPPath(data,path,default=[]):
    '''
    Takes a python dict and a ppath string and finds the 
    matching data in the dict. If no data can be found the 
    default is returned
    '''
    pointers = [data]
    segments = pathSegmentParse(path)
    for segment in segments:
        if segment['type'] == 'path':
            pointers = applyPath(segment,pointers)
        if segment['type'] == 'filter':
            pointers = applyFilter(segment,pointers)
    if len(pointers)==0:
        return default
    return pointers



JSON = '''
{
  "friends" : [
        {"first_name": "Wendell",    "last_name": "Jordan",    "phone":[{"type":"mobile","number":"440-123-8126"},{"type":"home","number":"440-890-5573"}],"gender":"male"},
        {"first_name": "Jeanne",     "last_name": "Morris",    "phone":[{"type":"mobile","number":"216-123-0079"},{"type":"home","number":"216-890-7830"}],"gender":"female"},
        {"first_name": "Herman",     "last_name": "Mcdaniel",  "phone":[{"type":"mobile","number":"440-123-1344"},{"type":"home","number":"440-890-8825"}],"gender":"male"},
        {"first_name": "Emanuel",    "last_name": "Becker",    "phone":[{"type":"mobile","number":"216-123-8825"},{"type":"home","number":"216-890-8332"}],"gender":"female"},
        {"first_name": "Sara",       "last_name": "Todd",      "phone":[{"type":"mobile","number":"216-123-6117"},{"type":"home","number":"216-890-2899"}],"gender":"female"},
        {"first_name": "Victoria",   "last_name": "Gray",      "phone":[{"type":"mobile","number":"216-123-8456"},{"type":"home","number":"216-890-4469"}],"gender":"female"},
        {"first_name": "Ronnie",     "last_name": "Cook",      "phone":[{"type":"mobile","number":"216-123-2006"},{"type":"home","number":"216-890-2006"}],"gender":"female"},
        {"first_name": "Gilbert",    "last_name": "Morton",    "phone":[{"type":"mobile","number":"440-123-4567"},{"type":"home","number":"440-890-4567"}],"gender":"male"},
        {"first_name": "Claudia",    "last_name": "Padilla",   "phone":[{"type":"mobile","number":"216-123-4567"},{"type":"home","number":"216-890-4567"}],"gender":"female"},
        {"first_name": "Doris",      "last_name": "Maxwell",   "phone":[{"type":"mobile","number":"216-123-4567"},{"type":"home","number":"216-890-4567"}],"gender":"female"},
        {"first_name": "Luis",       "last_name": "Stokes",    "phone":[{"type":"mobile","number":"440-123-4567"},{"type":"home","number":"440-890-4567"}],"gender":"male"},
        {"first_name": "Byron",      "last_name": "Kelley",    "phone":[{"type":"mobile","number":"440-123-4567"},{"type":"home","number":"440-890-4567"}],"gender":"male"},
        {"first_name": "Terri",      "last_name": "Hayes",     "phone":[{"type":"mobile","number":"216-123-4567"},{"type":"home","number":"216-890-4567"}],"gender":"female"},
        {"first_name": "Flora",      "last_name": "Greene",    "phone":[{"type":"mobile","number":"216-123-4567"},{"type":"home","number":"216-890-4567"}],"gender":"female"},
        {"first_name": "Guadalupe",  "last_name": "Strickland","phone":[{"type":"mobile","number":"216-123-4567"},{"type":"home","number":"216-890-4567"}],"gender":"female"},
        {"first_name": "Sylvia",     "last_name": "Sutton",    "phone":[{"type":"mobile","number":"216-123-4567"},{"type":"home","number":"216-890-4567"}],"gender":"female"},
        {"first_name": "Caroline",   "last_name": "Perkins",   "phone":[{"type":"mobile","number":"216-123-4567"},{"type":"home","number":"216-890-4567"}],"gender":"female"},
        {"first_name": "Sherri",     "last_name": "Ross",      "phone":[{"type":"mobile","number":"216-123-4567"},{"type":"home","number":"216-890-4567"}],"gender":"female"},
        {"first_name": "Lloyd",      "last_name": "Townsend",  "phone":[{"type":"mobile","number":"440-123-4567"},{"type":"home","number":"440-890-4567"}],"gender":"male"},
        {"first_name": "Cody",       "last_name": "Erickson",  "phone":[{"type":"mobile","number":"440-123-4567"},{"type":"home","number":"440-890-4567"}],"gender":"male"},
        {"first_name": "Ron",        "last_name": "Williams",  "phone":[{"type":"mobile","number":"440-123-4567"},{"type":"home","number":"440-890-4567"}],"gender":"male"},
        {"first_name": "Francis",    "last_name": "Fitzgerald","phone":[{"type":"mobile","number":"216-123-4567"},{"type":"home","number":"216-890-4567"}],"gender":"female"},
        {"first_name": "Kenneth",    "last_name": "Hopkins",   "phone":[{"type":"mobile","number":"440-123-4567"},{"type":"home","number":"440-890-4567"}],"gender":"male"},
        {"first_name": "Mary",       "last_name": "Ballard",   "phone":[{"type":"mobile","number":"216-123-4567"},{"type":"home","number":"216-890-4567"}],"gender":"female"},
        {"first_name": "Andre",      "last_name": "Lewis",     "phone":[{"type":"mobile","number":"440-123-4567"},{"type":"home","number":"440-890-4567"}],"gender":"male"},
        {"first_name": "Marguerite", "last_name": "Medina",    "phone":[{"type":"mobile","number":"216-123-4567"},{"type":"home","number":"216-890-4567"}],"gender":"female"},
        {"first_name": "Rachael",    "last_name": "Brown",     "phone":[{"type":"mobile","number":"216-123-4567"},{"type":"home","number":"216-890-4567"}],"gender":"female"},
        {"first_name": "Daryl",      "last_name": "Mccarthy",  "phone":[{"type":"mobile","number":"440-123-4567"},{"type":"home","number":"440-890-4567"}],"gender":"male"},
        {"first_name": "Shelia",     "last_name": "Schultz",   "phone":[{"type":"mobile","number":"216-123-4567"},{"type":"home","number":"216-890-4567"}],"gender":"female"},
        {"first_name": "Geoffrey",   "last_name": "Mcdonald",  "phone":[{"type":"mobile","number":"440-123-4567"},{"type":"home","number":"440-890-4567"}],"gender":"male"},
        {"first_name": "Tyler",      "last_name": "Nichols",   "phone":[{"type":"mobile","number":"440-123-4567"},{"type":"home","number":"440-890-4567"}],"gender":"male"},
        {"first_name": "Gretchen",   "last_name": "Graves",    "phone":[{"type":"mobile","number":"216-123-4567"},{"type":"home","number":"216-890-4567"}],"gender":"female"},
        {"first_name": "Lora",       "last_name": "Pearson",   "phone":[{"type":"mobile","number":"216-123-4567"},{"type":"home","number":"216-890-4567"}],"gender":"female"},
        {"first_name": "Katie",      "last_name": "Watts",     "phone":[{"type":"mobile","number":"216-123-4567"},{"type":"home","number":"216-890-4567"}],"gender":"female"},
        {"first_name": "Taylor",     "last_name": "Jennings",  "phone":[{"type":"mobile","number":"216-123-4567"},{"type":"home","number":"216-890-4567"}],"gender":"female"},
        {"first_name": "Pamela",     "last_name": "Buchanan",  "phone":[{"type":"mobile","number":"216-123-4567"},{"type":"home","number":"216-890-4567"}],"gender":"female"},
        {"first_name": "Mark",       "last_name": "Potter",    "phone":[{"type":"mobile","number":"440-123-4567"},{"type":"home","number":"440-890-4567"}],"gender":"male"},
        {"first_name": "Domingo",    "last_name": "Allen",     "phone":[{"type":"mobile","number":"440-123-4567"},{"type":"home","number":"440-890-4567"}],"gender":"male"},
        {"first_name": "Jean",       "last_name": "Kim",       "phone":[{"type":"mobile","number":"216-123-4567"},{"type":"home","number":"216-890-4567"}],"gender":"female"},
        {"first_name": "Monique",    "last_name": "Sullivan",  "phone":[{"type":"mobile","number":"216-123-4567"},{"type":"home","number":"216-890-4567"}],"gender":"female"},
        {"first_name": "Theresa",    "last_name": "Beck",      "phone":[{"type":"mobile","number":"216-123-4567"},{"type":"home","number":"216-890-4567"}],"gender":"female"},
        {"first_name": "Morris",     "last_name": "Moreno",    "phone":[                                          {"type":"home","number":"440-890-4567"}],"gender":"male"},
        {"first_name": "Wade",       "last_name": "Luna",      "phone":[{"type":"mobile","number":"440-123-4567"},{"type":"home","number":"440-890-4567"}],"gender":"male"},
        {"first_name": "Traci",      "last_name": "Garcia",    "phone":[{"type":"mobile","number":"216-123-4567"},{"type":"home","number":"216-890-4567"}],"gender":"female"},
        {"first_name": "Alan",       "last_name": "Washington","phone":[{"type":"mobile","number":"440-123-4567"},{"type":"home","number":"440-890-4567"}],"gender":"male"},
        {"first_name": "Toni",       "last_name": "Weaver",    "phone":[{"type":"mobile","number":"216-123-4567"},{"type":"home","number":"216-890-4567"}],"gender":"female"},
        {"first_name": "David",      "last_name": "Brown",     "phone":[{"type":"mobile","number":"440-123-4567"},{"type":"home","number":"440-890-4567"}],"gender":"male"},
        {"first_name": "Sergio",     "last_name": "Marsh",     "phone":[{"type":"mobile","number":"440-123-4567"},{"type":"home","number":"440-890-4567"}],"gender":"male"},
        {"first_name": "Charlie",    "last_name": "Rice",      "phone":[{"type":"mobile","number":"440-123-4567"},{"type":"home","number":"440-890-4567"}],"gender":"male"},
        {"first_name": "Bernard",    "last_name": "Hogan",     "phone":[{"type":"mobile","number":"440-123-4567"},{"type":"home","number":"440-890-4567"}],"gender":"male"} 
  ]
}
'''

data = json.loads(JSON)

# how many friends do I have?
assert(len(getFromPPath(data,'friends'))==50)
# how many friends with the last name brown?
assert(len(getFromPPath(data,'friends[last_name=Brown]'))==2)
# What are the first names of the friends whose last names are brown?
assert(getFromPPath(data,'friends[last_name=Brown].first_name')[0]=="Rachael")
assert(getFromPPath(data,'friends[last_name=Brown].first_name')[1]=="David")
# what's the first name of the person with this phone number?
assert(len(getFromPPath(data,"friends[phone.number=216-890-7830].first_name"))==1)
assert(getFromPPath(data,"friends[phone.number=216-890-7830].first_name")[0]=="Jeanne")
# what about this phone number?
assert(len(getFromPPath(data,"friends[phone.number=216-123-0079].first_name"))==1)
assert(getFromPPath(data,"friends[phone.number=216-123-0079].first_name")[0]=="Jeanne")
# How many friends are female?
assert(len(getFromPPath(data,"friends[gender=female]"))==28)
# First names of female friends
assert(len(getFromPPath(data,"friends[gender=female].first_name"))==28)
assert(getFromPPath(data,"friends[gender=female].first_name")[0]=="Jeanne")
assert(getFromPPath(data,"friends[gender=female].first_name")[10]=="Sylvia")
assert(getFromPPath(data,"friends[gender=female].first_name")[27]=="Toni")
# Phone numbers of female friends
assert(len(getFromPPath(data,"friends[gender=female].phone.number"))==56)
assert(getFromPPath(data,"friends[gender=female].phone.number")[0]=="216-123-0079")
assert(getFromPPath(data,"friends[gender=female].phone.number")[10]=="216-123-4567")
assert(getFromPPath(data,"friends[gender=female].phone.number")[55]=="216-890-4567")
# Mobile phone numbers of female friends
assert(len(getFromPPath(data,"friends[gender=female].phone[type=mobile].number"))==28)
assert(getFromPPath(data,"friends[gender=female].phone[type=mobile].number")[0]=="216-123-0079")
assert(getFromPPath(data,"friends[gender=female].phone[type=mobile].number")[10]=="216-123-4567")
assert(getFromPPath(data,"friends[gender=female].phone[type=mobile].number")[27]=="216-123-4567")
# Male friends
assert(len(getFromPPath(data,"friends[gender=male]"))==22)
# Male friends with mobile phones
assert(len(getFromPPath(data,"friends[gender=male][phone.type=mobile]"))==21)
# Male friends with home phones
assert(len(getFromPPath(data,"friends[gender=male][phone.type=home]"))==22)
# First names of male friends with home phones
assert(getFromPPath(data,"friends[gender=male][phone.type=home].first_name")[0]=="Wendell")
assert(getFromPPath(data,"friends[gender=male][phone.type=home].first_name")[10]=="Daryl")
assert(getFromPPath(data,"friends[gender=male][phone.type=home].first_name")[21]=="Bernard")
# Male friends with fax numbers
assert(len(getFromPPath(data,"friends[gender=male][phone.type=fax]"))==0)




