# Copyright (c) 2016,2017,2019 MetPy Developers.
# Distributed under the terms of the BSD 3-Clause License.
# SPDX-License-Identifier: BSD-3-Clause
"""Simplify using the weather symbol font.
See WMO manual 485 Vol 1 for more info on the symbols.
"""
try:
    from importlib.resources import (files as importlib_resources_files,
                                     as_file as importlib_resources_as_file)
except ImportError:  # Can remove when we require Python > 3.8
    from importlib_resources import (files as importlib_resources_files,
                                     as_file as importlib_resources_as_file)
import matplotlib.font_manager as fm
import numpy as np
from ..package_tools import Exporter
exporter = Exporter(globals())
# Create a matplotlib font object pointing to our weather symbol font
fontfile = importlib_resources_files('metpy.plots') / 'fonts/wx_symbols.ttf'
with importlib_resources_as_file(fontfile) as fname:
    # Need to pass str, not Path, for older matplotlib
    wx_symbol_font = fm.FontProperties(fname=str(fname))
[docs]@exporter.export
def wx_code_to_numeric(codes):
    """Determine the numeric weather symbol value from METAR code text.
    A robust method to identifies the numeric value for plotting the correct symbol from a
    decoded METAR current weather group. The METAR codes should be strings with no missing
    values or NaN strings (empty strings are okay).
    For example, if from a Pandas Dataframe sfc_df.wxcodes.fillna('')
    Parameters
    ----------
    codes : Array like containing string values of METAR weather codes
    Returns
    -------
    array of numeric codes of current weather symbols from the wx_code_map for use in
    plotting.
    """
    wx_sym_list = []
    for s in codes:
        wxcode = s.split()[0] if ' ' in s else s
        try:
            wx_sym_list.append(wx_code_map[wxcode])
        except KeyError:
            if wxcode[0].startswith(('-', '+')):
                options = [slice(None, 7), slice(None, 5), slice(1, 5), slice(None, 3),
                           slice(1, 3)]
            else:
                options = [slice(None, 6), slice(None, 4), slice(None, 2)]
            for opt in options:
                try:
                    wx_sym_list.append(wx_code_map[wxcode[opt]])
                    break
                except KeyError:
                    pass
            else:
                wx_sym_list.append(0)
    return np.array(wx_sym_list) 
class CodePointMapping:
    """Map integer values to font code points."""
    def __init__(self, num, font_start, font_jumps=None, char_jumps=None):
        """Initialize the instance.
        Parameters
        ----------
        num : int
            The number of values that will be mapped
        font_start : int
            The first code point in the font to use in the mapping
        font_jumps : list[int, int], optional
            Sequence of code point jumps in the font. These are places where the next
            font code point does not correspond to a new input code. This is usually caused
            by there being multiple symbols for a single code. Defaults to :data:`None`, which
            indicates no jumps.
        char_jumps : list[int, int], optional
            Sequence of code jumps. These are places where the next code value does not
            have a valid code point in the font. This usually comes from place in the WMO
            table where codes have no symbol. Defaults to :data:`None`, which indicates no
            jumps.
        """
        next_font_jump = self._safe_pop(font_jumps)
        next_char_jump = self._safe_pop(char_jumps)
        font_point = font_start
        self.chrs = []
        code = 0
        while code < num:
            if next_char_jump and code >= next_char_jump[0]:
                jump_len = next_char_jump[1]
                code += jump_len
                self.chrs.extend([''] * jump_len)
                next_char_jump = self._safe_pop(char_jumps)
            else:
                self.chrs.append(chr(font_point))
                if next_font_jump and code >= next_font_jump[0]:
                    font_point += next_font_jump[1]
                    next_font_jump = self._safe_pop(font_jumps)
                code += 1
                font_point += 1
    @staticmethod
    def _safe_pop(lst):
        """Safely pop from a list.
        Returns None if list empty.
        """
        return lst.pop(0) if lst else None
    def __call__(self, code):
        """Return the Unicode code point corresponding to `code`."""
        return self.chrs[code]
    def __len__(self):
        """Return the number of codes supported by this mapping."""
        return len(self.chrs)
    def alt_char(self, code, alt):
        """Get one of the alternate code points for a given value.
        In the WMO tables, some code have multiple symbols. This allows getting that
        symbol rather than main one.
        Parameters
        ----------
        code : int
            The code for looking up the font code point
        alt : int
            The number of the alternate symbol
        Returns
        -------
        int
            The appropriate code point in the font
        """
        return chr(ord(self(code)) + alt)
#
# Set up mapping objects for various groups of symbols. The integer values follow from
# the WMO.
#
with exporter:
    #: Current weather
    current_weather = CodePointMapping(100, 0xE9A2, [(7, 2), (93, 2), (94, 2), (95, 2),
                                                     (97, 2)], [(0, 4)])
    #: Current weather from an automated station
    current_weather_auto = CodePointMapping(100, 0xE94B, [(92, 2), (95, 2)],
                                            [(6, 4), (13, 5), (19, 1), (36, 4), (49, 1),
                                             (59, 1), (69, 1), (79, 1), (88, 1), (97, 2)])
    #: Low clouds
    low_clouds = CodePointMapping(10, 0xE933, [(7, 1)], [(0, 1)])
    #: Mid-altitude clouds
    mid_clouds = CodePointMapping(10, 0xE93D, char_jumps=[(0, 1)])
    #: High clouds
    high_clouds = CodePointMapping(10, 0xE946, char_jumps=[(0, 1)])
    #: Sky cover symbols
    sky_cover = CodePointMapping(12, 0xE90A)
    #: Pressure tendency
    pressure_tendency = CodePointMapping(10, 0xE900)
    #####################################################################
    # This dictionary is for mapping METAR present weather text codes
    # to WMO codes for plotting wx symbols along with the station plots.
    # Pages II-4-3 and II-4-4 of this document describes the difference between
    # manned and automated stations:
    # https://github.com/Unidata/MetPy/files/1151142/485_Vol_I_en.pdf
    # It may become necessary to add automated station wx_codes in the future,
    # but that will also require knowing the status of all stations.
    wx_code_map = {'': 0, 'M': 0, 'TSNO': 0, 'VA': 4, 'FU': 4,
                   'HZ': 5, 'DU': 6, 'BLDU': 7, 'SA': 7,
                   'BLSA': 7, 'VCBLSA': 7, 'VCBLDU': 7, 'BLPY': 7,
                   'PO': 8, 'VCPO': 8, 'VCDS': 9, 'VCSS': 9,
                   'BR': 10, 'BCBR': 10, 'BC': 11, 'MIFG': 12,
                   'VCTS': 13, 'VIRGA': 14, 'VCSH': 16, 'TS': 17,
                   'THDR': 17, 'VCTSHZ': 17, 'TSFZFG': 17, 'TSBR': 17,
                   'TSDZ': 17, 'SQ': 18, 'FC': 19, '+FC': 19,
                   'DS': 31, 'SS': 31, 'DRSA': 31, 'DRDU': 31,
                   'TSUP': 32, '+DS': 34, '+SS': 34, '-BLSN': 36,
                   'BLSN': 36, '+BLSN': 36, 'VCBLSN': 36, 'DRSN': 38,
                   '+DRSN': 38, 'VCFG': 40, 'BCFG': 41, 'PRFG': 44,
                   'FG': 45, 'FZFG': 49, '-VCTSDZ': 51, '-DZ': 51,
                   '-DZBR': 51, 'VCTSDZ': 53, 'DZ': 53, '+VCTSDZ': 55,
                   '+DZ': 55, '-FZDZ': 56, '-FZDZSN': 56, 'FZDZ': 57,
                   '+FZDZ': 57, 'FZDZSN': 57, '-DZRA': 58, 'DZRA': 59,
                   '+DZRA': 59, '-VCTSRA': 61, '-RA': 61, '-RABR': 61,
                   'VCTSRA': 63, 'RA': 63, 'RABR': 63, 'RAFG': 63,
                   '+VCTSRA': 65, '+RA': 65, '-FZRA': 66, '-FZRASN': 66,
                   '-FZRABR': 66, '-FZRAPL': 66, '-FZRASNPL': 66, 'TSFZRAPL': 67,
                   '-TSFZRA': 67, 'FZRA': 67, '+FZRA': 67, 'FZRASN': 67,
                   'TSFZRA': 67, '-DZSN': 68, '-RASN': 68, '-SNRA': 68,
                   '-SNDZ': 68, 'RASN': 69, '+RASN': 69, 'SNRA': 69,
                   'DZSN': 69, 'SNDZ': 69, '+DZSN': 69, '+SNDZ': 69,
                   '-VCTSSN': 71, '-SN': 71, '-SNBR': 71, 'VCTSSN': 73,
                   'SN': 73, '+VCTSSN': 75, '+SN': 75, 'VCTSUP': 76,
                   'IN': 76, '-UP': 76, 'UP': 76, '+UP': 76,
                   '-SNSG': 77, 'SG': 77, '-SG': 77, 'IC': 78,
                   '-FZDZPL': 79, '-FZDZPLSN': 79, 'FZDZPL': 79, '-FZRAPLSN': 79,
                   'FZRAPL': 79, '+FZRAPL': 79, '-RAPL': 79, '-RASNPL': 79,
                   '-RAPLSN': 79, '+RAPL': 79, 'RAPL': 79, '-SNPL': 79,
                   'SNPL': 79, '-PL': 79, 'PL': 79, '-PLSN': 79,
                   '-PLRA': 79, 'PLRA': 79, '-PLDZ': 79, '+PL': 79,
                   'PLSN': 79, 'PLUP': 79, '+PLSN': 79, '-SH': 80,
                   '-SHRA': 80, 'SH': 81, 'SHRA': 81, '+SH': 81,
                   '+SHRA': 81, '-SHRASN': 83, '-SHSNRA': 83, '+SHRABR': 84,
                   'SHRASN': 84, '+SHRASN': 84, 'SHSNRA': 84, '+SHSNRA': 84,
                   '-SHSN': 85, 'SHSN': 86, '+SHSN': 86, '-GS': 87,
                   '-SHGS': 87, 'FZRAPLGS': 88, '-SNGS': 88, 'GSPLSN': 88,
                   'GSPL': 88, 'PLGSSN': 88, 'GS': 88, 'SHGS': 88,
                   '+GS': 88, '+SHGS': 88, '-GR': 89, '-SHGR': 89,
                   '-SNGR': 90, 'GR': 90, 'SHGR': 90, '+GR': 90,
                   '+SHGR': 90, '-TSRA': 95, 'TSRA': 95, 'TSSN': 95,
                   'TSPL': 95, '-TSDZ': 95, '-TSSN': 95, '-TSPL': 95,
                   'TSPLSN': 95, 'TSSNPL': 95, '-TSSNPL': 95, 'TSRAGS': 96,
                   'TSGS': 96, 'TSGR': 96, '+TSRA': 97, '+TSSN': 97,
                   '+TSPL': 97, '+TSPLSN': 97, 'TSSA': 98, 'TSDS': 98,
                   'TSDU': 98, '+TSGS': 99, '+TSGR': 99}