"""Loosely formatted coordinate files from KMS

Description:
------------

Read files from old KMS software. The files are generally not structured in a
manner that is particularly readable and the format is very loosely defined.
Hence it is only possible to read a subset of the possible data in a KMS file.

Example files:
--------------

    kms\koordinater
    kms\koter
"""
# Standard library imports
import re
from typing import IO

# Posetta imports
from posetta import data
from posetta.lib import plugins
from posetta.readers._reader_line import Reader


@plugins.register
class KmsReader(Reader):
    """A reader for KMS files
    """

    def __init__(self, input_stream: IO[bytes], encoding: str = "latin-1") -> None:
        """Set up the basic information needed by the reader

        Kms files will usually, if not always, be generated by software
        created before UTF-8 was thought of... So do latin-1 as default
        encoding.

        Args:
            input_stream:  Byte stream that will be read.
        """
        super().__init__(input_stream, encoding)

        fields = [
            "easting",
            "northing",
            "elevation",
            "epoch",
            "minilabel",
            "comment",
            "station",
        ]
        self.data = {f: [] for f in fields}

    def setup_reader(self) -> None:
        self.meta["__params__"] = dict(names=True)

    def read_data(self) -> None:
        """
        Read KMS coordinate file.
        """
        text = self.input_stream.read()
        text = re.sub(r"\*.*?;", "", text, flags=re.DOTALL)  # Remove comment lines
        text = re.sub(r"\s+\n", "\n", text, flags=re.MULTILINE)  # Remove empty lines
        lines = text.split("\n")

        for line in lines:
            if line[0:17].strip().startswith("#"):
                kms_label = line.strip()
            elif line[0:17].strip().startswith("-1z"):
                kms_label = ""
            else:
                northing = None
                easting = None
                elevation = None

                if line and line[0] != " ":
                    continue

                station_id = line[0:18].replace(" ", "")
                # station_descr = line[18:21].replace(" ", "")
                cols = re.split("  +", line[21:].strip())
                if len(cols) == 1 and cols[0] == "":
                    continue

                elements = []
                for i, element in enumerate(cols):
                    value = None
                    unit = None
                    if element.strip().endswith((" m", " dg", " rad")):
                        value = "".join(element.strip().split(" ")[:-1])
                        unit = element.strip().split(" ")[-1]

                    if element.strip().endswith(("\dm", "\ddg", "\drad")):
                        value = re.findall(r"[-+]?\d*\.\d+|\d+", element.strip())[0]
                        unit = re.findall(r"\w$", element.strip())[0]

                    if element.strip().endswith((" sx", " nt")):
                        value = element.strip()
                        unit = element.strip().split(" ")[-1]

                    if value is not None:
                        elements.append(float(value))

                if len(elements) == 3:
                    easting = elements[1]
                    northing = elements[0]
                    elevation = elements[2]

                if len(elements) == 2:
                    easting = elements[1]
                    northing = elements[0]
                    elevation = 0

                if len(elements) == 1:
                    easting = 0
                    northing = 0
                    elevation = elements[0]

                if len(elements) > 0:
                    self.data["comment"].append(" ".join(cols[len(elements):]))
                    self.data["minilabel"].append(kms_label)
                    self.data["station"].append(station_id)
                    if easting is not None:
                        self.data["easting"].append(easting)
                    if northing is not None:
                        self.data["northing"].append(northing)
                    if elevation is not None:
                        self.data["elevation"].append(elevation)

    def as_coordset(self) -> data.CoordSet:
        """Return the data as a coordinate dataset

        Returns:
            The data that has been read as a coordinate dataset.
        """
        cset = data.CoordSet()

        # add coordinates
        if all(v is not None for v in self.data["easting"]):
            cset.add("positions", self.data["easting"], 0, "easting")
        if all(v is not None for v in self.data["northing"]):
            cset.add("positions", self.data["northing"], 1, "northing")
        if all(v is not None for v in self.data["elevation"]):
            cset.add("positions", self.data["elevation"], 2, "elevation")

        # add whatever metadata is available
        if all(v is not None for v in self.data["station"]):
            cset.add("values", self.data["station"], None, "station")
        if all(v is not None for v in self.data["minilabel"]):
            cset.add("values", self.data["minilabel"], None, "minilabel")
        if all(v is not None for v in self.data["comment"]):
            cset.add("values", self.data["comment"], None, "comment")

        return cset
