# validated: 2017-10-08 DS 8a37b81f4e10 cpp/Storage_load.cpp

import ast
import binascii
import base64
import re

from .value import Value

from .support.compat import RawConfigParser, NoSectionError, PY2

import logging
logger = logging.getLogger('nt')


PERSISTENT_SECTION = 'NetworkTables Storage 3.0'

_key_bool = re.compile('boolean "(.+)"')
_key_double = re.compile('double "(.+)"')
_key_string = re.compile('string "(.+)"')
_key_raw = re.compile('raw "(.+)"')
_key_bool_array = re.compile('array boolean "(.+)"')
_key_double_array = re.compile('array double "(.+)"')
_key_string_array = re.compile('array string "(.+)"')

_value_string = re.compile(r'"((?:\\.|[^"\\])*)",?')

# TODO: these escape functions almost certainly don't deal with unicode
#       correctly

# TODO: strictly speaking, this isn't 100% compatible with ntcore... but

def _unescape_string(s):
    # shortcut if no escapes present
    if '\\' not in s:
        return s
    
    # let python do the hard work
    return ast.literal_eval('"%s"' % s)


def load_entries(fp, filename, prefix):
    
    entries = []
    
    parser = RawConfigParser()
    parser.optionxform = str
    
    try:
        if hasattr(parser, 'read_file'):
            parser.read_file(fp, filename)
        else:
            parser.readfp(fp, filename)
    except IOError:
        raise
    except Exception as e:
        raise IOError('Error reading persistent file: %s' % e)
    
    try:
        items = parser.items(PERSISTENT_SECTION)
    except NoSectionError:
        raise IOError("Persistent section not found")

    value = None
    m = None
    
    for k, v in items:
        
        # Reduces code duplication
        if value:
            key = _unescape_string(m.group(1))
            if key.startswith(prefix):
                entries.append((key, value))
            
        value = None
        
        m = _key_bool.match(k)
        if m:
            if v == 'true':
                value = Value.makeBoolean(True)
            elif v == 'false':
                value = Value.makeBoolean(False)
            else:
                logger.warn("Unrecognized boolean value %r for %s", v, m.group(1))
            continue
        
        m = _key_double.match(k)
        if m:
            try:
                value = Value.makeDouble(float(v))
            except ValueError as e:
                logger.warn("Unrecognized double value %r for %s", v, m.group(1))
                
            continue
        
        m = _key_string.match(k)
        if m:
            mm = _value_string.match(v)
            
            if mm:
                value = Value.makeString(_unescape_string(mm.group(1)))
            elif PY2 and v == '':
                value = Value.makeString('')
            else:
                logger.warn("Unrecognized string value %r for %s", v, m.group(1))
            continue
        
        m = _key_raw.match(k)
        if m:
            try:
                if PY2:
                    v = base64.b64decode(v)
                else:
                    v = base64.b64decode(v, validate=True)
                value = Value.makeRaw(v)
            except binascii.Error:
                logger.warn("Unrecognized raw value %r for %s", v, m.group(1))
            continue
        
        m = _key_bool_array.match(k)
        if m:
            bools = []
            arr = v.strip().split(',')
            if arr != ['']:
                for vv in arr:
                    vv = vv.strip()
                    if vv == 'true':
                        bools.append(True)
                    elif vv == 'false':
                        bools.append(False)
                    else:
                        logger.warn("Unrecognized bool '%s' in bool array %s'", vv, m.group(1))
                        bools = None
                        break
                
            if bools is not None:
                value = Value.makeBooleanArray(bools)    
            continue
        
        m = _key_double_array.match(k)
        if m:
            doubles = []
            arr = v.strip().split(',')
            if arr != ['']:
                for vv in arr:
                    try:
                        doubles.append(float(vv))
                    except ValueError:
                        logger.warn("Unrecognized double '%s' in double array %s", vv, m.group(1))
                        doubles = None
                        break
                
            value = Value.makeDoubleArray(doubles)    
            continue
        
        m = _key_string_array.match(k)
        if m:
            # Technically, this will let invalid inputs in... but,
            # I don't really care. Feel free to fix it if you do.
            strings = [_unescape_string(vv) for vv in _value_string.findall(v)]
            value = Value.makeStringArray(strings)
            continue
        
        logger.warn("Unrecognized type '%s'", k)
        
    if value:
        key = _unescape_string(m.group(1))
        if key.startswith(prefix):
            entries.append((key, value))
    
    return entries
