{-# LANGUAGE PolyKinds, DataKinds, TypeOperators, FlexibleInstances,
             ScopedTypeVariables, FlexibleContexts, ConstraintKinds, CPP,
             UndecidableInstances #-}

#if __GLASGOW_HASKELL__ < 709
{-# LANGUAGE OverlappingInstances #-}
#endif

#if __GLASGOW_HASKELL__ >= 900
{-# OPTIONS_GHC -Wno-star-is-type #-}
#endif

{-# OPTIONS_GHC -fno-warn-orphans #-}

-----------------------------------------------------------------------------
-- |
-- Module      :  Data.Metrology.Show
-- Copyright   :  (C) 2013 Richard Eisenberg
-- License     :  BSD-style (see LICENSE)
-- Maintainer  :  Richard Eisenberg (rae@cs.brynmawr.edu)
-- Stability   :  experimental
-- Portability :  non-portable
--
-- This module defines 'Show' instance for quantities. The show instance
-- prints out the number stored internally with its correct units. To print
-- out quantities with specific units use the function `showIn`.
-----------------------------------------------------------------------------

module Data.Metrology.Show () where

import Data.Proxy (Proxy(..))
import Data.List
import Data.Singletons (sing, SingI)

import Data.Metrology.Factor
import Data.Metrology.Qu
import Data.Metrology.Z
import Data.Metrology.LCSU

class ShowUnitFactor (dims :: [Factor *]) where
  showDims :: Bool   -- take absolute value of exponents?
           -> Proxy dims -> ([String], [String])

instance ShowUnitFactor '[] where
  showDims _ _ = ([], [])

instance (ShowUnitFactor rest, Show unit, SingI z)
         => ShowUnitFactor (F unit z ': rest) where
  showDims take_abs _ =
    let (nums, denoms) = showDims take_abs (Proxy :: Proxy rest)
        baseStr        = show (undefined :: unit)
        power          = szToInt (sing :: Sing z)
        abs_power      = if take_abs then abs power else power
        str            = if abs_power == 1
                         then baseStr
                         else baseStr ++ "^" ++ (show abs_power) in
    case compare power 0 of
      LT -> (nums, str : denoms)
      EQ -> (nums, denoms)
      GT -> (str : nums, denoms)

showFactor :: ShowUnitFactor dimspec => Proxy dimspec -> String
showFactor p
  = let (nums, denoms) = mapPair (build_string . sort) $ showDims True p in
    case (length nums, length denoms) of
      (0, 0) -> ""
      (_, 0) -> " " ++ nums
      (0, _) -> " " ++ build_string (snd (showDims False p))
      (_, _) -> " " ++ nums ++ "/" ++ denoms
  where
    mapPair :: (a -> b) -> (a, a) -> (b, b)
    mapPair f (x, y) = (f x, f y)

    build_string :: [String] -> String
    build_string [] = ""
    build_string [s] = s
    build_string s = "(" ++ build_string_helper s ++ ")"

    build_string_helper :: [String] -> String
    build_string_helper [] = ""
    build_string_helper [s] = s
    build_string_helper (h:t) = h ++ " * " ++ build_string_helper t

instance
#if __GLASGOW_HASKELL__ >= 709
    {-# OVERLAPPABLE #-}
#endif
    (ShowUnitFactor (LookupList dims lcsu), Show n)
    => Show (Qu dims lcsu n) where
  show (Qu d) = show d ++
                (showFactor (Proxy :: Proxy (LookupList dims lcsu)))
