# coding=utf-8

from __future__ import absolute_import, unicode_literals, print_function
from collections import namedtuple


class Node(object):
    @classmethod
    def build(cls, node_desc):
        if isinstance(node_desc, Node):
            return node_desc

        parent = cls(weight = node_desc[0])
        for n in node_desc[1]:
            parent.add_child(cls.build(n))

        return parent

    def __init__(self, weight=1.0, **kwargs):
        self.weight = weight
        self.children = []
        self.score = 0.0
        self.node_name = 'node'

    def add_child(self, ch):
        if isinstance(ch, Node):
            self.children.append(ch)

    def get_info(self):
        return {
            'node_name':self.node_name,
            'weight':self.weight,
            'score':self.score,
        }


class PreCalcNode(Node):
    def add_child(self, ch):
        '''
        pre calculation node only allows pre calculation child nodes
        '''
        if isinstance(ch, PreCalcNode):
            self.children.append(ch)

    def calc_score(self, obj):
        if self.children:
            total_weight = 0.0
            total_score = 0.0
            for ch in self.children:
                total_weight += ch.weight
                total_score += ch.weight*ch.calc_score(obj)
            total_score /= 1.0*total_weight
            self.score = total_score
        else:
            self.score = self._calc_score(obj)

        return self.score

    def _calc_score(self, obj):
        """
        for leaf nodes, you should write a method call
        otherwise, just derived it
        """
        return 0.0


class AdhocNode(Node):
    def calc_score(self, obj, ext_params):
        if self.children:
            total_weight = 0.0
            total_score = 0.0
            for ch in self.children:
                total_weight += ch.weight
                if isinstance(ch, PreCalcNode):
                    total_score += ch.weight*ch.calc_score(obj)
                else:
                    total_score += ch.weight*ch.calc_score(obj, ext_params)

            total_score /= 1.0*total_weight
            self.score = total_score
        else:
            self.score = self._calc_score(obj, ext_params)

        return self.score

    def _calc_score(self, obj, ext_params):
        '''
        ext_params is a named tuple
        for leaf nodes, you should overwrite this method
        otherwise, just derived it
        '''
        return 0.0
