from ..analyzer import TimeWindowAnalyzer
from .builder import ExpressionBuilder
from .abc import Expression, evaluate


class DeclarativeTimeWindowAnalyzer(TimeWindowAnalyzer):
    @classmethod
    def construct(cls, app, pipeline, definition: dict):
        _id = definition.get("id")
        config = definition.get("config")
        declaration = definition.get("declaration")
        return cls(app, pipeline, declaration=declaration, id=_id, config=config)

    def __init__(self, app, pipeline, declaration, id=None, config=None):
        builder = ExpressionBuilder(app)
        decl = builder.parse(declaration)
        define = decl.pop("define")
        self.Predicate = decl.pop("predicate")

        matrixes = define.pop("matrixes", {})
        dtype = matrixes.pop("primary", {}).get("cell", {})
        if isinstance(dtype, dict):
            dtype = {
                "names": list(dtype.keys()),
                "formats": list(dtype.values()),
            }

        columns = define.pop("columns", {})
        columns_count = columns.pop("count", 15)
        columns_resolution = columns.pop("resolution", 15)
        self.ColumnItem = columns.pop("item", "timestamp")

        self.Evaluates = decl.pop("evaluate")
        assert isinstance(self.Evaluates, dict)

        self.Dimension = define.pop("dimension")
        assert isinstance(self.Dimension, list) or isinstance(self.Dimension, str)

        self.Trigger = decl.pop("trigger", None)
        if self.Trigger is not None:
            assert isinstance(self.Trigger, list)

        # TODO: matrix_id
        # TODO: analyze_on_clock
        # TODO: start_time
        # TODO: clock_driven
        super().__init__(
            app,
            pipeline,
            id=id,
            config=config,
            dtype=dtype,
            columns=columns_count,
            resolution=columns_resolution,
        )

    def process(self, context, event):
        x = self.Predicate(context, event)
        if x:
            if x:
                x = "evaluate"
            if isinstance(x, list):
                for i in x:
                    evaluate(self.Evaluates[i], context, event)
            else:
                evaluate(self.Evaluates[x], context, event)

        return event

    def evaluate(self, evalobj, context, event):
        # Find the column in timewindow matrix to fit in
        column = event[self.ColumnItem]
        column = self.TimeWindow.get_column(column)
        if column is None:
            return

        # Find the row, existing or create a new one
        row_key = tuple(event[item] for item in self.Dimension)
        row = self.TimeWindow.get_row_index(row_key)
        if row is None:
            row = self.TimeWindow.add_row(row_key)

        cell = self.TimeWindow.Array[row, column]

        matrix = evalobj.get("primary")
        if isinstance(matrix, Expression):
            ret = evaluate(
                matrix,
                context,
                event,
                cell,
                row=row,
                column=column,
            )
        else:
            ret = matrix

        self.TimeWindow.Array[row, column] = ret
        self.trigger(context, event, "primary", ret, row, column)

    def trigger(self, context, event, matrix, cell, row, column):
        for t in self.Trigger:
            matrix = t[matrix]
            test = matrix["test"]
            result = evaluate(
                test,
                context,
                event,
                cell,
                row=row,
                column=column,
            )

            if result:
                print(">>> trigger", event, row, column)
