    {-# LANGUAGE OverloadedStrings #-}
    {- |
       Module      : Text.Pandoc.Readers.Xlsx.Cells
       Copyright   : © 2025 Anton Antic
       License     : GNU GPL, version 2 or above

       Maintainer  : Anton Antic <anton@everworker.ai>
       Stability   : alpha
       Portability : portable

    Cell types and parsing for XLSX.
    -}
    module Text.Pandoc.Readers.Xlsx.Cells
      ( CellRef(..)
      , XlsxCell(..)
      , CellValue(..)
      , parseCellRef
      ) where

    import qualified Data.Text as T
    import Data.Text (Text)
    import Data.Char (ord, isAlpha)
    import Text.Read (readMaybe)

    -- | Cell reference (A1 notation)
    data CellRef = CellRef
      { CellRef -> Int
    cellRefCol :: Int    -- 1-based (A=1, B=2, ..., AA=27)
      , CellRef -> Int
    cellRefRow :: Int    -- 1-based
      } deriving (Int -> CellRef -> ShowS
    [CellRef] -> ShowS
    CellRef -> String
    (Int -> CellRef -> ShowS)
    -> (CellRef -> String) -> ([CellRef] -> ShowS) -> Show CellRef
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: Int -> CellRef -> ShowS
    showsPrec :: Int -> CellRef -> ShowS
    $cshow :: CellRef -> String
    show :: CellRef -> String
    $cshowList :: [CellRef] -> ShowS
    showList :: [CellRef] -> ShowS
    Show, CellRef -> CellRef -> Bool
    (CellRef -> CellRef -> Bool)
    -> (CellRef -> CellRef -> Bool) -> Eq CellRef
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    $c== :: CellRef -> CellRef -> Bool
    == :: CellRef -> CellRef -> Bool
    $c/= :: CellRef -> CellRef -> Bool
    /= :: CellRef -> CellRef -> Bool
    Eq, Eq CellRef
    Eq CellRef =>
    (CellRef -> CellRef -> Ordering)
    -> (CellRef -> CellRef -> Bool)
    -> (CellRef -> CellRef -> Bool)
    -> (CellRef -> CellRef -> Bool)
    -> (CellRef -> CellRef -> Bool)
    -> (CellRef -> CellRef -> CellRef)
    -> (CellRef -> CellRef -> CellRef)
    -> Ord CellRef
    CellRef -> CellRef -> Bool
    CellRef -> CellRef -> Ordering
    CellRef -> CellRef -> CellRef
    forall a.
    Eq a =>
    (a -> a -> Ordering)
    -> (a -> a -> Bool)
    -> (a -> a -> Bool)
    -> (a -> a -> Bool)
    -> (a -> a -> Bool)
    -> (a -> a -> a)
    -> (a -> a -> a)
    -> Ord a
    $ccompare :: CellRef -> CellRef -> Ordering
    compare :: CellRef -> CellRef -> Ordering
    $c< :: CellRef -> CellRef -> Bool
    < :: CellRef -> CellRef -> Bool
    $c<= :: CellRef -> CellRef -> Bool
    <= :: CellRef -> CellRef -> Bool
    $c> :: CellRef -> CellRef -> Bool
    > :: CellRef -> CellRef -> Bool
    $c>= :: CellRef -> CellRef -> Bool
    >= :: CellRef -> CellRef -> Bool
    $cmax :: CellRef -> CellRef -> CellRef
    max :: CellRef -> CellRef -> CellRef
    $cmin :: CellRef -> CellRef -> CellRef
    min :: CellRef -> CellRef -> CellRef
    Ord)

    -- | Cell value types
    data CellValue
      = TextValue Text
      | NumberValue Double
      | EmptyValue
      deriving (Int -> CellValue -> ShowS
    [CellValue] -> ShowS
    CellValue -> String
    (Int -> CellValue -> ShowS)
    -> (CellValue -> String)
    -> ([CellValue] -> ShowS)
    -> Show CellValue
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: Int -> CellValue -> ShowS
    showsPrec :: Int -> CellValue -> ShowS
    $cshow :: CellValue -> String
    show :: CellValue -> String
    $cshowList :: [CellValue] -> ShowS
    showList :: [CellValue] -> ShowS
    Show, CellValue -> CellValue -> Bool
    (CellValue -> CellValue -> Bool)
    -> (CellValue -> CellValue -> Bool) -> Eq CellValue
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    $c== :: CellValue -> CellValue -> Bool
    == :: CellValue -> CellValue -> Bool
    $c/= :: CellValue -> CellValue -> Bool
    /= :: CellValue -> CellValue -> Bool
    Eq)

    -- | Parsed cell
    data XlsxCell = XlsxCell
      { XlsxCell -> CellRef
    cellRef :: CellRef
      , XlsxCell -> CellValue
    cellValue :: CellValue
      , XlsxCell -> Bool
    cellBold :: Bool
      , XlsxCell -> Bool
    cellItalic :: Bool
      } deriving (Int -> XlsxCell -> ShowS
    [XlsxCell] -> ShowS
    XlsxCell -> String
    (Int -> XlsxCell -> ShowS)
    -> (XlsxCell -> String) -> ([XlsxCell] -> ShowS) -> Show XlsxCell
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: Int -> XlsxCell -> ShowS
    showsPrec :: Int -> XlsxCell -> ShowS
    $cshow :: XlsxCell -> String
    show :: XlsxCell -> String
    $cshowList :: [XlsxCell] -> ShowS
    showList :: [XlsxCell] -> ShowS
    Show)

    -- | Parse cell reference (A1 → CellRef)
    parseCellRef :: Text -> Either Text CellRef
    parseCellRef :: Text -> Either Text CellRef
    parseCellRef Text
    ref = do
      let (Text
    colStr, Text
    rowStr) = (Char -> Bool) -> Text -> (Text, Text)
    T.span Char -> Bool
    isAlpha Text
    ref

      row <- case String -> Maybe Int
    forall a. Read a => String -> Maybe a
    readMaybe (Text -> String
    T.unpack Text
    rowStr) of
        Just Int
    r | Int
    r Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    > Int
    0 -> Int -> Either Text Int
    forall a b. b -> Either a b
    Right Int
    r
        Maybe Int
    _ -> Text -> Either Text Int
    forall a b. a -> Either a b
    Left (Text -> Either Text Int) -> Text -> Either Text Int
    forall a b. (a -> b) -> a -> b
    $ Text
    "Invalid row: " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    rowStr

      col <- parseColumn colStr

      return $ CellRef col row

    -- | Parse column (A=1, Z=26, AA=27, etc.)
    parseColumn :: Text -> Either Text Int
    parseColumn :: Text -> Either Text Int
    parseColumn Text
    colStr
      | Text -> Bool
    T.null Text
    colStr = Text -> Either Text Int
    forall a b. a -> Either a b
    Left Text
    "Empty column"
      | Bool
    otherwise = Int -> Either Text Int
    forall a b. b -> Either a b
    Right (Int -> Either Text Int) -> Int -> Either Text Int
    forall a b. (a -> b) -> a -> b
    $ (Int -> Char -> Int) -> Int -> Text -> Int
    forall a. (a -> Char -> a) -> a -> Text -> a
    T.foldl' (\Int
    acc Char
    c -> Int
    acc Int -> Int -> Int
    forall a. Num a => a -> a -> a
    * Int
    26 Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + (Char -> Int
    ord Char
    c Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Char -> Int
    ord Char
    'A' Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Int
    1)) Int
    0 Text
    colStr