packages feed

clash-shockwaves-1.1.1: src/Clash/Shockwaves/Internal/Waveform.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fconstraint-solver-iterations=10 #-}

{- |
Copyright  :  (C) 2025-2026, QBayLogic B.V.
License    :  BSD2 (see the file LICENSE)
Maintainer :  QBayLogic B.V. <devops@qbaylogic.com>

The 'Waveform' class, functions derived from it, special 'Waveform' variants such as
'WaveformLUT', and 'Waveform' instances for default types.
-}
module Clash.Shockwaves.Internal.Waveform where

import Clash.Prelude hiding (bitSize)

import Clash.Shockwaves.BitList (BitList)
import qualified Clash.Shockwaves.BitList as BL
import Clash.Shockwaves.Internal.TH.Waveform (deriveWaveformTuples)
import Clash.Shockwaves.Internal.Translator
import Clash.Shockwaves.Internal.Types
import Clash.Shockwaves.Internal.Util

import Data.Char (isAlpha)
import qualified Data.List as L
import qualified Data.Map as M
import Data.Maybe (fromMaybe, listToMaybe)
import Data.Proxy
import Data.Typeable
import GHC.Generics

-- for standard type instances
import Data.Int (Int16, Int32, Int64, Int8)
import Data.Word (Word16, Word32, Word64, Word8)

import Clash.Num.Erroring (Erroring)
import Clash.Num.Overflowing (Overflowing)
import Clash.Num.Saturating (Saturating)
import Clash.Num.Wrapping (Wrapping)
import Clash.Num.Zeroing (Zeroing)
import Data.Bifunctor (first)
import Data.Complex (Complex)
import Data.Functor.Identity (Identity)
import Data.Ord (Down)

{- FOURMOLU_DISABLE -}
#ifndef MAX_TUPLE_SIZE
#ifdef LARGE_TUPLES

#if MIN_VERSION_ghc(9,0,0)
import GHC.Settings.Constants (mAX_TUPLE_SIZE)
#else
import Constants (mAX_TUPLE_SIZE)
#endif
#define MAX_TUPLE_SIZE (fromIntegral mAX_TUPLE_SIZE)

#else
#ifdef HADDOCK_ONLY
#define MAX_TUPLE_SIZE 3
#else
#define MAX_TUPLE_SIZE 12
#endif
#endif
#endif
{- FOURMOLU_ENABLE -}

-- making values

-- | Get a 'Render' from a 'Value' using 'WSDefault' and precedence 11.
defaultRender :: Value -> Render
defaultRender v = Just (v, WSDefault, 11)

-- making translators

{- | Wrap a t'Translator' in a 'TStyled' variant translator with the
provided style.
-}
tStyled :: WaveStyle -> Translator -> Translator
tStyled s (Translator w v) = Translator w $ TStyled s (Translator w v)

{- | Wrap a t'Translator' in a 'TDuplicate' variant translator with the
provided subsignal name.
-}
tDup :: SubSignal -> Translator -> Translator
tDup name (Translator w t) = Translator w $ TDuplicate name (Translator w t)

{- | Generate a translator reference for a type.
Also checks whether the translator width matches the value of 'bitSize' for
the type: if not, the function errors.
-}
tRef :: forall a. (Waveform a) => Translator
tRef
  | w == bitSize @a =
      Translator (bitSize @a)
        $ TRef
          (typeName @a)
          TypeRef
            { translateBinRef = translateBin @a
            , translatorRef = translator @a
            , structureRef = structure @a
            }
  | otherwise =
      error
        $ "The Translator width and BitSize for type "
        <> show (typeName @a)
        <> " do not match."
 where
  Translator w _ = translator @a

-- | Create a constant translator that consumes 0 bits and has no subsignals.
tConst :: Render -> Translator
tConst r = Translator 0 $ TConst $ Translation r []

{- | Create a LUT translator for a type, using either the static LUT or the translation
function specified in 'WaveformLUT'
-}
tLut :: forall a. (Waveform a, WaveformLUT a) => Maybe LUT -> Translator
tLut l = case l of
  Just lut -> tStaticLut @a lut
  Nothing -> tGeneratedLut @a

-- | Create a LUT translator for a type, using the translation function of 'WaveformLUT'.
tGeneratedLut :: forall a. (Waveform a, WaveformLUT a) => Translator
tGeneratedLut =
  Translator (bitSize @a)
    $ TLut
      (typeName @a)
      Nothing
      TypeRef
        { translateBinRef = translateL @a . BL.unpack
        , structureRef = structureL @a
        , translatorRef = translator @a
        }

-- | Create a LUT translator for a type, using the static LUT in 'WaveformLUT'.
tStaticLut :: forall a. (Waveform a, WaveformLUT a) => LUT -> Translator
tStaticLut lut =
  Translator (bitSize @a)
    $ TLut
      (typeName @a)
      (Just lut)
      TypeRef
        { translateBinRef = translateStaticL @a . BL.unpack
        , structureRef = L.foldl1 (<>) $ L.map fromTranslation $ M.elems lut
        , translatorRef = translator @a
        }

------------------------------------------ WAVEFORM --------------------------------------

{-# DEPRECATED width "Use bitSize instead" #-}

{- |

'Waveform' is the main class for making types displayable in the waveform viewer.
The class is responsible for defining an appropriate translator and subsignal
structure, as well as registering types.

To make a LUT approache possible, the class must also be able to translate values,
and to register individual values.

By default, 'GHC.Generics.Generic' is used to automatically derive this behaviour.
Extra classes are provided to help implement lookup tables or common types,
like numerical translators. Custom implementations are also very possible.
-}
class (Typeable a, BitPack a) => Waveform a where
  {- | Provide the type name.
  Overriding this value is only really useful for derive via strategies.
  -}
  typeName :: TypeName
  typeName = defaultTypeName @a

  -- | The translator used for the data type. Must match the structure value.
  translator :: Translator
  default translator :: (WaveformG (Rep a ())) => Translator
  translator =
    inheritSingleFieldStyle
      $ withConstructorStyles (constructorStyles @a)
      $ defaultTranslator @a

  {- | List of styles used for constructors.

  Since assigning different constructors different colors is a very common usecase
  of the waveform style,
  this list can be overridden to provides styles for the constructors, in order.
  To not change a style, use 'WSDefault'.
  -}
  constructorStyles :: [WaveStyle]
  constructorStyles = []

  {- |
  Defines the width of the translator based on @bitSize@
  -}
  width :: Int
  width = bitSize @a

{- | Return the default translator that is derived for a data type.
This default can be modified to obtain a slightly different translator.
-}
defaultTranslator ::
  forall a. (BitPack a, WaveformG (Rep a ())) => Translator
defaultTranslator = translatorG @(Rep a ()) (bitSize @a)

{- | Function to translate values. This function creates a translation from
the binary representation of the data using translateBin, and the translator.
-}
translate :: forall a. (Waveform a, BitPack a) => a -> Translation
translate = translateBin @a . BL.pack

{- | Translate binary data.
Normally, this simply translates the value according to the translator.
For LUTs, this involves translating the value back to the original type and
translating it using a specially defined translation function.
-}
translateBin :: forall a. (Waveform a) => BitList -> Translation
translateBin = translateBinT (translator @a)

-- | Register this type and all its subtypes.
addTypes :: forall a. (Waveform a) => TypeMap -> TypeMap
addTypes = addTypesT $ tRef @a

-- | Helper function that fills the 'constructorStyles' list with 'WSDefault'.
constructorStyles' :: forall a. (Waveform a) => [WaveStyle]
constructorStyles' = constructorStyles @a <> L.repeat WSDefault

-- | Check if the type requires values to be added to LUTs.
hasGeneratedLut :: forall a. (Waveform a) => Bool
hasGeneratedLut = hasGeneratedLutT $ translator @a

-- | Return the structure of a type.
structure :: forall a. (Waveform a) => Structure
structure = structureT $ translator @a

-- | Add all (sub) values that use 'TLut' to their respective LUTs.
addValue :: forall a. (Waveform a) => a -> [LUTMap -> LUTMap]
addValue = addValueT (translator @a) . BL.pack

-- translator modification

{- | Remove constructor subsignals from a (generated) translator.
This results in all constructor field subsignals becoming direct subsignals of the toplevel signal.
Set rename to `True` to add the constructor's name as a prefix to the signal name.

Essentially, this function searches through 'TStyled' and 'TSum' for any 'TDuplicate' translators to remove.
If renaming subsignals, it then searches through 'TStyled' to rename subsignals in 'TProduct'.
-}
noConstructorSubsignals :: Bool -> Translator -> Translator
noConstructorSubsignals rename (Translator w (TStyled s t)) = Translator w $ TStyled s $ noConstructorSubsignals rename t
noConstructorSubsignals rename (Translator w (TSum subs)) = Translator w $ TSum $ noConstructorSubsignals rename <$> subs
noConstructorSubsignals rename (Translator _ (TDuplicate n t)) = if rename then prefixFields t else t
 where
  prefixFields (Translator w (TStyled s t')) = Translator w $ TStyled s $ prefixFields t'
  prefixFields (Translator w p@TProduct{subs}) = Translator w p{subs = (\(s, t') -> (n <> "." <> s, t')) <$> subs}
  prefixFields t' = t'
noConstructorSubsignals _ t = t

{- | Rename constructor fields. This is particularly useful for non-record types.
The input is a list of a list of field names, per constructor.
Errors if the number of constructors/fields does not match the structure of the 'Translator'.
For translators other than 'TProduct', use an empty list of fieldnames.
-}
renameFields :: [[String]] -> Translator -> Translator
renameFields names (Translator w (TStyled s t)) = Translator w $ TStyled s $ renameFields names t
renameFields names (Translator w (TDuplicate n t)) = Translator w $ TDuplicate n $ renameFields names t
renameFields names (Translator w (TSum subs)) =
  Translator w
    $ TSum
    $ erroringZipWith
      ("Incorrect number of constructors:" <> show names)
      (\n t -> renameFields [n] t)
      names
      subs
renameFields names (Translator w p@TProduct{subs}) =
  Translator
    w
    p
      { subs =
          erroringZipWith
            ("Incorrect number of fields" <> show fieldNames)
            (\n (_, t) -> (n, t))
            fieldNames
            subs
      }
 where
  fieldNames = case names of
    [x] -> x
    _ -> error ("Incorrect number of constructors: " <> show names)
renameFields [[]] t = t
renameFields names t =
  error
    ( "renameFields encountered unexpected Translator for names "
        <> show names
        <> ": "
        <> show t
    )

{- | Rename the constructors subsignals of a data type.
Errors if the number of constructor subsignal names provided is incorrect,
or when called on a translator that does not have a sum translator.
-}
renameConstructors :: [String] -> Translator -> Translator
renameConstructors names (Translator w (TStyled s t)) = Translator w $ TStyled s $ renameConstructors names t
renameConstructors names (Translator w (TDuplicate n t)) = Translator w $ TDuplicate n $ renameConstructors names t
renameConstructors names (Translator w (TSum subs)) =
  Translator w
    $ TSum
    $ erroringZipWith
      ("Incorrect number of constructors:" <> show names)
      renameConstructor
      names
      subs
 where
  renameConstructor :: String -> Translator -> Translator
  renameConstructor name (Translator w' (TStyled s t)) = Translator w' $ TStyled s $ renameConstructor name t
  renameConstructor name (Translator w' (TDuplicate _n t)) = Translator w' $ TDuplicate name t
  renameConstructor _ t = t
renameConstructors _ _ = error "renameFields called on translator without explicit constructors"

{- | Wrap constructors with a single field in the @WSInherit 0@ style.
Ignores any structures that are wrapped in a TStyled translator.
-}
inheritSingleFieldStyle :: Translator -> Translator
inheritSingleFieldStyle t@(Translator _ (TStyled _ _)) = t
inheritSingleFieldStyle (Translator w (TDuplicate n t)) = Translator w $ TDuplicate n $ inheritSingleFieldStyle t
inheritSingleFieldStyle (Translator w (TSum ts)) = Translator w $ TSum $ L.map inheritSingleFieldStyle ts
inheritSingleFieldStyle t@(Translator _ TProduct{subs}) = if L.length subs == 1 then tStyled (WSInherit 0) t else t
inheritSingleFieldStyle t = t -- TODO: continue on AS,AP,P,Ar,CB

{- | Apply constructor styles. This wraps 'TProduct' translators and modifies the style of 'TConst' translators.
Does nothing if the list of styles is empty.
Otherwise, errors if the number of styles does not match the number of constructors.
-}
withConstructorStyles :: [WaveStyle] -> Translator -> Translator
withConstructorStyles [] t = t
withConstructorStyles sty (Translator w (TDuplicate n t)) = Translator w $ TDuplicate n $ withConstructorStyles sty t
withConstructorStyles sty (Translator w (TSum ts)) =
  Translator w
    $ TSum
    $ erroringZipWith
      "withConstructorStyles called with incorrect number of styles"
      (\s t -> withConstructorStyles [s] t)
      sty
      ts
withConstructorStyles [WSDefault] t = t
withConstructorStyles [s] (Translator w (TStyled _ t)) = Translator w $ TStyled s t
withConstructorStyles [s] (Translator w (TConst (Translation r ss))) = Translator w $ TConst $ Translation r' ss
 where
  r' = (\(v, _, p) -> (v, s, p)) <$> r
withConstructorStyles [s] t = tStyled s t
withConstructorStyles _ t =
  error
    $ "withConstructorStyles called with incorrect number of styles for translator "
    <> show t

------------------------------------------- GENERIC -------------------------------------

{- | A class for obtaining the required behaviour of 'Waveform' through "GHC.Generics".
The exact details might change later; use at your own risk.
-}
class WaveformG a where
  {- | Given a bitsize and list of styles for the constructors, provide a translator.

  Defined only for full types and constructors
  -}
  translatorG :: Int -> Translator

  {- | Return a list of translators for constructors as subsignals.

  Defined for constructors, @:+:@ and types with multiple constructors.
  -}
  constrTranslatorsG :: [Translator]

  {- | Return a list of translators for fields.

  Defined for fields, @:*:@, constructors, and types with a single constructor.
  Product type subsignals are labeled (numbered) for types and constructors only.
  -}
  fieldTranslatorsG :: [(SubSignal, Translator)]

  {- | Bitsize of a type. Only used to determine the width of constructors
  (and their fields).

  Defined for constructors, @:*:@, and fields.
  -}
  widthG :: Int -- for individual constructors

  {- | For LUTs.
  Create translation subsignals from supplied 'Render' value.
  Duplicate the value if there are multiple constructors, and just translate the fields.
  If getting the constructor fails, create no subsignals.

  Defined for types, @:+:@ and constructors.
  -}
  translateWithG :: Render -> a -> [(SubSignal, Translation)]

  {- | For LUTs.
  Translate all fields of a (the) constructor.

  Defined for constructors, @:*:@, fields and types with 1 constructor.
  -}
  translateFieldsG :: a -> [(SubSignal, Translation)]

-- void type (assuming it has a custom bitpack implementation)
instance WaveformG (D1 m1 V1 k) where
  translatorG _ = tConst Nothing
  constrTranslatorsG = undefined
  fieldTranslatorsG = undefined

  widthG = undefined

  translateWithG _ _ = []
  translateFieldsG = undefined

-- single constructor type
instance (WaveformG (C1 m2 s k), WaveformG (s k)) => WaveformG (D1 m1 (C1 m2 s) k) where
  translatorG = translatorG @(C1 m2 s k)
  constrTranslatorsG = undefined
  fieldTranslatorsG = fieldTranslatorsG @(C1 m2 s k)

  widthG = undefined

  translateWithG r x = case translateWithG r (unM1 x) of
    [(_, Translation _ subs)] -> subs -- remove duplicated singal from constructor
    _ -> undefined
  translateFieldsG x = translateFieldsG (unM1 x)

-- multiple constructors type
instance (WaveformG ((a :+: b) k)) => WaveformG (D1 m1 (a :+: b) k) where
  translatorG w = Translator w . TSum $ constrTranslatorsG @((a :+: b) k)
  constrTranslatorsG = constrTranslatorsG @((a :+: b) k)
  fieldTranslatorsG = undefined

  widthG = undefined

  translateWithG r x = fromMaybe [] $ safeWHNF $ translateWithG r (unM1 x)
  translateFieldsG = undefined

-- multiple constructors
instance (WaveformG (a k), WaveformG (b k)) => WaveformG ((a :+: b) k) where
  translatorG = undefined
  constrTranslatorsG = a <> b
   where
    a = constrTranslatorsG @(a k)
    b = constrTranslatorsG @(b k)
  fieldTranslatorsG = undefined

  widthG = undefined

  translateWithG r xy = case safeWHNF xy of
    Just (L1 x) -> translateWithG r x
    Just (R1 y) -> translateWithG r y
    Nothing -> []
  translateFieldsG = undefined

-- struct constructor
instance
  (WaveformG (fields k), KnownSymbol name) =>
  WaveformG (C1 (MetaCons name fix True) fields k)
  where
  translatorG _ = t
   where
    subs = fieldTranslatorsG @(C1 (MetaCons name fix True) fields k)
    t =
      Translator (widthG @(fields k))
        $ TProduct
          { start = sym @name <> "{"
          , sep = ", "
          , stop = "}"
          , preci = -1
          , preco = 11
          , labels = L.map ((<> " = ") . fst) subs
          , subs = subs
          }

  constrTranslatorsG =
    [ tDup (sym @name)
        $ translatorG @(C1 (MetaCons name fix True) fields k) undefined
    ]
  fieldTranslatorsG = fieldTranslatorsG @(fields k)

  widthG = undefined

  translateWithG r x = [(sym @name, Translation r $ translateFieldsG x)]
  translateFieldsG x = translateFieldsG (unM1 x)

-- applicative product
instance
  (WaveformG (fields k), KnownSymbol name, PrecF fix) =>
  WaveformG (C1 (MetaCons name fix False) fields k)
  where
  translatorG _ = t
   where
    subs = fieldTranslatorsG @(C1 (MetaCons name fix False) fields k)
    t =
      if isOperator
        then
          Translator (widthG @(fields k))
            $ TProduct
              { start = ""
              , sep = " " <> sym @name <> " "
              , stop = ""
              , preci = precF @fix
              , preco = precF @fix
              , labels = []
              , subs = subs
              }
        else
          Translator (widthG @(fields k))
            $ TProduct
              { start = case subs of
                  [] -> sname
                  _ -> sname <> " "
              , sep = " "
              , stop = ""
              , preci = 10
              , preco = case subs of
                  [] -> 11
                  _ -> 10
              , labels = []
              , subs = subs
              }

    sname = safeName (sym @name)
    isOperator = not (isAlpha $ fromMaybe '_' $ listToMaybe $ sym @name) && (L.length subs == 2)

  constrTranslatorsG =
    [ tDup (sym @name)
        $ translatorG @(C1 (MetaCons name fix False) fields k) undefined
    ]
  fieldTranslatorsG = enumLabel $ fieldTranslatorsG @(fields k)

  widthG = undefined

  translateWithG r x = [(sym @name, Translation r $ translateFieldsG x)]
  translateFieldsG x = enumLabel $ translateFieldsG (unM1 x)

-- no fields
instance WaveformG (U1 k) where
  translatorG = undefined
  constrTranslatorsG = undefined
  fieldTranslatorsG = []

  widthG = 0

  translateWithG _ _ = undefined
  translateFieldsG _ = []

-- | Lazily get left field.
left :: (a :*: b) k -> a k
left (x :*: _y) = x

-- | Lazily get right field.
right :: (a :*: b) k -> b k
right (_x :*: y) = y

-- multiple fields
instance (WaveformG (a k), WaveformG (b k)) => WaveformG ((a :*: b) k) where
  translatorG = undefined
  constrTranslatorsG = undefined
  fieldTranslatorsG = fieldTranslatorsG @(a k) <> fieldTranslatorsG @(b k)

  widthG = widthG @(a k) + widthG @(b k)

  translateWithG _ _ = undefined
  translateFieldsG xy = translateFieldsG (left xy) <> translateFieldsG (right xy)

-- struct field
instance
  (Waveform t, KnownSymbol name) =>
  WaveformG (S1 (MetaSel (Just name) p q r) (Rec0 t) k)
  where
  translatorG = undefined
  constrTranslatorsG = undefined
  fieldTranslatorsG = [(sym @name, tRef @t)]

  widthG = bitSize @t

  translateWithG _ _ = undefined
  translateFieldsG x = [(sym @name, translate $ unK1 $ unM1 x)]

-- unnamed field
instance (Waveform t) => WaveformG (S1 (MetaSel Nothing p q r) (Rec0 t) k) where
  translatorG = undefined
  constrTranslatorsG = undefined
  fieldTranslatorsG = [("", tRef @t)]

  widthG = bitSize @t

  translateWithG _ _ = undefined
  translateFieldsG x = [("", translate $ unK1 $ unM1 x)]

------------------------------------------------ LUTS ------------------------------------

{- |
Class for easily defining custom translations for a type by using LUTs.
To use this class, a type must derive 'Waveform' via 'WaveformForLut'.

Bye default, the implementation uses 'GHC.Generics.Generic' for defining subsignals
and operator precedence, and 'Show' for displaying the value.
-}
class (Typeable a, BitPack a) => WaveformLUT a where
  -- | Provides the hierarchy of subsignals.
  structureL :: Structure
  default structureL :: (WaveformG (Rep a ())) => Structure
  structureL = structureT $ translatorG @(Rep a ()) 0

  {- | Translate a value. The translations must adhere to the structure defined in 'structureL'.
  This function must be robust to @undefined@ values!
  -}
  translateL :: a -> Translation
  default translateL ::
    (Generic a, Show a, WaveformG (Rep a ()), PrecG (Rep a ())) => a -> Translation
  translateL = translateWith renderShow splitL

  {- | A static lookup table.
  To use a static lookup table rather than one created from the values found during simulation,
  set this to a list of values and their translations. Set 'translateL' and 'structureL' to 'undefined'.
  -}
  staticL :: Maybe [(a, Translation)]
  staticL = Nothing

-- | Return the static LUT of a type with 'WaveformLUT'
staticLutL :: forall a. (WaveformLUT a) => Maybe LUT
staticLutL = staticLut <$> staticL @a

-- | Turn a list of (value,translation) pairs into a LUT
staticLut :: (BitPack a) => [(a, Translation)] -> LUT
staticLut = M.fromList . L.map (first BL.pack)

-- | Translate a value from a type with a static LUT
translateStaticL :: forall a. (Waveform a, WaveformLUT a) => a -> Translation
translateStaticL x = case staticLutL @a of
  Just lut -> case M.lookup (BL.pack x) lut of
    Just t -> t
    Nothing -> errorT "{value missing from LUT}"
  Nothing -> error "cannot translate type; it has no static LUT" -- TODO rewrite using maybe function instead of case

-- | Make sure a t'Translation' is fully defined. If not, return a t'Translation' with @"undefined"@.
safeTranslation :: Translation -> Translation
safeTranslation = safeNFOr (errorT "undefined")

{- | Given a function that renders a value, and a function that (given this 'Render')
prodices the subsignals, create a translation.
If rendering fails, @"undefined"@ is displayed. If creating the subsignals fails, no subsignals are shown.
-}
translateWith ::
  (a -> Render) -> (Render -> a -> [(SubSignal, Translation)]) -> a -> Translation
translateWith d s x = Translation ren subs
 where
  ren = safeNFOr (errorR "undefined") $ d x
  subs =
    safeNFOr []
      $ s ren x

-- | Display a value with 'Show', the default wave style, and operator precedence determined using 'Generic'.
renderShow :: (Show a, Generic a, PrecG (Rep a ())) => a -> Render
renderShow = renderWith show (const WSDefault) precL

-- | Display a value with the provided functions for creating the text value, style and operator precedence.
renderWith :: (a -> Value) -> (a -> WaveStyle) -> (a -> Prec) -> a -> Render
renderWith v s p x = Just (v x, s x, p x)

{- | Display an atomic value (such as a number) using the provided function to obtain the value.
(normal wavestyle, precedence 11).
-}
translateAtomWith :: (a -> Value) -> a -> Translation
translateAtomWith f = translateWith (renderWith f (const WSDefault) (const 11)) noSplit

-- | Display an atomic value (like a number) with 'Show'. See 'translateAtomWith'.
translateAtomShow :: (Show a) => a -> Translation
translateAtomShow = translateAtomWith show

{- | Render an atomic value representing a signed number.
If the render value is found to start with @-@, the precedence is set to 0.
-}
translateAtomSigWith :: (Show a) => (a -> Value) -> a -> Translation
translateAtomSigWith f = translateWith go noSplit
 where
  go x = Just (v, WSDefault, p)
   where
    v = f x
    p = case v of
      '-' : _ -> 0
      _ -> 11

{- | Render an atomic value representing a signed number using 'show'.
See 'translateAtomSigWith'.
-}
translateAtomSigShow :: (Show a) => a -> Translation
translateAtomSigShow = translateAtomSigWith show

{- | Create subsignals for the constructors and fields.
Constructor translations are a copy of the toplevel render value provided.
-}
splitL :: (Generic a, WaveformG (Rep a ())) => Render -> a -> [(SubSignal, Translation)]
splitL r x = translateWithG r (from @_ @() x)

-- | Create no subsignals for this type.
noSplit :: Render -> a -> [(SubSignal, Translation)]
noSplit _r _x = []

-- | Get the operator precedence of a value.
precL :: (PrecG (Rep a ()), Generic a) => a -> Prec
precL x = precG (from @_ @() x)

{- | Type for deriving 'Waveform' for types implementing 'WaveformLUT'.

@
type T = ... deriving (...)
deriving via WaveformForLut T instance Waveform T

isntance WaveformLUT T where
  ...
@
-}
newtype WaveformForLut a = WaveformForLut a deriving (Generic, BitPack, Typeable)

instance
  (Waveform a, WaveformLUT a, BitPack a, Typeable a) =>
  Waveform (WaveformForLut a)
  where
  typeName = defaultTypeName @a

  translator = tLut @a (staticLutL @a)

----------------------------------------------- PREC ----------------------------------

-- Stuff for figuring out the operator precedence of a type.

{- | Helper class for determining the precedence and number of fields of a
value's constructor.
-}
class (Generic a) => PrecG a where
  -- | Operator precedence of a value.
  precG :: a -> Prec

  {- | Return the number of fields of a constructor.
  This is needed to determine whether a constructor is atomic or not.
  -}
  nFields :: Integer
  nFields = undefined

-- get constructor(s)
instance (PrecG (c k)) => PrecG (D1 m1 c k) where
  precG M1{unM1 = x} = precG x

-- no constructors (void tpye)
instance PrecG (V1 k) where
  precG _ = 11

-- multiple constructors
instance (PrecG (a k), PrecG (b k)) => PrecG ((a :+: b) k) where
  precG (L1 x) = precG x
  precG (R1 y) = precG y

-- struct
instance
  (PrecG (fields k), PrecF fix) =>
  PrecG (C1 (MetaCons name fix True) fields k)
  where
  precG _ = 11

-- applicative
instance
  (PrecG (fields k), PrecF fix) =>
  PrecG (C1 (MetaCons name fix False) fields k)
  where
  precG _ = if nFields @(fields k) == 0 then 11 else precF @fix

-- count fields
instance PrecG (U1 k) where
  precG = undefined
  nFields = 0

instance (PrecG (a k), PrecG (b k)) => PrecG ((a :*: b) k) where
  precG = undefined
  nFields = nFields @(a k) + nFields @(b k)

instance PrecG (S1 (MetaSel n p q r) t k) where
  precG = undefined
  nFields = 1

-- | Class for obtaining the runtime precedence of a typelevel fixity value.
class PrecF (f :: FixityI) where
  -- | Return the precedence of a fixity value as an 'Integer'.
  precF :: Prec

instance PrecF PrefixI where
  precF = 10
instance (KnownNat p) => PrecF (InfixI a p) where
  precF = natVal (Proxy @p)

---------------------------------------- OTHER VARIANTS ----------------------------------

-- CONST

{- | Helper class for defining a constant translation value. To use this,
derive Waveform via WaveformForConst.
-}
class (BitPack a, Typeable a) => WaveformConst a where
  -- | The constant translation value. Overwrite this if the translation has subsignals.
  constTrans :: Translation
  constTrans = Translation (constRen @a) []

  -- | Constant render value. Overwrite this if the constant value has no subsignals.
  constRen :: Render
  constRen = undefined

  {-# MINIMAL constTrans | constRen #-}

-- | Helper class for deriving 'Waveform' for types implementing 'WaveformConst'.
newtype WaveformForConst a = WfConst a deriving (Generic, BitPack, Typeable)

instance
  (WaveformConst a, BitPack a, Typeable a) =>
  Waveform (WaveformForConst a)
  where
  typeName = defaultTypeName @a
  translator = Translator (bitSize @a) $ TConst $ constTrans @a

-- NUMBERS

{- | Helper class for deriving 'Waveform' for numerical types.
Options are provided at the type level (signed, format).

Example:
@
deriving via WaveformForNumber NFSig ('Just '(3,"_")) instance Waveform (Signed 3)
@
-}
newtype WaveformForNumber (f :: NumberFormat) (s :: Maybe NSPair) a
  = WaveformForNumber a
  deriving (Generic, BitPack, Typeable)

-- | Pair of a 'Nat' and 'Symbol', used for type-level spacer values.
type NSPair = (Nat, Symbol)

instance
  ( BitPack a
  , Typeable a
  , Typeable f
  , Typeable s
  , KnownNFormat f
  , KnownNSpacer s
  ) =>
  Waveform (WaveformForNumber (f :: NumberFormat) (s :: Maybe NSPair) a)
  where
  typeName = defaultTypeName @a
  translator =
    Translator (bitSize @(WaveformForNumber f s a))
      $ TNumber
        { format = formatVal (Proxy @f)
        , spacer = spacerVal (Proxy @s)
        , prefix = case formatVal (Proxy @f) of
            NFBin -> "0b"
            NFOct -> "0o"
            NFHex -> "0X"
            _ -> ""
        , warn = False
        }

-- | Default spacer for decimal values (@_@ every 3 digits)
type DecSpacer = 'Just '(3, "_")

-- | Default spacer for hexadecimal values (@_@ every 2 digits)
type HexSpacer = 'Just '(2, "_")

-- | Default spacer for octal values (@_@ every 4 digits)
type OctSpacer = 'Just '(4, "_")

-- | Default spacer for binary values (@_@ every 8 digits)
type BinSpacer = 'Just '(8, "_")

-- | Add @_@ every /n/ digits.
type SpacerEvery n = 'Just '(n, "_")

-- | Do not add spacers.
type NoSpacer = 'Nothing :: (Maybe NSPair)

-- | Class for turning a type level 'NumberFormat' into a runtime value.
class KnownNFormat (f :: NumberFormat) where
  formatVal :: forall proxy. proxy f -> NumberFormat

instance KnownNFormat NFSig where
  formatVal _ = NFSig
instance KnownNFormat NFUns where
  formatVal _ = NFUns
instance KnownNFormat NFHex where
  formatVal _ = NFHex
instance KnownNFormat NFOct where
  formatVal _ = NFOct
instance KnownNFormat NFBin where
  formatVal _ = NFBin

-- | Type to get the runtime value of a type-level number spacer.
class KnownNSpacer (f :: Maybe NSPair) where
  spacerVal :: proxy f -> Maybe (Integer, String)

instance KnownNSpacer 'Nothing where
  spacerVal _ = Nothing
instance (KnownNat n, KnownSymbol s) => KnownNSpacer ('Just '(n, s)) where
  spacerVal _ = Just (natVal (Proxy @n), sym @s)

--------------------------------------- IMPLEMENTATIONS ----------------------------------

instance WaveformConst () where
  constRen = defaultRender "()"
deriving via WaveformForConst () instance Waveform ()

-- | Configure styles through style variables @bool_false@ and @bool_true@.
instance Waveform Bool where
  translator =
    noConstructorSubsignals False
      $ withConstructorStyles ["$bool_false", "$bool_true"]
      $ defaultTranslator @Bool

-- | Configure styles through style variables @maybe_nothing@ and @maybe_just@.
instance (Waveform a) => Waveform (Maybe a) where
  translator =
    noConstructorSubsignals True
      $ withConstructorStyles ["$maybe_nothing", WSVar "maybe_just" $ WSInherit 0]
      $ defaultTranslator @(Maybe a)

-- | Configure styles through style variables @either_left@ and @either_right@.
instance (Waveform a, Waveform b) => Waveform (Either a b) where
  constructorStyles = ["$either_left", "$either_right"]

instance (BitPack Char) => WaveformLUT Char where
  structureL = Structure []
  translateL = translateAtomShow
deriving via WaveformForLut Char instance (BitPack Char) => Waveform Char

instance WaveformLUT Bit where
  staticL =
    Just
      [ (high, Translation (Just ("1", "$bit_high", 11)) [])
      , (low, Translation (Just ("0", "$bit_low", 11)) [])
      , (undefined, Translation (Just ("x", WSWarn, 11)) [])
      ]
  structureL = undefined -- Structure []
  translateL = undefined -- translateAtomShow
deriving via WaveformForLut Bit instance Waveform Bit

instance WaveformLUT Double where
  structureL = Structure []
  translateL = translateAtomSigShow
deriving via WaveformForLut Double instance Waveform Double

instance WaveformLUT Float where
  structureL = Structure []
  translateL = translateAtomSigShow
deriving via WaveformForLut Float instance Waveform Float

deriving via WaveformForNumber NFSig DecSpacer Int instance Waveform Int
deriving via WaveformForNumber NFSig DecSpacer Int8 instance Waveform Int8
deriving via WaveformForNumber NFSig DecSpacer Int16 instance Waveform Int16
deriving via WaveformForNumber NFSig DecSpacer Int32 instance Waveform Int32
deriving via WaveformForNumber NFSig DecSpacer Int64 instance Waveform Int64

instance Waveform Ordering

deriving via WaveformForNumber NFUns DecSpacer Word instance Waveform Word
deriving via WaveformForNumber NFUns DecSpacer Word8 instance Waveform Word8
deriving via WaveformForNumber NFUns DecSpacer Word16 instance Waveform Word16
deriving via WaveformForNumber NFUns DecSpacer Word32 instance Waveform Word32
deriving via WaveformForNumber NFUns DecSpacer Word64 instance Waveform Word64

deriving via
  WaveformForNumber NFSig DecSpacer (Signed n)
  instance
    (KnownNat n) => Waveform (Signed n)
deriving via
  WaveformForNumber NFUns DecSpacer (Unsigned n)
  instance
    (KnownNat n) => Waveform (Unsigned n)
deriving via
  WaveformForNumber NFUns DecSpacer (Index n)
  instance
    (1 <= n, KnownNat n) => Waveform (Index n)

instance (Waveform a) => Waveform (Complex a)

instance (Waveform a) => Waveform (Down a)

instance (Waveform a) => Waveform (Identity a)

-- number wrappers
instance (Waveform a) => Waveform (Zeroing a) where
  translator = tDup "zeroing" $ tRef @a

instance (Waveform a) => Waveform (Wrapping a) where
  translator = tDup "wrapping" $ tRef @a

instance (Waveform a) => Waveform (Saturating a) where
  translator = tDup "saturating" $ tRef @a

instance (Waveform a) => Waveform (Overflowing a) where
  translator = tDup "overflowing" $ tRef @a

instance (Waveform a) => Waveform (Erroring a) where
  translator = tDup "erroring" $ tRef @a

-- vectors
instance (KnownNat n, Waveform a) => Waveform (Vec n a) where
  translator =
    Translator (bitSize @(Vec n a))
      $ if natVal (Proxy @n) /= 0
        then
          TArray
            { start = ""
            , sep = " :> "
            , stop = " :> Nil"
            , preci = 5
            , preco = 5
            , len = fromIntegral $ natVal (Proxy @n)
            , sub = tRef @a
            }
        else
          TConst $ Translation (defaultRender "Nil") []

-- deriving via
--   WaveformForNumber NFBin BinSpacer (BitVector n)
--   instance
--     (KnownNat n) => Waveform (BitVector n)

instance (KnownNat n) => Waveform (BitVector n) where
  translator =
    Translator n
      $ TChangeBits (BPConcat [BPHasUndefined BPIn, BPIn])
      $ Translator (n + 1)
      $ TSum [t, tStyled WSWarn t]
   where
    t =
      Translator n
        $ TAdvancedProduct
          { sliceTrans = bits <> [((0, n), num)]
          , hierarchy = L.map (\i -> (show (n - 1 - i), i)) [0 .. n - 1]
          , valueParts = [VPRef n 0]
          , preco = 11
          }
    bits = L.map (\i -> ((i, i + 1), tRef @Bit)) [0 .. n - 1]
    num = Translator n $ TNumber NFBin (Just (8, "_")) "0b" True
    n = bitSize @(BitVector n)

-- fixed point
instance
  (BitPack (Fixed r i f), KnownNat i, KnownNat f, Show (Fixed r i f), Typeable r) =>
  WaveformLUT (Fixed r i f)
  where
  structureL = Structure []
  translateL = translateAtomSigShow
deriving via
  WaveformForLut (Fixed r i f)
  instance
    (BitPack (Fixed r i f), KnownNat i, KnownNat f, Show (Fixed r i f), Typeable r) =>
    Waveform (Fixed r i f)

-- snat
instance (KnownNat n, BitPack (SNat n)) => WaveformConst (SNat n) where
  constRen = defaultRender $ show $ natVal $ Proxy @n
deriving via
  WaveformForConst (SNat n)
  instance
    (KnownNat n, BitPack (SNat n)) => Waveform (SNat n)

-- RTree implementation

-- | Helper family for implementing 'Waveform' for 'RTree'.
type family RTreeIsLeaf d where
  RTreeIsLeaf 0 = True
  RTreeIsLeaf d = False

instance
  (Waveform a, KnownNat d, WaveformRTree (RTreeIsLeaf d) d a) =>
  Waveform (RTree d a)
  where
  translator = translatorRTree @(RTreeIsLeaf d) @d @a

-- | Helper class for implementing 'Waveform' for 'RTree'.
class WaveformRTree (isLeaf :: Bool) d a where
  translatorRTree :: Translator

instance (Waveform a) => WaveformRTree True 0 a where
  translatorRTree = tRef @a

instance
  (Waveform (RTree d1 a), Waveform a, d ~ d1 + 1, KnownNat d, KnownNat d1) =>
  WaveformRTree False d a
  where
  translatorRTree =
    Translator (bitSize @(RTree d a))
      $ TProduct
        { start = "<"
        , sep = ","
        , stop = ">"
        , labels = []
        , preci = -1
        , preco = 11
        , subs = [("left", tsub), ("right", tsub)]
        }
   where
    tsub = tRef @(RTree d1 a)

{- | A translator for displaying values with zero or more fields like tuples.
This function will error if called for a type that has more than one constructor!
-}
tupleTranslator :: forall t. (BitPack t, WaveformG (Rep t ())) => Translator
tupleTranslator =
  Translator (bitSize @t)
    $ TProduct
      { start = "("
      , sep = ","
      , stop = ")"
      , labels = []
      , preci = -1
      , preco = 11
      , subs = fieldTranslatorsG @(Rep t ())
      }

{- | __NB__: The documentation only shows instances up to /3/-tuples. By
default, instances up to and including /12/-tuples will exist. If the flag
@large-tuples@ is set instances up to the GHC imposed limit will exist. The
GHC imposed limit is either 62 or 64 depending on the GHC version.
-}
deriveWaveformTuples 2 MAX_TUPLE_SIZE