# PamhyrList.py -- Pamhyr Abstract List object for the Model
# Copyright (C) 2023-2026  INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

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

import logging

from copy import copy
from tools import trace, timer

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

logger = logging.getLogger()


class PamhyrModelList(SQLSubModel):
    _sub_classes = []

    def __init__(self, status=None):
        super(PamhyrModelList, self).__init__()

        self._status = status
        self._lst = []

    #######
    # SQL #
    #######

    @classmethod
    def _db_create(cls, execute):
        return cls._create_submodel(execute)

    @classmethod
    def _db_update(cls, execute, version, data=None):
        return cls._update_submodel(execute, version, data)

    @classmethod
    def _db_load(cls, execute, data=None):
        raise NotImplementedMethodeError(cls, cls._db_load)

    def _db_save(self, execute, data=None):
        raise NotImplementedMethodeError(self, self._db_save)

    ################
    # MODEL METHOD #
    ################

    @property
    def lst(self):
        """Return the PamhyrList as a Python list

        Return the PamhyrList as a Python list.  This list is the
        current list used in the PamhyrList object.  **If you need to
        modify it, please make a copy.**

        Returns: The Python list
        """
        return self._lst

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

    def index(self, el):
        return self._lst.index(el)

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

    def set(self, index, new):
        self._lst[index] = new
        if self._status is not None:
            self._status.modified()

    def _data_traversal(self,
                        predicate=lambda obj, data: True,
                        modifier=lambda obj, data: None,
                        data={}):
        """Traversal data and execute modifier fonction if predicate
        true

        Args:
            predicate: Function predicate, take current obj and data as input
            modifier: Function modifier, take current obj and data as input

        Returns:
            Nothing

        """
        if predicate(self, data):
            modifier(self, data)

        for el in self._lst:
            el._data_traversal(predicate, modifier, data)

    def new(self, index):
        """Create new elements and add it to list

        Args:
            index: The index of new elements

        Returns:
            The new elements
        """
        raise NotImplementedMethodeError(self, self.new)

    def insert(self, index, new):
        self._lst.insert(index, new)

        if self._status is not None:
            self._status.modified()

    def delete(self, lst):
        """Delete a list of elements

        Args:
            lst: The list of elements

        Returns:
            Nothing
        """
        for el in lst:
            self._lst.remove(el)

        if self._status is not None:
            self._status.modified()

    def delete_i(self, indexes):
        """Delete elements from list of indexes

        Args:
            indexes: The elements indexes

        Returns:
            Nothing
        """
        lst = list(
            map(
                lambda x: x[1],
                filter(
                    lambda x: x[0] in indexes,
                    enumerate(self._lst)
                )
            )
        )
        self.delete(lst)

    def sort(self, reverse=False, key=None):
        self._lst.sort(reverse=reverse, key=key)

        if self._status is not None:
            self._status.modified()

    def move_up(self, index):
        if index < len(self._lst):
            next = index - 1

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

            if self._status is not None:
                self._status.modified()

    def move_down(self, index):
        if index >= 0:
            prev = index + 1

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

            if self._status is not None:
                self._status.modified()


class PamhyrModelListWithTab(SQLSubModel):
    _tabs_list = []
    _sub_classes = []

    def __init__(self, status=None):
        super(PamhyrModelListWithTab, self).__init__()

        self._status = status
        self._tabs = {}
        for tab in self._tabs_list:
            self._tabs[tab] = []

    #######
    # SQL #
    #######

    @classmethod
    def _db_create(cls, execute):
        return cls._create_submodel(execute)

    @classmethod
    def _db_update(cls, execute, version, data=None):
        return cls._update_submodel(execute, version, data)

    @classmethod
    def _db_load(cls, execute, data=None):
        raise NotImplementedMethodeError(cls, cls._db_load)

    def _db_save(self, execute, data=None):
        raise NotImplementedMethodeError(self, self._db_save)

    def _data_traversal(self,
                        predicate=lambda obj, data: True,
                        modifier=lambda obj, data: None,
                        data={}):
        """Traversal data and execute modifier fonction if predicate
        true

        Args:
            predicate: Function predicate, take current obj and data as input
            modifier: Function modifier, take current obj and data as input

        Returns:
            Nothing
        """
        for tab in self._tabs:
            for el in self._tabs[tab]:
                el._data_traversal(predicate, modifier, data)

    ################
    # MODEL METHOD #
    ################

    def len(self, lst):
        """Size of tab list

        Args:
            lst: The tab name

        Returns:
            The size of the tab list
        """
        return len(self._tabs[lst])

    def get_tab(self, lst):
        """Get tab list (copy) from name

        Args:
            lst: The tab name

        Returns:
            Nothing
        """
        return self._tabs[lst].copy()

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

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

    def new(self, lst, index):
        """Create new elements and add it to list

        Args:
            lst: The tab name
            index: The index of new elements

        Returns:
            The new elements
        """
        raise NotImplementedMethodeError(self, self.new)

    def insert(self, lst, index, new):
        """Insert element in tab

        Args:
            lst: The tab name
            index: The index of new element
            new: The new elements

        Returns:
            Nothing
        """
        self._tabs[lst].insert(index, new)
        self._status.modified()

    def delete(self, lst, els):
        """Delete elements from specific tab

        Args:
            lst: The tab name
            els: The elements list

        Returns:
            Nothing
        """
        for el in els:
            self._tabs[lst].remove(el)
        self._status.modified()

    def delete_i(self, lst, indexes):
        """Delete elements from specific tab

        Args:
            lst: The tab name
            indexes: The elements indexes

        Returns:
            Nothing
        """
        els = list(
            map(
                lambda x: x[1],
                filter(
                    lambda x: x[0] in indexes,
                    enumerate(self._tabs[lst])
                )
            )
        )
        self.delete(lst, els)

    def sort(self, lst, reverse=False, key=None):
        self._tabs[lst].sort(reverse=reverse, key=key)
        self._status.modified()

    def move_up(self, lst, index):
        if index < len(self._tabs[lst]):
            next = index - 1

            lst = self._tabs[lst]
            lst[index], lst[next] = lst[next], lst[index]
            self._status.modified()

    def move_down(self, lst, index):
        if index >= 0:
            prev = index + 1

            lst = self._tabs[lst]
            lst[index], lst[prev] = lst[prev], lst[index]
            self._status.modified()
