# -*- coding: UTF-8 -*-
"""
   Copyright 2016 Esri
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at
       http://www.apache.org/licenses/LICENSE-2.0
   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.​
    This sample adds and updates GNSS metadata fields in a feature class.
"""
import arcpy
import argparse
import os

def get_geodatabase_path(input_layer):
    """
    Gets the parent geodatabase of the layer
    :param input_layer: (string) The feature layer to get the parent database of
    :return: (string) The path to the geodatabase
    """
    workspace = os.path.dirname(arcpy.Describe(input_layer).catalogPath)
    if [any(ext) for ext in ('.gdb', '.mdb', '.sde') if ext in os.path.splitext(workspace)]:
        return workspace
    else:
        return os.path.dirname(workspace)

def check_and_create_domains(geodatabase):
    """
    Checks if the domains already exist, if they do
    then it checks the values and ranges

    If the domains do not exist, they are created

    :param geodatabase: (string) the path to the geodatabase to check
    :return:
    """
    domains = arcpy.da.ListDomains(geodatabase)
    domain_names = [domain.name for domain in domains]
    if 'ESRI_FIX_TYPE_DOMAIN' in domain_names:
        for domain in domains:
            if domain.name == 'ESRI_FIX_TYPE_DOMAIN':
                # check if cvs 0,1,2,4,5 are in the codedValues
                values = [cv for cv in domain.codedValues]
                if not set(set([0, 1, 2, 4, 5])).issubset(values):
                    arcpy.AddError("ESRI_FIX_TYPE_DOMAIN is missing a coded value pair.")
                    return
    else:
        # Add the domain and values
        arcpy.AddMessage('Adding ESRI_FIX_TYPE_DOMAIN domain to parent geodatabase...')
        
        arcpy.CreateDomain_management(in_workspace=geodatabase,
                                      domain_name="ESRI_FIX_TYPE_DOMAIN",
                                      domain_description="Fix Type",
                                      field_type="SHORT",
                                      domain_type="CODED",
                                      split_policy="DEFAULT",
                                      merge_policy="DEFAULT")

        arcpy.AddCodedValueToDomain_management(in_workspace=geodatabase,
                                               domain_name="ESRI_FIX_TYPE_DOMAIN",
                                               code="0",
                                               code_description="Fix not valid")
        arcpy.AddCodedValueToDomain_management(in_workspace=geodatabase,
                                               domain_name="ESRI_FIX_TYPE_DOMAIN",
                                               code="1",
                                               code_description="GPS")
        arcpy.AddCodedValueToDomain_management(in_workspace=geodatabase,
                                               domain_name="ESRI_FIX_TYPE_DOMAIN",
                                               code="2",
                                               code_description="Differential GPS")
        arcpy.AddCodedValueToDomain_management(in_workspace=geodatabase,
                                               domain_name="ESRI_FIX_TYPE_DOMAIN",
                                               code="4",
                                               code_description="RTK Fixed")
        arcpy.AddCodedValueToDomain_management(in_workspace=geodatabase,
                                               domain_name="ESRI_FIX_TYPE_DOMAIN",
                                               code="5",
                                               code_description="RTK Float")
    # Check if 'NumSats" is a domain, if so check the range
    if 'ESRI_NUM_SATS_DOMAIN' in domain_names:
        if domain.name == "ESRI_NUM_SATS_DOMAIN":
            if domain.range[0] != 0 or domain.range[1] != 99:
                arcpy.AddError("ESRI_NUM_SATS_DOMAIN domain has invalid range")
                return
    else:
        # Add the domain and set the range
        arcpy.AddMessage("Adding ESRI_NUM_SATS_DOMAIN to parent database...")
        arcpy.CreateDomain_management(in_workspace=geodatabase,
                                      domain_name="ESRI_NUM_SATS_DOMAIN",
                                      domain_description="Number of Satellites",
                                      field_type="SHORT",
                                      domain_type="RANGE",
                                      split_policy="DEFAULT",
                                      merge_policy="DEFAULT")
        arcpy.SetValueForRangeDomain_management(geodatabase, "ESRI_NUM_SATS_DOMAIN", 0, 99)
    # Check if 'StationID" is a domain, if so check the range
    if 'ESRI_STATION_ID_DOMAIN' in domain_names:
        if domain.name == "ESRI_STATION_ID_DOMAIN":
            if domain.range[0] != 0 or domain.range[1] != 1023:
                arcpy.AddError("ESRI_STATION_ID_DOMAIN domain has invalid range")
                return
    else:
        # Add the domain and set the range
        arcpy.AddMessage("Adding ESRI_STATION_ID_DOMAIN to parent database...")
        arcpy.CreateDomain_management(in_workspace=geodatabase,
                                      domain_name="ESRI_STATION_ID_DOMAIN",
                                      domain_description="Station ID",
                                      field_type="SHORT",
                                      domain_type="RANGE",
                                      split_policy="DEFAULT",
                                      merge_policy="DEFAULT")
        arcpy.SetValueForRangeDomain_management(geodatabase, "ESRI_STATION_ID_DOMAIN", 0, 1023)
    if 'ESRI_POSITIONSOURCETYPE_DOMAIN' in domain_names:
        for domain in domains:
            if domain.name == 'ESRI_POSITIONSOURCETYPE_DOMAIN':
                # check if cvs 0,1,2,3,4 are in the codedValues
                values = [cv for cv in domain.codedValues]
                if not set(set([0, 1, 2, 3, 4])).issubset(values):
                    arcpy.AddError("ESRI_POSITIONSOURCETYPE_DOMAIN is missing a coded value pair.")
                    return
    else:
        # Add the domain and values
        arcpy.AddMessage('Adding ESRI_POSITIONSOURCETYPE_DOMAIN domain to parent geodatabase...')

        arcpy.CreateDomain_management(in_workspace=geodatabase,
                                      domain_name="ESRI_POSITIONSOURCETYPE_DOMAIN",
                                      domain_description="Position Source Type",
                                      field_type="SHORT",
                                      domain_type="CODED",
                                      split_policy="DEFAULT",
                                      merge_policy="DEFAULT")

        arcpy.AddCodedValueToDomain_management(in_workspace=geodatabase,
                                               domain_name="ESRI_POSITIONSOURCETYPE_DOMAIN",
                                               code="0",
                                               code_description="Unknown")
        arcpy.AddCodedValueToDomain_management(in_workspace=geodatabase,
                                               domain_name="ESRI_POSITIONSOURCETYPE_DOMAIN",
                                               code="1",
                                               code_description="User defined")
        arcpy.AddCodedValueToDomain_management(in_workspace=geodatabase,
                                               domain_name="ESRI_POSITIONSOURCETYPE_DOMAIN",
                                               code="2",
                                               code_description="Integrated (System) Location Provider")
        arcpy.AddCodedValueToDomain_management(in_workspace=geodatabase,
                                               domain_name="ESRI_POSITIONSOURCETYPE_DOMAIN",
                                               code="3",
                                               code_description="External GNSS Receiver")
        arcpy.AddCodedValueToDomain_management(in_workspace=geodatabase,
                                               domain_name="ESRI_POSITIONSOURCETYPE_DOMAIN",
                                               code="4",
                                               code_description="Network Location Provider")

def add_gnss_fields(feature_layer):
    """
    This adds specific fields required for GPS units to
        auto-populate in collector application

        This will report errors if:
            1) Any of the fields already exist
            2) The input layer is not a point layer
            3) The layer is not found
            4) The layer is a shapefile

    Example: add_gps_fields(r"C:/temp/test.shp")

    :param feature_layer: (string) The feature layer (shapefile, feature class, etc) to add the fields to
    :return:
    """

    try:
        if not arcpy.Exists(feature_layer):
            arcpy.AddError("Feature layer: {} not found!".format(feature_layer))
            return
        if arcpy.Describe(feature_layer).shapeType != "Point":
            arcpy.AddError("Feature layer: {} is not a point layer".format(feature_layer))
            return
        if arcpy.Describe(feature_layer).dataType == "Feature Layer":
            if arcpy.Describe(feature_layer).dataElement.dataType == "ShapeFile":
                arcpy.AddError("ShapeFiles are not supported.")
                return
        elif arcpy.Describe(feature_layer).dataType == "ShapeFile":
            arcpy.AddError("ShapeFiles are not supported.")
            return

        # check if it's a service or feature class in db
        # Check the domains to see if they exist and are valid
        # will update if necessary
        
        if r'/rest/services' not in arcpy.Describe(feature_layer).catalogPath:
            arcpy.AddMessage('Checking domains...')
            geodatabase = get_geodatabase_path(feature_layer)
            check_and_create_domains(geodatabase)

        # Add the fields
        arcpy.AddMessage('Adding Required Fields...')
        
        # Add GNSS metadata fields
        existingFields = [field.name for field in arcpy.ListFields(feature_layer)]

        if 'ESRIGNSS_POSITIONSOURCETYPE' not in existingFields:
            arcpy.AddField_management(feature_layer,
                                      'ESRIGNSS_POSITIONSOURCETYPE',
                                      field_type="SHORT",
                                      field_alias='Position source type',
                                      field_is_nullable="NULLABLE",
                                      field_domain="ESRI_POSITIONSOURCETYPE_DOMAIN"
                                      )
        
        if 'ESRIGNSS_RECEIVER' not in existingFields:
            arcpy.AddField_management(feature_layer,
                                      'ESRIGNSS_RECEIVER',
                                      field_type="STRING",
                                      field_length=50,
                                      field_alias='Receiver Name',
                                      field_is_nullable="NULLABLE"
                                      )

        if 'ESRIGNSS_LATITUDE' not in existingFields:
            arcpy.AddField_management(feature_layer,
                                      'ESRIGNSS_LATITUDE',
                                      field_type="DOUBLE",
                                      field_alias='Latitude',
                                      field_is_nullable="NULLABLE"
                                      )

        if 'ESRIGNSS_LONGITUDE' not in existingFields:
            arcpy.AddField_management(feature_layer,
                                      'ESRIGNSS_LONGITUDE',
                                      field_type="DOUBLE",
                                      field_alias='Longitude',
                                      field_is_nullable="NULLABLE"
                                      )

        if 'ESRIGNSS_ALTITUDE' not in existingFields:
            arcpy.AddField_management(feature_layer,
                                      'ESRIGNSS_ALTITUDE',
                                      field_type="DOUBLE",
                                      field_alias='Altitude',
                                      field_is_nullable="NULLABLE"
                                      )
        
        if 'ESRIGNSS_H_RMS' not in existingFields:
            arcpy.AddField_management(feature_layer,
                                      'ESRIGNSS_H_RMS',
                                      field_type="DOUBLE",
                                      field_alias='Horizontal Accuracy (m)',
                                      field_is_nullable="NULLABLE"
                                      )
        
        if 'ESRIGNSS_V_RMS' not in existingFields:
            arcpy.AddField_management(feature_layer,
                                      'ESRIGNSS_V_RMS',
                                      field_type="DOUBLE",
                                      field_alias='Vertical Accuracy (m)',
                                      field_is_nullable="NULLABLE"
                                      )

        if 'ESRIGNSS_FIXDATETIME' not in existingFields:
            arcpy.AddField_management(feature_layer,
                                      'ESRIGNSS_FIXDATETIME',
                                      field_type="Date",
                                      field_alias='Fix Time',
                                      field_is_nullable="NULLABLE",
                                      )

        if 'ESRIGNSS_FIXTYPE' not in existingFields:
            arcpy.AddField_management(feature_layer,
                                      'ESRIGNSS_FIXTYPE',
                                      field_type="SHORT",
                                      field_alias='Fix Type',
                                      field_is_nullable="NULLABLE",
                                      field_domain="ESRI_FIX_TYPE_DOMAIN"
                                      )

        if 'ESRIGNSS_CORRECTIONAGE' not in existingFields:
            arcpy.AddField_management(feature_layer,
                                      'ESRIGNSS_CORRECTIONAGE',
                                      field_type="DOUBLE",
                                      field_alias='Correction Age',
                                      field_is_nullable="NULLABLE"
                                      )

        if 'ESRIGNSS_STATIONID' not in existingFields:
            arcpy.AddField_management(feature_layer,
                                      'ESRIGNSS_STATIONID',
                                      field_type="SHORT",
                                      field_alias='Station ID',
                                      field_is_nullable="NULLABLE",
                                      field_domain="ESRI_STATION_ID_DOMAIN"
                                      )

        if 'ESRIGNSS_NUMSATS' not in existingFields:
            arcpy.AddField_management(feature_layer,
                                      'ESRIGNSS_NUMSATS',
                                      field_type="SHORT",
                                      field_alias='Number of Satellites',
                                      field_is_nullable="NULLABLE",
                                      field_domain="ESRI_NUM_SATS_DOMAIN"
                                      )

        if 'ESRIGNSS_PDOP' not in existingFields:
            arcpy.AddField_management(feature_layer,
                                      'ESRIGNSS_PDOP',
                                      field_type="DOUBLE",
                                      field_alias='PDOP',
                                      field_is_nullable="NULLABLE"
                                      )                                  

        if 'ESRIGNSS_HDOP' not in existingFields:
            arcpy.AddField_management(feature_layer,
                                      'ESRIGNSS_HDOP',
                                      field_type="DOUBLE",
                                      field_alias='HDOP',
                                      field_is_nullable="NULLABLE"
                                      )

        if 'ESRIGNSS_VDOP' not in existingFields:
            arcpy.AddField_management(feature_layer,
                                  'ESRIGNSS_VDOP',
                                  field_type="DOUBLE",
                                  field_alias='VDOP',
                                  field_is_nullable="NULLABLE"
                                  )

        if 'ESRIGNSS_DIRECTION' not in existingFields:
            arcpy.AddField_management(feature_layer,
                                      'ESRIGNSS_DIRECTION',
                                      field_type="DOUBLE",
                                      field_alias='Direction of travel (°)',
                                      field_is_nullable="NULLABLE"
                                      )

        if 'ESRIGNSS_SPEED' not in existingFields:
            arcpy.AddField_management(feature_layer,
                                      'ESRIGNSS_SPEED',
                                      field_type="DOUBLE",
                                      field_alias='Speed (km/h)',
                                      field_is_nullable="NULLABLE"
                                      )

        if 'ESRISNSR_AZIMUTH' not in existingFields:
            arcpy.AddField_management(feature_layer,
                                      'ESRISNSR_AZIMUTH',
                                      field_type="DOUBLE",
                                      field_alias='Compass reading (°)',
                                      field_is_nullable="NULLABLE"
                                          )

        if 'ESRIGNSS_AVG_H_RMS' not in existingFields:
            arcpy.AddField_management(feature_layer,
                                      'ESRIGNSS_AVG_H_RMS',
                                      field_type="DOUBLE",
                                      field_alias='Average Horizontal Accuracy (m)',
                                      field_is_nullable="NULLABLE",
                                      )
        
        if 'ESRIGNSS_AVG_V_RMS' not in existingFields:
            arcpy.AddField_management(feature_layer,
                                      'ESRIGNSS_AVG_V_RMS',
                                      field_type="DOUBLE",
                                      field_alias='Average Vertical Accuracy (m)',
                                      field_is_nullable="NULLABLE",
                                      )
        
        if 'ESRIGNSS_AVG_POSITIONS' not in existingFields:
            arcpy.AddField_management(feature_layer,
                                      'ESRIGNSS_AVG_POSITIONS',
                                      field_type="SHORT",
                                      field_alias='Averaged Positions',
                                      field_is_nullable="NULLABLE",
                                      )
        
        if 'ESRIGNSS_H_STDDEV' not in existingFields:
            arcpy.AddField_management(feature_layer,
                                      'ESRIGNSS_H_STDDEV',
                                      field_type="DOUBLE",
                                      field_alias='Standard Deviation (m)',
                                      field_is_nullable="NULLABLE",
                                      )
        
        arcpy.AddMessage("Successfully added GPS Metadata fields.\n")

        # Update fields with Domains
        arcpy.AddMessage('Updating Fields with Domains...')
        
        # Update GNSS metadata fields with Domains
        domainFields = [field for field in arcpy.ListFields(feature_layer) if field.name == 'ESRIGNSS_FIXTYPE' or \
                          field.name == 'ESRIGNSS_STATIONID' or field.name == 'ESRIGNSS_NUMSATS' or field.name == 'ESRIGNSS_POSITIONSOURCETYPE']
        
        for field in domainFields:
            if field.name == 'ESRIGNSS_FIXTYPE' and not field.domain:
                arcpy.AssignDomainToField_management(feature_layer, field, 'ESRI_FIX_TYPE_DOMAIN')
                continue
            
            if field.name == 'ESRIGNSS_STATIONID' and not field.domain:
                arcpy.AssignDomainToField_management(feature_layer, field, 'ESRI_STATION_ID_DOMAIN')
                continue
            
            if field.name == 'ESRIGNSS_NUMSATS' and not field.domain:
                arcpy.AssignDomainToField_management(feature_layer, field, 'ESRI_NUM_SATS_DOMAIN')
                continue

            if field.name == 'ESRIGNSS_POSITIONSOURCETYPE' and not field.domain:
                arcpy.AssignDomainToField_management(feature_layer, field, 'ESRI_POSITIONSOURCETYPE_DOMAIN')
                continue
            
        arcpy.AddMessage("Successfully updated GPS Metadata fields with domains.\n")
        
    except Exception as e:
        arcpy.AddError("{}\n".format(e))
        return

if __name__ == "__main__":
    """
        Commandline use to add fields to a layer

        Input: layer names (fully qualified paths)

        Example: python add_gps_fields "C:/temp/test.gdb/test" "C:/temp/test.gdb/test2"
    """
    parser = argparse.ArgumentParser("Add GPS Fields to Feature Layers")
    parser.add_argument("layers", nargs='+', help="The layers to add fields to")
    args = parser.parse_args()
    for layer in args.layers:
        add_gnss_fields(layer)