from django.contrib.gis.geos import Point
from django.db.models import Q
from django.shortcuts import render

from functools import reduce
import json, operator, re

from shared.address import normalize_number, normalize_street
from shared.geo import geojson_data
from shared.request import make_response, ErrorResponse

from .models import Parcel, Attribute

def make_query(d, reducer=operator.and_):
    subqueries = []

    if "lng" in d and "lat" in d:
        try:
            lat = float(d["lat"])
            lng = float(d["lng"])
            subqueries.append(Q(shape__contains=Point(lng, lat, srid=4326)))
        except ValueError as err:
            raise ErrorResponse(
                "Bad parameter",
                {"error": "Bad value for latitude or longitude."},
                err=err)

    if "loc_id" in d:
        subqueries.append(Q(loc_id=d["loc_id"]))

    street = d.get("street")
    number = d.get("number")
    if "address" in d:
        m = re.match(r"(\d+)(-\d+)?\s", d["address"])
        if m:
            number = m.group(1)
            street = d["address"][m.end():]

    if street:
        sq = {"full_street": normalize_street(street)}

        if number:
            sq["address_num"] = normalize_number(number)

        subqueries.append(Q(**sq))

    if "types" in d:
        types = [t.upper() for t in re.split(r"\s*,\s*", d["types"])]
        subqueries.append(Q(poly_type__in=types))
    elif "include_row" not in d:
        subqueries.append(~Q(poly_type="ROW"))

    if d.get("mode") == "or":
        reducer = operator.or_

    return reduce(reducer, subqueries)

def parcels_for_request(req):
    """
    Helper function for retrieving the parcel(s) at a given latitude and
    longitude.
    """
    query = make_query(req.GET)
    queryset = Parcel.objects.filter(query).transform()

        # Include attributes
    if req.GET.get("attributes"):
        queryset = queryset.prefetch_related("attributes")

    if not req.GET.get("multi"):
        queryset = queryset[0:1]

    return queryset

def parcels_for_multi_request(req):
    #
    # [{ "lat": <lat>,
    #    "lng": <lng>,
    #    "id": <id> }]
    try:
        print(req.GET["points"])
        points = json.loads(req.GET["points"])
    except KeyError as kerr:
        raise ErrorResponse(
            "",
            {"error": "Missing required key: {}".format(kerr)})
    except ValueError:
        raise ErrorResponse(
            "",
            {"error": "Malformed 'points' value; must be valid JSON array: {}".format(e)})

    query = None
    for point in points:
        q = Q(shape__contains=Point(point["lng"], point["lat"], srid=4326))
        if query:
            query |= q
        else:
            query = qe

    return Parcel.objects.filter(query)

def make_parcel_data(parcel, include_attributes=False):
    d = geojson_data(parcel.shape)
    d["properties"] = {
        "type": parcel.poly_type,
        "loc_id": parcel.loc_id
    }

    if include_attributes:
        d["properties"].update(
            {a.name: a.value for a in parcel.attributes.all()})

    return d

@make_response()
def find_parcels(req):
    try:
        parcel = parcels_for_request(req)[0]

        include_attributes = req.GET.get("attributes", False)
    except IndexError:
        raise ErrorResponse(
            "No matching parcels found",
            status=404)
    return make_parcel_data(parcel, include_attributes=include_attributes)

@make_response()
def parcel_with_loc_id(req, loc_id=None):
    if not loc_id:
        loc_id = req.GET["loc_id"]

    parcel = Parcel.objects.get(loc_id=loc_id)
    return make_parcel_data(parcel, include_attributes=True)

@@ -220,16 +220,31 @@
 e_street
+, split_address
 %0Afrom sh
@@ -953,16 +953,31 @@
 %0A    if 
+not street and 
 %22address
@@ -1000,36 +1000,22 @@
 m = 
-re.match(r%22(%5Cd+)(-%5Cd+)?%5Cs%22, 
+split_address(
 d%5B%22a
@@ -1060,65 +1060,20 @@
 mber
- = m.group(1)%0A            street = d%5B%22address%22%5D%5Bm.end():%5D
+, street = m
 %0A%0A  
@@ -1250,24 +1250,123 @@
 d(Q(**sq))%0A%0A
+    if d.get(%22mode%22) == %22or%22:%0A        reducer = operator.or_%0A%0A    q = reduce(reducer, subqueries)%0A%0A
     if %22type
@@ -1452,34 +1452,27 @@
         
-subqueries.append(
+return q & 
 Q(poly_t
@@ -1485,17 +1485,16 @@
 n=types)
-)
 %0A    eli
@@ -1531,147 +1531,37 @@
     
-subqueries.append(~Q(poly_type=%22ROW%22))%0A%0A    if d.get(%22mode%22) == %22or%22:%0A        reducer = operator.or_%0A%0A    return reduce(reducer, subqueries
+return q & ~Q(poly_type=%22ROW%22
 )%0A%0Ad

Fix bug in parcel lookup