from dataclasses import dataclass, field
from enum import Enum
import os
from typing import List, Optional
from geopic_tag_reader import reader
import toml
from pathlib import Path


@dataclass
class Geovisio:
    url: str
    token: Optional[str] = None


@dataclass
class Picture:
    path: Optional[str] = None
    id: Optional[str] = None
    location: Optional[str] = None
    status: Optional[str] = None
    metadata: Optional[reader.GeoPicTags] = None

    def toml(self):
        return {
            "path": self.path,
            "id": self.id,
            "location": self.location,
            "status": self.status,
        }

    @staticmethod
    def from_toml(data):
        return Picture(
            path=data.get("path"),
            id=data.get("id"),
            location=data.get("location"),
            status=data.get("status"),
        )


@dataclass
class Split:
    prevPic: Picture
    nextPic: Picture
    reason: str


@dataclass
class InteriorOrientation:
    make: str
    model: str
    field_of_view: Optional[int]


class SortMethod(str, Enum):
    filename_asc = "filename-asc"
    filename_desc = "filename-desc"
    time_asc = "time-asc"
    time_desc = "time-desc"


@dataclass
class Sequence:
    title: str = ""
    path: str = ""
    id: Optional[str] = None
    location: Optional[str] = None
    producer: Optional[str] = None
    interior_orientation: List[InteriorOrientation] = field(default_factory=lambda: [])
    pictures: List[Picture] = field(default_factory=lambda: [])
    sort_method: Optional[SortMethod] = None

    def toml(self):
        res = {
            "sequence": {
                "title": self.title,
                "path": self.path,
                "id": self.id,
                "location": self.location,
                "producer": self.producer,
                "sort_method": self.sort_method.value
                if self.sort_method is not None
                else None,
            },
            "pictures": {},
        }

        for pos, pic in enumerate(self.pictures, start=1):
            pict = pic.toml()
            pict["position"] = pos
            res["pictures"][os.path.basename(pic.path)] = pict

        return res

    @staticmethod
    def from_toml(data):
        s = Sequence()
        s.update_from_toml(data)
        return s

    def update_from_toml(self, data):
        if data.get("sequence"):
            self.title = data["sequence"].get("title", "")
            self.path = data["sequence"].get("path", "")
            self.id = data["sequence"].get("id")
            self.location = data["sequence"].get("location")
            self.producer = data["sequence"].get("producer")
            self.sort_method = (
                SortMethod(data["sequence"]["sort_method"])
                if "sort_method" in data["sequence"]
                else None
            )

        if data.get("pictures"):
            self.pictures = [
                Picture.from_toml(picData)
                for picId, picData in sorted(
                    data["pictures"].items(), key=lambda item: int(item[1]["position"])
                )
            ]

    def all_done(self):
        return self.nb_waiting() + self.nb_preparing() == 0

    def nb_ready(self):
        return sum((1 for p in self.pictures if p.status == "ready"))

    def nb_waiting(self):
        return sum((1 for p in self.pictures if p.status == "waiting-for-process"))

    def nb_preparing(self):
        return sum(
            (
                1
                for p in self.pictures
                if (p.status and p.status.startswith("preparing"))
            )
        )

    def nb_broken(self):
        return sum((1 for p in self.pictures if p.status == "broken"))


@dataclass
class ManySequences:
    """ManySequences is a single handler for multiple sequences found in a single folder."""

    toml_file: Path
    sequences: List[Sequence] = field(default_factory=lambda: [])
    splits: List[Split] = field(default_factory=lambda: [])

    def toml(self):
        res = {}
        for i, s in enumerate(self.sequences, start=1):
            res[str(i)] = s.toml()
        return res

    def has_same_sort_method(self, sortMethod):
        if sortMethod is None:
            return True
        else:
            for s in self.sequences:
                if s.sort_method != sortMethod:
                    return False
            return True

    def is_empty(self):
        if len(self.sequences) == 0:
            return True
        return not any((len(s.pictures) != 0 for s in self.sequences))

    def has_valid_pictures(self):
        for s in self.sequences:
            for p in s.pictures:
                if p.status != "broken-metadata":
                    return True
        return False

    @classmethod
    def read_from_file(cls, file: Path):
        """Read multiple sequences from a single TOML config file"""

        with open(file, "r") as f:
            tomlFileContent = f.read()
            if len(tomlFileContent) == 0:
                raise EOFError("Config file is empty")
            tomlContent = toml.loads(tomlFileContent)
            f.close()

            # Retro-compatibility with old file formats (single-sequence in file)
            if tomlContent.get("sequence") is not None:
                ms = ManySequences(
                    toml_file=file, sequences=[Sequence.from_toml(tomlContent)]
                )
            else:
                ms = ManySequences(
                    toml_file=file,
                    sequences=[
                        Sequence.from_toml(seqData) for seqData in tomlContent.values()
                    ],
                )

            return ms

    def persist(self) -> Path:
        """Writes TOML sequence metadata file"""

        with open(self.toml_file, "w") as f:
            f.write(toml.dumps(self.toml()))
            f.close()

        return self.toml_file


SEQUENCE_TOML_FILE = "_geovisio.toml"
