# -*- coding: utf-8 -*-

from tools import trace, timer

from Model.Tools.PamhyrDB import SQLSubModel
from Model.Except import NotImplementedMethodeError


class Layer(SQLSubModel):
    _sub_classes = []
    _id_cnt = 0

    def __init__(self,
                 id: int = -1, name: str = "",
                 type="",
                 height=0.0, d50=0.0, sigma=0.0,
                 critical_constraint=0.0,
                 sl=None, status=None):
        super(Layer, self).__init__()

        self._status = status

        self._name = name
        self._type = type

        self._height = height
        self._d50 = d50
        self._sigma = sigma
        self._critical_constraint = critical_constraint

        if id == -1:
            self.id = Layer._id_cnt
        else:
            self.id = id

        Layer._id_cnt = max(id, Layer._id_cnt+1)

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, name):
        self._name = name

    @property
    def type(self):
        return self._type

    @type.setter
    def type(self, type):
        self._type = type

    @property
    def height(self):
        return self._height

    @height.setter
    def height(self, height):
        self._height = float(height)

    @property
    def d50(self):
        return self._d50

    @d50.setter
    def d50(self, d50):
        self._d50 = float(d50)

    @property
    def sigma(self):
        return self._sigma

    @sigma.setter
    def sigma(self, sigma):
        self._sigma = float(sigma)

    @property
    def critical_constraint(self):
        return self._critical_constraint

    @critical_constraint.setter
    def critical_constraint(self, critical_constraint):
        self._critical_constraint = float(critical_constraint)

    @classmethod
    def _sql_create(cls, execute):
        execute("""
          CREATE TABLE sedimentary_layer_layer(
            id INTEGER NOT NULL PRIMARY KEY,
            ind INTEGER NOT NULL,
            name TEXT NOT NULL,
            type TEXT NOT NULL,
            height REAL NOT NULL,
            d50 REAL NOT NULL,
            sigma REAL NOT NULL,
            critical_constraint REAL NOT NULL,
            sl INTEGER,
            FOREIGN KEY(sl) REFERENCES sedimentary_layer(id)
          )
        """)

        return cls._create_submodel(execute)

    @classmethod
    def _sql_update(cls, execute, version):
        major, minor, release = version.strip().split(".")
        if major == minor == "0":
            if int(release) < 2:
                cls._sql_create(execute)

        return True

    @classmethod
    def _sql_load(cls, execute, data=None):
        new = []
        sl = data["sl"]

        table = execute(
            "SELECT id, ind, name, type, height, " +
            "d50, sigma, critical_constraint " +
            "FROM sedimentary_layer_layer " +
            f"WHERE sl = {sl}"
        )

        for _ in table:
            new.append(None)

        for row in table:
            ind = row[1]

            layer = cls(
                id=row[0], name=row[2],
                type=row[3], height=row[4],
                d50=row[5], sigma=row[6],
                critical_constraint=row[7],
                sl=sl, status=data['status']
            )

            new[ind] = layer

        return new

    def _sql_save(self, execute, data=None):
        ind = data["ind"]
        sl = data["sl"]

        sql = (
            "INSERT INTO " +
            "sedimentary_layer_layer(id, ind, name, type, height, " +
            "d50, sigma, critical_constraint, sl) " +
            "VALUES (" +
            f"{self.id}, {ind}, '{self._sql_format(self._name)}', " +
            f"'{self._sql_format(self._type)}', {self._height}, " +
            f"{self._d50}, {self._sigma}, {self._critical_constraint}, " +
            f"{sl.id}" +
            ")"
        )
        execute(sql)

        return True


class SedimentLayer(SQLSubModel):
    _sub_classes = [Layer]
    _id_cnt = 0

    def __init__(self, id: int = -1,
                 name: str = "", comment: str = "",
                 status=None):
        super(SedimentLayer, self).__init__()

        self._status = status
        self._name = name
        self._comment = comment
        self._layers = []

        if id == -1:
            self.id = SedimentLayer._id_cnt
        else:
            self.id = id

        SedimentLayer._id_cnt = max(id, SedimentLayer._id_cnt+1)

    def __str__(self):
        s = f"{self.name} ({len(self)})"

        if self.comment != "":
            s += f" - {self.comment}"

        return s

    def __len__(self):
        return len(self._layers)

    @property
    def layers(self):
        return self._layers.copy()

    def height(self):
        return list(
            map(lambda layer: layer.height, self._layers)
        )

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, name):
        self._name = name

    def names(self):
        return list(
            map(lambda layer: layer.name, self._layers)
        )

    @property
    def comment(self):
        return self._comment

    @comment.setter
    def comment(self, comment):
        self._comment = comment

    @classmethod
    def _sql_create(cls, execute):
        execute("""
          CREATE TABLE sedimentary_layer(
            id INTEGER NOT NULL PRIMARY KEY,
            name TEXT NOT NULL,
            comment TEXT NOT NULL
          )
        """)

        return cls._create_submodel(execute)

    @classmethod
    def _sql_update(cls, execute, version):
        major, minor, release = version.strip().split(".")
        if major == minor == "0":
            if int(release) < 2:
                cls._sql_create(execute)

        return True

    @classmethod
    def _sql_load(cls, execute, data=None):
        new = []

        table = execute(
            "SELECT id, name, comment " +
            "FROM sedimentary_layer "
        )

        for row in table:
            sl = cls(
                id=row[0],
                name=row[1],
                comment=row[2],
                status=data['status']
            )

            data["sl"] = sl.id
            sl._layers = Layer._sql_load(execute, data)

            new.append(sl)

        return new

    def _sql_save(self, execute, data=None):
        if data is None:
            data = {}

        sql = (
            "INSERT INTO sedimentary_layer (id, name, comment) " +
            f"VALUES ({self.id}, '{self._sql_format(self._name)}', " +
            f"'{self._sql_format(self._comment)}')"
        )
        execute(sql)

        data["sl"] = self

        ind = 0
        for layer in self._layers:
            data["ind"] = ind
            layer._sql_save(execute, data)
            ind += 1

        return True

    def get(self, index):
        return self._layers[index]

    def set(self, index, new):
        self._layers[index] = new
        self._status.modified()

    def insert(self, index, new):
        self._layers.insert(index, new)
        self._status.modified()

    def new(self, index):
        n = Layer(sl=self, status=self._status)
        self.insert(index, n)
        self._status.modified()
        return n

    def delete(self, els):
        for el in els:
            self._layers.remove(el)
        self._status.modified()

    def delete_i(self, indexes):
        els = list(
            map(
                lambda x: x[1],
                filter(
                    lambda x: x[0] in indexes,
                    enumerate(self._layers)
                )
            )
        )
        self.delete(els)

    def move_up(self, index):
        if index >= 0:
            next = index - 1

            lst = self._layers
            lst[index], lst[next] = lst[next], lst[index]

            self._status.modified()

    def move_down(self, index):
        if index + 1 < len(self._layers):
            prev = index + 1

            lst = self._layers
            lst[index], lst[prev] = lst[prev], lst[index]

            self._status.modified()
