#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os

from enum import Enum
import semantic.behavior

class TaintBase(object):
    pass


class Text(TaintBase):
    def __init__(self, raw_text):
        self.raw = raw_text
        self.text = None
        self.behaviors = None
        self.is_skip = False

        self.parsed_info = None
        self.parse_trees = None
        # self.json = None
        # self.dict = None

    def parse_json(self, json_dict):
        self.raw = "".join(json_dict.get("raw") or '')
        self.text = "".join(json_dict.get("text")) if json_dict.get("text") else ""


        if isinstance(json_dict.get("behaviors"),dict):
            self.behaviors = semantic.behavior.Behavior().parse_json(json_dict.get("behaviors"))
        else:
            self.behaviors = None
        return self

    def to_json(self):
        return self.__attrs()

    def __attrs(self):
        attrs = dict()
        attrs["raw"] = self.raw
        attrs["text"] = self.text
        behaviors = None
        if self.behaviors:
            behaviors = self.behaviors.to_json()
        attrs["behaviors"] = behaviors
        return attrs

    def __str__(self):
        _str = list([])
        _str.append("flow_text={flow_text}".format(flow_text=self.text))
        _str.append("behaviors=%s" % self.behaviors)
        if self.behaviors:
            _str.append("behaviors.pairs=%s" % self.behaviors.pairs)
            _str.append("behaviors.actions=%s" % self.behaviors.actions)
            _str.append("behaviors.resources=%s" % self.behaviors.resources)
        return os.linesep.join(_str)


class Point(TaintBase):
    def __init__(self, raw):
        self.raw = raw
        self.raw_name, self.raw_cls = self.__parse_name_and_cls()
        self.name = None
        self.cls = None
        self.text = None
        self.behaviors = None
        self.is_skip = False

    def __parse_name_and_cls(self):
        if not self.raw:
            return "", ""
        elif "@" not in self.raw:
            return self.raw.strip(), self.raw.strip()
        else:
            return [piece.strip() for piece in self.raw.split("@")]
    
    def parse_json(self, json_dict):
        self.raw = "".join(json_dict.get("raw"))
        self.text = "".join(json_dict.get("text") or '')
   
        if isinstance(json_dict.get("behaviors"),dict):
            self.behaviors = semantic.behavior.Behavior().parse_json(json_dict.get("behaviors"))
        else:
            self.behaviors = None
        return self

    def to_json(self):
        return self.__attrs()

    def __attrs(self):
        attrs = dict()
        attrs["raw"] = self.raw
        attrs["text"] = self.text
        behaviors = None
        if self.behaviors:
            behaviors = self.behaviors.to_json()
        attrs["behaviors"] = behaviors
        return attrs


    def __str__(self):
        _str = list([])
        _str.append("raw_name=%s" % self.raw_name)
        _str.append("raw_cls=%s" % self.raw_cls)
        _str.append("name=%s" % self.name)
        _str.append("cls=%s" % self.cls)
        _str.append("behaviors=%s" % self.behaviors)
        if self.behaviors:
            _str.append("behaviors.pairs=%s" % self.behaviors.pairs)
            _str.append("behaviors.actions=%s" % self.behaviors.actions)
            _str.append("behaviors.resources=%s" % self.behaviors.resources)
        return os.linesep.join(_str)


class Flow(TaintBase):
    def __init__(self, raw_flow=None):
        self.raw = raw_flow
        self.source = None
        self.sink = None
        self.flow = None

        if self.raw:
            self.source, self.sink = self._parse_source_and_sink()

    def parse_json(self, json_dict):
        self.raw = "".join(json_dict.get("raw"))
        self.flow = "".join(json_dict.get("flow")) if json_dict.get("flow") else ""
        self.source = Point('').parse_json(json_dict.get("source"))
        self.sink = Point('').parse_json(json_dict.get("sink"))

        return self

    def to_json(self):
        return self.__attrs()

    def __attrs(self):
        attrs = dict()
        attrs["raw"] = self.raw
        attrs["source"] = self.source.to_json()
        attrs["sink"] = self.sink.to_json()
        attrs["flow"] = self.flow
        return attrs

    def _parse_source_and_sink(self):
        source, sink = [piece.strip() for piece in self.raw.split("=>")]
        return Point(source), Point(sink)

    def parse(self, raw):
        self.raw = raw
        self.source, self.sink = self._parse_source_and_sink()
        return self


class Group(TaintBase):
    def __init__(self, idx = None, flow=None, app_descr=None, json_string=None,
                 refer_keywords=None, api_cls_descr=None, api_mtd_descr=None,layout_strings=None, label=None):
        self.flow = Flow(flow)
        self.app_descr = Text(app_descr)
        self.json_string = Text(json_string)
        self.refer_keywords = Text(refer_keywords)
        self.api_cls_descr = Text(api_cls_descr)
        self.api_mtd_descr = Text(api_mtd_descr)
        self.layout_strings = Text(layout_strings)
        self.label = Label.parse(label if label else "")

        self.__check_skip()
        self.id = idx
        self.prediction = None

    def parse_json(self, json_dict):
        """Parse from Json dict"""

        if isinstance(json_dict.get("flow"),dict):
            self.flow = Flow().parse_json(json_dict.get("flow"))
        else:
            self.flow = Flow(json_dict.get("flow"))

        if isinstance(json_dict.get("app_descr"),dict):
            self.app_descr = Text('').parse_json(json_dict.get("app_descr"))
        else:
            self.app_descr = Text("".join(json_dict.get("app_descr") or ''))

        if isinstance(json_dict.get("json_string"),dict):
            self.json_string = Text('').parse_json(json_dict.get("json_string"))
        else:
            self.json_string = Text("".join(json_dict.get("json_string") or ''))

        if isinstance(json_dict.get("refer_keywords"),dict):
            self.refer_keywords = Text('').parse_json(json_dict.get("refer_keywords"))
        else:
            self.refer_keywords = Text("".join(json_dict.get("refer_keywords") or ''))
        
        if isinstance(json_dict.get("api_cls_descr"),dict):
            self.api_cls_descr = Text('').parse_json(json_dict.get("api_cls_descr"))
        else:
            self.api_cls_descr = Text("".join(json_dict.get("api_cls_descr")))
        
        if isinstance(json_dict.get("api_mtd_descr"),dict):
            self.api_mtd_descr = Text('').parse_json(json_dict.get("api_mtd_descr"))
        else:
            self.api_mtd_descr = Text("".join(json_dict.get("api_mtd_descr")))

        if isinstance(json_dict.get("layout_strings"),dict):
            self.layout_strings = Text('').parse_json(json_dict.get("layout_strings"))
        else:
            self.layout_strings = Text("".join(json_dict.get("layout_strings") or ''))

        self.id = json_dict.get("id") or '-1'
        self.label = Label.parse(json_dict.get("label", ""))

        self.__check_skip()
        return self

    def __check_skip(self):
        if self.app_descr.raw:
            self.layout_strings.is_skip = True
    
    def to_json(self):
        return self.__attrs()

    def __attrs(self):
        attrs = dict()
        attrs["flow"] = self.flow.to_json()
        attrs["app_descr"] = self.app_descr.to_json()
        attrs["json_string"] = self.json_string.to_json()
        attrs["refer_keywords"] = self.refer_keywords.to_json()
        attrs["api_cls_descr"] = self.api_cls_descr.to_json()
        attrs["api_mtd_descr"] = self.api_mtd_descr.to_json()
        attrs["layout_strings"] = self.layout_strings.to_json()
        attrs["label"] = str(self.label)
        attrs["id"] = str(self.id)
        return attrs

    def __str__(self):
        attrs = dict()
        attrs["flow"] = self.flow.raw
        attrs["app_descr"] = self.app_descr.raw
        attrs["json_string"] = self.json_string.raw
        attrs["refer_keywords"] = self.refer_keywords.raw
        attrs["api_cls_descr"] = self.api_cls_descr.raw
        attrs["api_mtd_descr"] = self.api_mtd_descr.raw
        attrs["layout_strings"] = self.layout_strings.raw
        attrs["label"] = str(self.label)
        attrs["id"] = str(self.id)
        return str(attrs)


class GroupLabel(Enum):
    MALICIOUS = 0
    BENIGN = 1
    UNKNOWN = 2


class Label(TaintBase):
    @staticmethod
    def parse(raw_label):
        """Parse from string and covert it into type GroupLabel"""
        if raw_label.strip() == "m" or raw_label.strip() == 'GroupLabel.MALICIOUS':
            return GroupLabel.MALICIOUS
        elif raw_label.strip() == "b" or raw_label.strip() == 'GroupLabel.BENIGN':
            return GroupLabel.BENIGN
        return GroupLabel.UNKNOWN
