from aerofiles.errors import ParserError
from aerofiles.util import units
 
from .reader import Reader
from .common import WaypointStyle
 
 
WAYPOINT_STYLE_MAPPING = {
    WaypointStyle.NORMAL: [],
    WaypointStyle.AIRFIELD_GRASS: ['landable', 'airfield'],
    WaypointStyle.OUTLANDING: ['landable'],
    WaypointStyle.GLIDERSITE: ['landable', 'airfield', 'glidersite'],
    WaypointStyle.AIRFIELD_SOLID: ['landable', 'airfield'],
    WaypointStyle.MOUNTAIN_PASS: ['mountain-pass'],
    WaypointStyle.MOUNTAIN_TOP: ['mountain-top'],
    WaypointStyle.SENDER: ['sender'],
    WaypointStyle.VOR: ['vor'],
    WaypointStyle.NDB: ['ndb'],
    WaypointStyle.COOL_TOWER: ['tower'],
    WaypointStyle.DAM: ['dam'],
    WaypointStyle.TUNNEL: ['tunnel'],
    WaypointStyle.BRIDGE: ['bridge'],
    WaypointStyle.POWER_PLANT: ['power-plant'],
    WaypointStyle.CASTLE: ['castle'],
    WaypointStyle.INTERSECTION: ['intersection'],
}
 
UNITS_MAPPING = {
    'm': units.METER,
    'ft': units.FEET,
    'km': units.KILOMETER,
    'ml': units.STATUTE_MILE,
    'nm': units.NAUTICAL_MILE,
}
 
 
class Converter:
    """
    A reader for the SeeYou CUP waypoint file format.
 
    see http://www.keepitsoaring.com/LKSC/Downloads/cup_format.pdf
    """
 
    def __init__(self, fp):
        self.fp = fp
 
    def __iter__(self):
        return self.next()
 
    def next(self):
        for old in Reader(self.fp):
            new = self.convert_waypoint(old)
            if new:
                yield new
 
    def convert_waypoint(self, old):
        waypoint = {}
 
        waypoint['name'] = old['name']
        waypoint['shortname'] = old['code']
        waypoint['country'] = old['country']
        waypoint['description'] = old['description']
 
        waypoint['latitude'] = old['latitude']
        waypoint['longitude'] = old['longitude']
 
        waypoint['elevation'] = self.convert_elevation(old['elevation'])
 
        if old['style'] not in WAYPOINT_STYLE_MAPPING:
            raise ParserError('Unknown waypoint style')
 
        waypoint['classifiers'] = set(WAYPOINT_STYLE_MAPPING[old['style']])
 
        if 'landable' in waypoint['classifiers']:
            waypoint['icao'] = None
            waypoint['runways'] = self.convert_runways(
                old['style'], old['runway_direction'], old['runway_length'])
            waypoint['frequencies'] = self.convert_frequencies(
                old['frequency'])
 
        return waypoint
 
    def convert_elevation(self, elevation):
        if elevation['value'] is None and elevation['unit'] is None:
            return None
 
        unit = UNITS_MAPPING.get(elevation['unit'].lower(), units.METER)
        return units.to_SI(elevation['value'] or 0, unit)
 
    def convert_runways(self, style, dir, length):
        runways = []
        runway = {}
 
        if style == WaypointStyle.AIRFIELD_GRASS:
            runway['surface'] = 'grass'
        elif style == WaypointStyle.AIRFIELD_SOLID:
            runway['surface'] = 'solid'
 
        if dir is not None:
            runway['directions'] = [dir % 360, (dir + 180) % 360]
 
        if length['value']:
            unit = UNITS_MAPPING.get(length['unit'].lower(), units.METER)
            runway['length'] = units.to_SI(length['value'], unit)
 
        if runway:
            runways.append(runway)
 
        return runways
 
    def convert_frequencies(self, frq):
        if not frq:
            return []
 
        if len(frq) < 7:
            frq += \
                '5' if frq.endswith('2') or frq.endswith('7') else '0'
 
        return [{
            'frequency': frq,
        }]