"""
Extract data from Google Maps Geocoding API using Arcpy
"""


def address_table_to_shape(inTable, id_address_field, address_field, outShp,
                           epsg=4326, sheet_name=None,
                           door_number=None, zip4=None, zip3=None,
                           city=None, language=None, useComponents=None,
                           country=None):
    """
    Receive a table with a list of addresses and use the Google Maps API
    to get their position
    
    Preferred Address Structure:
    Rua+dos+Combatentes+da+Grande+Guerra,+14,+3030-185,+Coimbra
    
    Table Structure:
    ID | address | door_number | zip4 | zip3 | city
    ID field and address field are demandatory
    """
    
    import arcpy
    import os
    
    from gasp.arcgis                import create_feat_class
    from gasp.arcgis                import feature_lyr
    from gasp.arcgis.manage.fields  import add_field
    from gasp.arcgis.manage.fields  import get_fields_properties
    from gasp.arcgis.manage.fields  import get_field_type_str
    from gasp.fromapi.glg.geocoding import get_position
    
    def see_if_field_is_there(cols, f1, f2):
        if f1 not in cols or f2 not in cols:
            raise ValueError((
                '{} and/or {} does not exist in {}'
            ).format(
                f1, f2,
                os.path.splitext(os.path.basename(inTable))[0]
            ))
    
    def see_if_field_is_there2(cols, f):
        if f and f not in COLUMNS:
            raise ValueError((
                '{} does not exist in {}'
            ).format(
                f,
                os.path.splitext(os.path.basename(inTable))[0])
            )
    
    arcpy.env.overwriteOutput = True
    
    # If inTable is a excel table, convert table to dbf
    tableFormat = os.path.splitext(inTable)[1]
    
    if tableFormat == '.xls' or tableFormat == '.xlsx':
        from gasp.xls.fields   import columns_by_order
        from gasp.todbf.arcgis import excel_to_table
        
        # Check if all given columns are there
        COLUMNS = columns_by_order(inTable, sheet_name=sheet_name)
        see_if_field_is_there(COLUMNS, id_address_field, address_field)
        see_if_field_is_there2(COLUMNS, door_number)
        see_if_field_is_there2(COLUMNS, zip4)
        see_if_field_is_there2(COLUMNS, zip3)
        see_if_field_is_there2(COLUMNS, city)
        
        ADDRESS_TABLE = os.path.join(
            os.path.dirname(outShp),
            os.path.splitext(os.path.basename(inTable))[0] + '.dbf'
        )
        
        if sheet_name:
            excel_to_table(inTable, ADDRESS_TABLE, sheetname=sheet_name)
        
        else:
            excel_to_table(inTable, ADDRESS_TABLE, sheetindex=0)
    
    else:
        # Check if all given columns are there
        from gasp.arcgis.manage.fields import list_fields
        
        COLUMNS = list_fields(inTable)
        see_if_field_is_there(COLUMNS, id_address_field, address_field)
        see_if_field_is_there2(COLUMNS, door_number)
        see_if_field_is_there2(COLUMNS, zip4)
        see_if_field_is_there2(COLUMNS, zip3)
        see_if_field_is_there2(COLUMNS, city)
        
        ADDRESS_TABLE = inTable
    
    # Get Addresses from table
    cursor = arcpy.SearchCursor(ADDRESS_TABLE)
    
    line = cursor.next()
    __ADDRESS__ = {}
    while line:
        # Get Entity ID
        FID = line.getValue(id_address_field)
        
        # Get Full ADDRESS
        if not useComponents:
            if door_number and zip4 and zip3 and city:
                addr = u'{stt},{door},{z4}-{z3},{cit}'.format(
                    stt  = line.getValue(address_field).replace(' ', '+'),
                    door = line.getValue(door_number).replace(' ', '+'),
                    z4   = line.getValue(zip4).replace(' ', '+'),
                    z3   = line.getValue(zip3).replace(' ', '+'),
                    cit  = line.getValue(city).replace(' ', '+')
                )
        
            elif not door_number and zip4 and zip3 and city:
                addr = u'{stt},{z4}-{z3},{cit}'.format(
                    stt  = line.getValue(address_field).replace(' ', '+'),
                    z4   = line.getValue(zip4).replace(' ', '+'),
                    z3   = line.getValue(zip3).replace(' ', '+'),
                    cit  = line.getValue(city).replace(' ', '+')
                )
        
            elif door_number and not zip4 and not zip3 and not city:
                addr = u'{stt},{door}'.format(
                    stt  = line.getValue(address_field).replace(' ', '+'),
                    door = line.getValue(door_number).replace(' ', '+')
                )
        
            elif not door_number and not zip4 and not zip3 and not city:
                addr = u'{stt}'.format(
                    stt  = line.getValue(address_field).replace(' ', '+')
                )
        
            elif door_number and zip4 and not zip3 and city:
                addr = u'{stt},{door},{z4},{cit}'.format(
                    stt  = line.getValue(address_field).replace(' ', '+'),
                    door = line.getValue(door_number).replace(' ', '+'),
                    z4   = line.getValue(zip4).replace(' ', '+'),
                    cit  = line.getValue(city).replace(' ', '+')
                )
        
            else:
                raise ValueError('Parameters are not valid!')
            
            glg_response = get_position(addr)
        
        else:
            addr = u'{},{}'.format(
                    line.getValue(address_field).replace(' ', '+'),
                    line.getValue(door_number).replace(' ', '+')
                ) if door_number else line.getValue(address_field).replace(' ', '+')
            
            _locality = line.getValue(city).replace(' ', '+') if city else None
            
            if zip4 and zip3:
                _postal = u'{}-{}'.format(
                    line.getValue(zip4).replace(' ', '+'),
                    line.getValue(zip3).replace(' ', '+')
                )
            
            elif zip4 and not zip3:
                _postal = line.getValue(zip4).replace(' ', '+')
            
            else: _postal = None
            
            glg_response = get_position(
                addr,
                country=country,
                language=language,
                locality=_locality,
                postal_code=_postal
            )
        
        __ADDRESS__[FID] = {
            address_field   : line.getValue(address_field),
            'GADDRESS'      : addr,
            'GOOGLEDT'      : glg_response
        }
        
        if door_number:
            __ADDRESS__[FID][door_number] = line.getValue(door_number)
        
        if zip4:
            __ADDRESS__[FID][zip4] = line.getValue(zip4)
        
        if zip3:
            __ADDRESS__[FID][zip3] = line.getValue(zip3)
        
        if city:
            __ADDRESS__ [FID][city] = line.getValue(city)
        
        line = cursor.next()
    
    # Write Output
    out = create_feat_class(outShp, "POINT", epsg)
    outLyr = feature_lyr(out)
    
    # Create outFields
    FIELDS_PROPERTIES = get_fields_properties(ADDRESS_TABLE)
    
    out_fields = {
        id_address_field : FIELDS_PROPERTIES[id_address_field],
        address_field    : FIELDS_PROPERTIES[address_field],
        'G_ADRS'         : {'LENGTH' : 254, 'TYPE' : 'String'},
        'STREET'         : {'LENGTH' : 254, 'TYPE' : 'String'},
        'PORT'           : {'LENGTH' :  10, 'TYPE' : 'String'},
        'ZIPCODE'        : {'LENGTH' :  10, 'TYPE' : 'String'},
    }
    
    if door_number:
        out_fields[door_number] = FIELDS_PROPERTIES[door_number]
    
    if zip4:
        out_fields[zip4] = FIELDS_PROPERTIES[zip4]
    
    if zip3:
        out_fields[zip3] = FIELDS_PROPERTIES[zip3]
    
    if city:
        out_fields[city] = FIELDS_PROPERTIES[city]
    
    for field in out_fields:
        add_field(
            outLyr, field,
            get_field_type_str(out_fields[field]['TYPE']),
            out_fields[field]['LENGTH']
        )
    
    # Write geom data
    cursor = arcpy.InsertCursor(outLyr)
    
    for pnt in __ADDRESS__:
        if not __ADDRESS__[pnt]['GOOGLEDT']:
            continue
        
        pntX = __ADDRESS__[pnt]['GOOGLEDT'][0]["geometry"]["location"]["lng"]
        pntY = __ADDRESS__[pnt]['GOOGLEDT'][0]["geometry"]["location"]["lat"]
        
        if epsg != 4326:
            newPnt = arcpy.PointGeometry(
                arcpy.Point(pntX, pntY),
                arcpy.SpatialReference(4326),
                arcpy.SpatialReference(epsg)
            )
        
        else:
            newPnt = arcpy.CreateObject("Point")
            newPnt.X, newPnt.Y = pntX, pntY
        
        new_row = cursor.newRow()
        new_row.Shape = newPnt
        
        new_row.setValue(id_address_field, pnt)
        
        for k in __ADDRESS__[pnt]:
            if k != 'GOOGLEDT' and k != 'GADDRESS':
                new_row.setValue(k, __ADDRESS__[pnt][k])
        
        new_row.setValue('G_ADRS',
            __ADDRESS__[pnt]['GOOGLEDT'][0]["formatted_address"]
        )
        
        for i in __ADDRESS__[pnt]['GOOGLEDT'][0]["address_components"]:
            if i["types"][0] == 'street_number':
                F = 'PORT'
            
            elif i["types"][0] == 'route':
                F = 'STREET'
            
            elif i["types"][0] == 'postal_code':
                F = 'ZIPCODE'
            
            else: continue
            
            new_row.setValue(F, i["long_name"])
        
        cursor.insertRow(new_row)
    
    return outShp

