#!/usr/bin/env python
#
#   Project Horus - Browser-Based Chase Mapper - Config Reader
#
#   Copyright (C) 2018  Mark Jessop <vk5qi@rfhead.net>
#   Released under GNU GPL v3 or later
#
import logging
import os

try:
    # Python 2
    from ConfigParser import RawConfigParser
except ImportError:
    # Python 3
    from configparser import RawConfigParser


default_config = {
    # Start location for the map (until either a chase car position, or balloon position is available.)
    "default_lat": -34.9,
    "default_lon": 138.6,
    "default_alt": 0,
    "payload_max_age": 180,
    "thunderforest_api_key": "none",
    "stadia_api_key": "none",
    # Predictor settings
    "pred_enabled": True,  # Enable running and display of predicted flight paths.
    "offline_predictions": False,  # Use an offline GFS model and predictor instead of Tawhiri.
    # Default prediction settings (actual values will be used once the flight is underway)
    "pred_model": "Disabled",
    "pred_desc_rate": 6.0,
    "pred_burst": 28000,
    "show_abort": True,  # Show a prediction of an 'abort' paths (i.e. if the balloon bursts *now*)
    "pred_update_rate": 15,  # Update predictor every 15 seconds.
    # Range Rings
    "range_rings_enabled": False,
    "range_ring_quantity": 5,
    "range_ring_spacing": 1000,
    "range_ring_weight": 1.5,
    "range_ring_color": "red",
    "range_ring_custom_color": "#FF0000",
    # Chase Car Speedometer
    "chase_car_speed": True,
    # Bearing processing
    "max_bearings": 300,
    "max_bearing_age": 30 * 60,
    "car_speed_gate": 10,
    "bearing_length": 10,
    "bearing_weight": 1.0,
    "bearing_color": "black",
    "bearing_custom_color": "#FF0000",
    # History
    "reload_last_position": False,
}


def parse_config_file(filename):
    """ Parse a Configuration File """

    chase_config = default_config.copy()

    config = RawConfigParser()
    config.read(filename)

    # Map Defaults
    chase_config["flask_host"] = config.get("map", "flask_host")
    chase_config["flask_port"] = config.getint("map", "flask_port")
    chase_config["default_lat"] = config.getfloat("map", "default_lat")
    chase_config["default_lon"] = config.getfloat("map", "default_lon")
    chase_config["payload_max_age"] = config.getint("map", "payload_max_age")
    chase_config["thunderforest_api_key"] = config.get("map", "thunderforest_api_key")

    # GPSD Settings
    chase_config["car_gpsd_host"] = config.get("gpsd", "gpsd_host")
    chase_config["car_gpsd_port"] = config.getint("gpsd", "gpsd_port")

    # Serial GPS Settings
    chase_config["car_serial_port"] = config.get("gps_serial", "gps_port")
    chase_config["car_serial_baud"] = config.getint("gps_serial", "gps_baud")

    # Habitat Settings
    chase_config["habitat_upload_enabled"] = config.getboolean(
        "habitat", "habitat_upload_enabled"
    )
    chase_config["habitat_call"] = config.get("habitat", "habitat_call")
    chase_config["habitat_update_rate"] = config.getint(
        "habitat", "habitat_update_rate"
    )

    # Predictor
    chase_config["pred_enabled"] = config.getboolean("predictor", "predictor_enabled")
    chase_config["offline_predictions"] = config.getboolean(
        "predictor", "offline_predictions"
    )
    chase_config["pred_burst"] = config.getfloat("predictor", "default_burst")
    chase_config["pred_desc_rate"] = config.getfloat(
        "predictor", "default_descent_rate"
    )
    chase_config["pred_binary"] = config.get("predictor", "pred_binary")
    chase_config["pred_gfs_directory"] = config.get("predictor", "gfs_directory")
    chase_config["pred_model_download"] = config.get("predictor", "model_download")

    # Range Ring Settings
    chase_config["range_rings_enabled"] = config.getboolean(
        "range_rings", "range_rings_enabled"
    )
    chase_config["range_ring_quantity"] = config.getint(
        "range_rings", "range_ring_quantity"
    )
    chase_config["range_ring_spacing"] = config.getint(
        "range_rings", "range_ring_spacing"
    )
    chase_config["range_ring_weight"] = config.getfloat(
        "range_rings", "range_ring_weight"
    )
    chase_config["range_ring_color"] = config.get("range_rings", "range_ring_color")
    chase_config["range_ring_custom_color"] = config.get(
        "range_rings", "range_ring_custom_color"
    )

    # Bearing Processing
    chase_config["max_bearings"] = config.getint("bearings", "max_bearings")
    chase_config["max_bearing_age"] = (
        config.getint("bearings", "max_bearing_age") * 60
    )  # Convert to seconds
    if chase_config["max_bearing_age"] < 60:
        chase_config[
            "max_bearing_age"
        ] = 60  # Make sure this number is something sane, otherwise things will break
    chase_config["car_speed_gate"] = (
        config.getfloat("bearings", "car_speed_gate") / 3.6
    )  # Convert to m/s
    chase_config["bearing_length"] = config.getfloat("bearings", "bearing_length")
    chase_config["bearing_weight"] = config.getfloat("bearings", "bearing_weight")
    chase_config["bearing_color"] = config.get("bearings", "bearing_color")
    chase_config["bearing_custom_color"] = config.get(
        "bearings", "bearing_custom_color"
    )

    # Offline Map Settings
    chase_config["tile_server_enabled"] = config.getboolean(
        "offline_maps", "tile_server_enabled"
    )
    chase_config["tile_server_path"] = config.get("offline_maps", "tile_server_path")

    # Determine valid offline map layers.
    chase_config["offline_tile_layers"] = []
    if chase_config["tile_server_enabled"]:
        for _dir in os.listdir(chase_config["tile_server_path"]):
            if os.path.isdir(os.path.join(chase_config["tile_server_path"], _dir)):
                chase_config["offline_tile_layers"].append(_dir)
        logging.info("Found Map Layers: %s" % str(chase_config["offline_tile_layers"]))

    try:
        chase_config["chase_car_speed"] = config.getboolean("speedo", "chase_car_speed")
    except:
        logging.info("Missing Chase Car Speedo Setting, using default (disabled)")
        chase_config["chase_car_speed"] = False

    try:
        chase_config["default_alt"] = config.getfloat("map", "default_alt")
    except:
        logging.info("Missing default_alt setting, using default (0m)")
        chase_config["default_alt"] = 0

    try:
        chase_config["stadia_api_key"] = config.get("map", "stadia_api_key")
    except:
        logging.info("Missing Stadia API Key setting, using default (none)")
        chase_config["stadia_api_key"] = "none"

    try:
        chase_config["turn_rate_threshold"] = config.getfloat("bearings", "turn_rate_threshold")
    except:
        logging.info("Missing turn rate gate setting, using default (4m/s)")
        chase_config["turn_rate_threshold"] = 4.0


    # Telemetry Source Profiles

    _profile_count = config.getint("profile_selection", "profile_count")
    _default_profile = config.getint("profile_selection", "default_profile")

    chase_config["selected_profile"] = ""
    chase_config["profiles"] = {}

    # Unit Selection

    chase_config["unitselection"] = config.get(
        "units", "unitselection", fallback="metric"
    )
    if chase_config["unitselection"] != "imperial":
        chase_config[
            "unitselection"
        ] = "metric"  # unless imperial is explicitly requested do metric
    chase_config["switch_miles_feet"] = config.get(
        "units", "switch_miles_feet", fallback="400"
    )

    for i in range(1, _profile_count + 1):
        _profile_section = "profile_%d" % i
        try:
            _profile_name = config.get(_profile_section, "profile_name")
            _profile_telem_source_type = config.get(
                _profile_section, "telemetry_source_type"
            )
            _profile_telem_source_port = config.getint(
                _profile_section, "telemetry_source_port"
            )
            _profile_car_source_type = config.get(_profile_section, "car_source_type")
            _profile_car_source_port = config.getint(
                _profile_section, "car_source_port"
            )

            _profile_online_tracker = config.get(_profile_section, "online_tracker")

            chase_config["profiles"][_profile_name] = {
                "name": _profile_name,
                "telemetry_source_type": _profile_telem_source_type,
                "telemetry_source_port": _profile_telem_source_port,
                "car_source_type": _profile_car_source_type,
                "car_source_port": _profile_car_source_port,
                "online_tracker": _profile_online_tracker,
            }
            if _default_profile == i:
                chase_config["selected_profile"] = _profile_name

        except Exception as e:
            logging.error("Error reading profile section %d - %s" % (i, str(e)))

    if len(chase_config["profiles"].keys()) == 0:
        logging.critical("Could not read any profile data!")
        return None

    if chase_config["selected_profile"] not in chase_config["profiles"]:
        logging.critical("Default profile selection does not exist.")
        return None

        # History

    chase_config["reload_last_position"] = config.getboolean(
        "history", "reload_last_position", fallback=False
    )

    return chase_config


def read_config(filename, default_cfg="horusmapper.cfg.example"):
    """ Read in a Horus Mapper configuration file,and return as a dict. """

    try:
        config_dict = parse_config_file(filename)
    except Exception as e:
        logging.error("Could not parse %s, trying default: %s" % (filename, str(e)))
        try:
            config_dict = parse_config_file(default_cfg)
        except Exception as e:
            logging.critical("Could not parse example config file! - %s" % str(e))
            config_dict = None

    return config_dict


if __name__ == "__main__":
    import sys

    logging.basicConfig(
        format="%(asctime)s %(levelname)s:%(message)s",
        stream=sys.stdout,
        level=logging.DEBUG,
    )
    print(read_config(sys.argv[1]))
