#
# SPDX-FileCopyrightText: Sebastiano Barezzi
# SPDX-License-Identifier: Apache-2.0
#
"""Android fstab library."""

from itertools import repeat
from pathlib import Path
from typing import List, Set

from sebaubuntu_libs.libandroid.partitions.partition_model import PartitionModel, PartitionModels

FSTAB_HEADER = "#<src>                                                 <mnt_point>            <type>  <mnt_flags and options>                            <fs_mgr_flags>\n"


class FstabEntry:
    """
    A class representing a fstab entry
    """

    def __init__(
        self,
        src: str,
        mount_point: str,
        fs_type: str,
        mnt_flags: List[str],
        fs_flags: List[str],
    ):
        self.src = src
        self.mount_point = mount_point
        self.fs_type = fs_type
        self.mnt_flags = mnt_flags
        self.fs_flags = fs_flags

    def is_logical(self):
        return "logical" in self.fs_flags

    def is_slotselect(self):
        return "slotselect" in self.fs_flags

    @classmethod
    def from_entry(cls, line: str):
        src, mount_point, fs_type, mnt_flags, fs_flags = line.split()

        return cls(src, mount_point, fs_type, mnt_flags.split(","), fs_flags.split(","))


class Fstab:
    def __init__(self, fstab: Path):
        self.fstab = fstab

        self.entries: List[FstabEntry] = []

        for line in self.fstab.read_text().splitlines():
            if not line:
                continue

            if line.startswith("#"):
                continue

            self.entries.append(FstabEntry.from_entry(line))

    def __str__(self):
        return self.format()

    def format(self, twrp: bool = False):
        entries: List[str] = []

        src_len_max = 0
        mount_point_len_max = 0
        fs_type_len_max = 0
        mnt_flags_len_max = 0

        for entry in self.entries:
            mount_point_len = len(entry.mount_point)
            if mount_point_len > mount_point_len_max:
                mount_point_len_max = mount_point_len

            fs_type_len = len(entry.fs_type)
            if fs_type_len > fs_type_len_max:
                fs_type_len_max = fs_type_len

            src_len = len(entry.src)
            if src_len > src_len_max:
                src_len_max = src_len

            mnt_flags_len = len(entry.mnt_flags)
            if mnt_flags_len > mnt_flags_len_max:
                mnt_flags_len_max = mnt_flags_len

        src_len_max += 5
        mount_point_len_max += 5
        fs_type_len_max += 5
        mnt_flags_len_max += 5

        for entry in self.entries:
            src_space = ""
            mount_point_space = ""
            fs_type_space = ""
            mnt_flags_space = ""

            for _ in repeat(None, src_len_max - len(entry.src)):
                src_space += " "
            for _ in repeat(None, mount_point_len_max - len(entry.mount_point)):
                mount_point_space += " "
            for _ in repeat(None, fs_type_len_max - len(entry.fs_type)):
                fs_type_space += " "
            for _ in repeat(None, mnt_flags_len_max - len(entry.mnt_flags)):
                mnt_flags_space += " "

            if not twrp:
                entries.append(
                    f"{entry.src}{src_space}{entry.mount_point}{mount_point_space}{entry.fs_type}{fs_type_space}{','.join(entry.mnt_flags)}{mnt_flags_space}{','.join(entry.fs_flags)}"
                )
            else:
                flags = [f"display={Path(entry.mount_point).name}"]
                if entry.is_logical():
                    flags.append("logical")
                if entry.is_slotselect():
                    flags.append("slotselect")
                entries.append(
                    f"{entry.mount_point}{mount_point_space}{entry.fs_type}{fs_type_space}{entry.src}{src_space}flags={';'.join(flags)}"
                )

        entries.append("")

        return "\n".join(entries)

    def get_partition_by_mount_point(self, mount_point: str):
        for entry in self.entries:
            if entry.mount_point == mount_point:
                return entry

        return None

    def get_logical_partitions(self):
        return [entry for entry in self.entries if entry.is_logical()]

    def get_logical_partitions_models(self) -> Set[PartitionModel]:
        models: Set[PartitionModel] = set()

        for entry in self.entries:
            if entry.is_logical():
                partition_model = PartitionModels.from_mount_point(entry.mount_point)
                if partition_model is None:
                    continue
                models.add(partition_model)

        return models

    def get_slotselect_partitions(self):
        return [entry for entry in self.entries if entry.is_slotselect()]

    def get_ab_partitions_models(self) -> Set[PartitionModel]:
        models: Set[PartitionModel] = set()

        for entry in self.get_slotselect_partitions():
            partition_model = PartitionModels.from_mount_point(entry.mount_point)
            if partition_model is None:
                continue
            models.add(partition_model)

        return models
