{-# language FlexibleContexts, RecordWildCards #-}
-----------------------------------------------------------------------------
-- |
-- Copyright   :  (C) 2016 Marco Zocca
-- License     :  GPL-3 (see LICENSE)
-- Maintainer  :  zocca.marco gmail
-- Stability   :  provisional
-- Portability :  portable
--
--
-- Pretty printing helper functions
-----------------------------------------------------------------------------
module Data.Sparse.PPrint (prd, prd0, PrintDense, newline
                           , PPrintOptions(..)
                             -- , prdefR, prdefC
                           , printDN , printCN
                          ) where

import Data.Complex
import Data.List
import Text.Printf

import Numeric.Eps



class PrintDense a where
  -- | Pretty-print with a descriptive header
  prd :: a -> IO ()
  -- | Pretty-print with no header
  prd0 :: a -> IO ()

newline :: IO ()
newline = putStrLn ""  


-- | Pretty printing options: total length in # digits (including the decimal point), # of decimal digits
data PPrintOptions =
  PPOpts {
     pprintLen, pprintDec, pprintColWidth :: Int
     } deriving (Eq, Show)

-- | Some defaults
prdefR, prdefC :: PPrintOptions
prdefR = PPOpts 4 2 7   -- reals
prdefC = PPOpts 4 2 16  -- complex values


-- | Pretty print an array of real numbers
printDN :: (PrintfArg a, Epsilon a, Ord a) =>
     Int -> Int -> PPrintOptions -> [a] -> String
printDN l n = printNpad l n f where
  f o x | isNz x = printf (prepD o x) x
        | otherwise = printf "_"


-- | Pretty print an array of complex numbers
printCN :: (PrintfArg a, Epsilon a, Epsilon (Complex a), Ord a) =>
     Int -> Int -> PPrintOptions -> [Complex a] -> String
printCN l n = printNpad l n f where
  f o x | nearZero (re x) && isNz (imagPart x) =
               printf (prepD o (imagPart x)++ "i") (aim x)
        | nearZero (imagPart x) && isNz (re x) =
               printf (prepD o (realPart x)) (re x)
        | isNz x = printf (prepC o x) (re x) (aim x)
        | otherwise = printf "_"






-- | printf an array of items with padding space to render a fixed column width
printNpad ::
     Int     -- ^ Length of list to be provided
     -> Int  -- ^ 
     -> (PPrintOptions -> a -> String)
     -> PPrintOptions -> [a] -> String
printNpad llen nmax f o@PPOpts{..} xxl = commas [h,l] where
  h = commas $ take hlen ll
  l = last ll
  hlen = min (llen-1) (nmax-1)
  ll = unfoldr g (0, xxl) 
  g (i, x:xs) | i<nmax-2 || llen>=nmax-1 = Just (s', sxs)
              | i==nmax-2 = Just (dots', sxs)
              | null xs = Just (s', sxs)
              | otherwise = Just ("", sxs) where                  
                  s = f o x
                  sxs = (succ i, xs)
                  s' = s ++ spaces (n - length s) 
                  dots' = dots ++ spaces (n - length dots)
  g (_, []) = Nothing
  n = pprintColWidth
  dots = " ... "






-- | printf format string
prepD :: (Ord t, Epsilon t) => PPrintOptions -> t -> String
prepD PPOpts{..} x = s where
  s | nearZero x  = "_"
    | abs x > magHi || abs x < magLo = s0 ++ "e"
    | otherwise = s0 ++ "f"
  s0 = concat ["%" , show nl, ".", show nd]
  -- s0 = "%1." ++ show nd
  nl = pprintLen  
  nd = pprintDec 
  nint = nl - nd  -- # of integer digits
  magLo = 10 ** (- fromIntegral nd)
  magHi = 10 ** fromIntegral nint
            


-- | printf format string for a Complex
prepC :: (Epsilon t, Ord t) => PPrintOptions -> Complex t -> String
prepC opts (r :+ i) = prepD opts r ++ oi where
  oi = concat [s, prepD opts i', "i"]
  s | signum i >= 0 = " + "
    | otherwise = " - "
  i' = abs i




    

-- | Helpers

spaces :: Int -> String
spaces n = replicate n ' '

commas :: [String] -> String    
commas = intercalate ", "

re :: Complex a -> a
re = realPart

aim :: Num a => Complex a -> a
aim = abs . imagPart