"""Class that represents a realized subset of the `GenomicArrayDataset`.

This class provides a slice data class usually generated by the access
methods from
:py:func:`~genomicarray.GenomicArrayDataset.GenomicArrayDataset`.

Example:

    .. code-block:: python

        from genomicarray import GenomicArrayDataset

        garr = GenomicArrayDataset(dataset_path="/path/to/genomicarray/dir")
        feature_indices = slice(0, 10)
        result1 = garr[feature_indices, 0]

        print(result1)
"""

from dataclasses import dataclass
from typing import Any

import anndata
import genomicranges as gr
import pandas as pd
import summarizedexperiment as se

__author__ = "Jayaram Kancherla"
__copyright__ = "Jayaram Kancherla"
__license__ = "MIT"


@dataclass
class GenomicArrayDatasetSlice:
    """Class that represents a realized subset of the `CellArrDataset`."""

    sample_metadata: pd.DataFrame
    feature_annotation: pd.DataFrame
    matrix: Any

    ####
    ## Interop
    ####

    def to_anndata(self):
        """Convert the realized slice to :py:class:`~anndata.AnnData`."""
        return anndata.AnnData(
            layers={"matrix": self.matrix.transpose()},
            obs=self.sample_metadata,
            var=self.feature_annotation,
        )

    def to_rangedsummarizedexperiment(self):
        """Convert the realized slice to
        :py:class:`~summarizedexperiment.RangedSummarizedExperiment.RangedSummarizedExperiment`."""
        return se.RangedSummarizedExperiment(
            assays={"matrix": self.matrix},
            row_ranges=gr.GenomicRanges.from_pandas(self.feature_annotation),
            column_data=self.sample_metadata,
        )

    ####
    ## Misc methods.
    ####

    @property
    def shape(self):
        return self.matrix.shape

    def __len__(self):
        return self.shape[0]

    ####
    ## Printing.
    ####

    def __repr__(self) -> str:
        """
        Returns:
            A string representation.
        """
        output = f"{type(self).__name__}(number_of_rows={self.shape[0]}"
        output += f", number_of_columns={self.shape[1]}"

        output += ")"
        return output

    def __str__(self) -> str:
        """
        Returns:
            A pretty-printed string containing the contents of this object.
        """
        output = f"class: {type(self).__name__}\n"

        output += f"number_of_rows: {self.shape[0]}\n"
        output += f"number_of_columns: {self.shape[1]}\n"

        return output
