# -*- coding: utf-8 -*-
# Copyright (c) 2023 Salvador E. Tropea
# Copyright (c) 2023 Instituto Nacional de Tecnología Industrial
# License: MIT
# Project: KiBot (formerly KiPlot)

from decimal import Decimal
from lark import Lark, Transformer
import os
from ..gs import GS
from .. import log

logger = log.get_logger()
# Metric to imperial package sizes
TO_IMPERIAL = {'0402': '01005',
               '0603': '0201',
               '1005': '0402',
               '1608': '0603',
               '2012': '0805',
               '2520': '1008',
               '3216': '1206',
               '3225': '1210',
               '4516': '1806',
               '4532': '1812',
               '5025': '2010',
               '6332': '2512'}
parser = None


class ComponentTransformer(Transformer):
    """ Transforms a tree parsed by Lark to the electro-grammar dict """
    def __init__(self):
        self.parsed = {}
        # Extra information, not in the original lib and needed for internal purposes
        self.extra = {}

    def value3(self, d, type):
        """ VALUE [METRIC_PREFIX [MANTISSA]] """
        v = Decimal(d[0])
        c = len(d)
        if c >= 3:
            # We have something like 2n2
            dec = d[2]
            c_dec = len(dec)
            v += Decimal(dec)/(Decimal(10)*c_dec)
        self.extra['val'] = v
        if c >= 2:
            # Metric prefix
            v *= d[1]
            self.extra['mult'] = d[1]
        else:
            self.extra['mult'] = Decimal(1)
        v = float(v)
        self.parsed[type] = v
        return v

    def value2(self, d, type):
        """ VALUE [MANTISSA] """
        v = float(d[0])
        c = len(d)
        if c >= 2:
            # We have something like 3V3
            dec = d[1]
            c_dec = len(dec)
            v += float(dec)/(10.0*c_dec)
        self.parsed[type] = v
        return v

    def value1(self, d, type):
        """ VALUE """
        v = float(d[0])
        iv = round(v)
        if iv == v:
            v = iv
        self.parsed[type] = v
        return v

    def tolerance(self, d):
        return self.value1(d, 'tolerance')

    def voltage_rating(self, d):
        return self.value2(d, 'voltage_rating')

    def temp_coef(self, d):
        c_len = len(d)
        if c_len == 3:
            # Class 2: i.e. X7R
            v = d[0].value+d[1].value+d[2].value
        else:
            # Class 1: i.e. C0G
            v = d[0].type
        self.parsed['characteristic'] = v.upper()
        return v

    def power_rating(self, d):
        if len(d) == 1:
            # 1 W
            v = float(d[0])
        elif d[0].type == 'INT':
            # 1/4 W
            v = float(d[0].value)/float(d[1].value)
        else:
            # 250 mW
            v = float(Decimal(d[0].value)*d[1])
        self.parsed['power_rating'] = v
        return v

    def color(self, d):
        c = d[0].value.lower()
        self.parsed['color'] = c
        return c

    def set_type(self, type, d):
        self.parsed['type'] = type
        return d

    # Package size
    def imperial_size(self, d):
        s = d[0].value
        self.parsed['size'] = s
        return s

    def unambigious_metric_size(self, d):
        s = TO_IMPERIAL[d[0].value]
        self.parsed['size'] = s
        return s

    metric_size_base = unambigious_metric_size

    # RLC
    def resistance(self, d):
        return self.value3(d, 'resistance')

    resistance_no_r = resistance

    def inductance(self, d):
        return self.value3(d, 'inductance')

    inductance_no_henry = inductance

    def capacitance(self, d):
        return self.value3(d, 'capacitance')

    capacitance_no_farad = capacitance

    # Known components
    def inductor(self, d):
        return self.set_type('inductor', d)

    def capacitor(self, d):
        return self.set_type('capacitor', d)

    def resistor(self, d):
        return self.set_type('resistor', d)

    def led(self, d):
        return self.set_type('led', d)

    # Metrix prefixes
    def giga(self, _):
        return Decimal('1e9')

    def mega(self, _):
        return Decimal('1e6')

    def kilo(self, _):
        return Decimal('1e3')

    def unit(self, _):
        return Decimal(1)

    def milli(self, _):
        return Decimal('1e-3')

    def nano(self, _):
        return Decimal('1e-9')

    def micro(self, _):
        return Decimal('1e-6')

    def pico(self, _):
        return Decimal('1e-12')

    def femto(self, _):
        return Decimal('1e-15')

    def crap(self, v):
        if 'discarded' in self.extra:
            self.extra['discarded'].append(v[0].value)
        else:
            self.extra['discarded'] = [v[0].value]
        return None


def initialize():
    global parser
    if parser is not None:
        return
    with open(os.path.join(GS.get_resource_path('parsers'), 'electro.lark'), 'rt') as f:
        g = f.read()
    parser = Lark(g, start='main')  # , debug=DEBUG)


def parse(text, with_extra=False, stronger=False):
    initialize()
    if stronger:
        text = text.replace('+/-', ' +/-')
        text = text.replace(' - ', ' ')
    try:
        tree = parser.parse(text)
    except Exception as e:
        logger.debugl(2, str(e))
        return {}
    logger.debugl(3, tree.pretty())
    res_o = ComponentTransformer()
    res = res_o.transform(tree)
    logger.debugl(3, res)
    res = res_o.parsed
    if with_extra:
        res.update(res_o.extra)
    return res
