# -------------------------------------------------------------------
# ORTHOGRAPHIC
# Your personal aerial satellite. Always on. At any altitude.*
# Developed by MarStrMind
# License: Open Software License 3.0
# Up to date version always on marstr.online
# -------------------------------------------------------------------
# osmxml.py
# Performs calls to Overpass API to acquire XML files which we can 
# then store into a much faster SQLite3 database.
# -------------------------------------------------------------------


import xml.dom.minidom
import requests
import os
from defines import *
from log import *

class mstr_osmxml:
    def __init__(self, lat, lng):
        self._latv = lat
        self._lngv = lng
        self._lat = lat
        self._lng = lng
        self._curB_lat = lat + mstr_zl_18
        self._curB_lng = lng + mstr_zl_18

    
    # Adjust bbox for when this class should persost, but acquire data for a different bbox
    def adjust_bbox(self, lat, lng, lat_e, lng_e):
        self._lat = round(lat, 4)
        self._lng = round(lng, 4)
        self._curB_lat = round(lat_e, 4)
        self._curB_lng = round(lng_e, 4)
    

    # Acquire XMLs in chunks, then store them
    def acquire_osm(self, v, h, asobject=False):
        mstr_msg("osmxml", "Acquiring OSM data for " + str(self._lat)+","+str(self._lng)+" - "+str(self._curB_lat)+","+str(self._curB_lng))
        
        # We will use our self-hosted API for this.
        data = { 
            "bbox": { 
                    "lat": str(self._lat),
                    "lng": str(self._lng),
                    "lat_b": str(self._curB_lat),
                    "lng_b": str(self._curB_lng)
                },
                "tile_lat": str(self._lat),
                "tile_lng": str(self._lng),
                "square_lat": str(v),
                "square_lng": str(h)
                }
        r = requests.post(mstr_osm_endpoint, json=data)

        xmlf = mstr_datafolder + "_cache/tile.xml"
        if os.path.isfile(xmlf):
            os.remove(xmlf)
        with open(xmlf, 'wb') as textfile:
            textfile.write(r.content)

        # Provide the object directly
        if asobject == True:
            xml_doc = xml.dom.minidom.parse("_cache/tile.xml")
            return xml_doc


    # Get all nodes from the specified OSM file
    def acquire_nodes(self, xmlfile):
        xml_doc = xml.dom.minidom.parse(xmlfile)
        nodedata = xml_doc.getElementsByTagName("node")
        nodes = []
        for node in nodedata:
            p = (node.getAttribute("id"), node.getAttribute("lat"), node.getAttribute("lon"))
            nodes.append(p)
        return nodes
    

    # Get all waypoint data
    def acquire_waypoint_data(self, xmlfile):
        xml_doc = xml.dom.minidom.parse(xmlfile)
        wpdata = xml_doc.getElementsByTagName("way")
        wps = []
        for wp in wpdata:
            nddata = wp.getElementsByTagName("nd")
            for nd in nddata:
                p = (wp.getAttribute("id"), nd.getAttribute("ref"), "", "")
                wps.append(p)
        for wp in wpdata:
            tagdata = wp.getElementsByTagName("tag")
            for tag in tagdata:
                c = tag.getAttribute("v").replace(",", "")
                c = c.replace("'", "")
                p = (wp.getAttribute("id"), "NULL", tag.getAttribute("k"), c)
                wps.append(p)
        return wps
    

    # Checks if there is an airport or many airports with an ICAO code in the
    # supplied XML data chunk
    def find_icao_codes(self, xmlfile):
        icao = []
        xml_doc = xml.dom.minidom.parse(xmlfile)
        wpdata = xml_doc.getElementsByTagName("way")
        for wp in wpdata:
            tags = wp.getElementsByTagName("tag")
            for tag in tags:
                a = tag.getAttribute("k")
                if a == "icao":
                    v = tag.getAttribute("v")
                    icao.append(v)
        # Return list of found airports
        return icao
        

    # Finds the surface type of a runway in the current data chunk.
    # If no surface type is specified, the runway will be rendered similar
    # to how motorways are rendered.
    # This gets called only if some ICAO code was found
    def find_runway_surface(self, xmlfile):
        surface = ""
        wpid = ""
        xml_doc = xml.dom.minidom.parse(xmlfile)
        wpdata = xml_doc.getElementsByTagName("way")
        for wp in wpdata:
            tags = wp.getElementsByTagName("tag")
            for tag in tags:
                a = tag.getAttribute("k")
                v = tag.getAttribute("v")
                if a == "aeroway" and v == "runway":
                    wpid = wp.getAttribute("id")
                    break
        for wp in wpdata:
            wid = wp.getAttribute("id")
            if wid == wpid:
                tags = wp.getElementsByTagName("tag")
                for tag in tags:
                    a = tag.getAttribute("k")
                    v = tag.getAttribute("v")
                    if a == "surface":
                        surface = v

        # Return the found surface type
        return surface
    

    # It turns out that some features hide themselves in the relations section.
    # I figured this out during testing, and almost going insane over the
    # question as to why some parts like forests are missing in the masks, while
    # it is clearly labeled as forest on openstreetmap. We need to scan 
    # the relations and then scan through the outer and inner way IDs.
    #
    # Get all relation entries of importance.
    def acquire_relations(self, xmlfile):
        xml_doc = xml.dom.minidom.parse(xmlfile)
        rlxml = xml_doc.getElementsByTagName("relation")
        rls = []
        for rl in rlxml:
            rldata  = rl.getElementsByTagName("member")
            tagdata = rl.getElementsByTagName("tag")
            for rp in rldata:
                t = rp.getAttribute("role")
                if t == "inner" or t == "outer":
                    tgd = []
                    for i in tagdata:
                        tgd.append(i.getAttribute("k"))
                        tgd.append(i.getAttribute("v"))
                    rls.append((rp.getAttribute("ref"), tgd))
        return rls