-- | A class used while converting Cabal dependencies into Debian
-- dependencies.

{-# LANGUAGE FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, StandaloneDeriving, TypeSynonymInstances #-}
{-# OPTIONS_GHC -Wall -Werror #-}
module Debian.Debianize.Interspersed
    ( Interspersed(..)
    ) where

import Debug.Trace (trace)

-- | A class of Bs insterspersed with Cs.  It is used when converting
-- the cabal dependencies to debian, where the "around" type is the
-- binary package name and the "between" type is the version number.
-- 
-- Minimum implementation is a method to return the leftmost B, and
-- another to return the following (C,B) pairs.  Its unfortunate to
-- require lists in the implementation, a fold function would be
-- better (though I find implementing such folds to be a pain in the
-- you-know-what.)
-- 
-- The class provides implementations of three folds, each of which
-- exposes slightly different views of the data.
class Interspersed t around between | t -> around, t -> between where
    leftmost :: t -> around
    pairs :: t -> [(between, around)]

    foldTriples :: (around -> between -> around -> r -> r) -> r -> t -> r
    foldTriples f r0 x = snd $ foldl (\ (b1, r) (c, b2) -> (b1, f b1 c b2 r)) (leftmost x, r0) (pairs x)

    -- Treat the b's as the centers and the c's as the things to their
    -- left and right.  Use Maybe to make up for the missing c's at the
    -- ends.
    foldInverted :: (Maybe between -> around -> Maybe between -> r -> r) -> r -> t -> r
    foldInverted f r0 x =
        (\ (bn, an, r) -> f bn an Nothing r) $
           foldl g (Nothing, leftmost x, r0) (pairs x)
        where
          g (b1, a1, r) (b2, a2) = (Just b2, a2, f b1 a1 (Just b2) r)

    foldArounds :: (around -> around -> r -> r) -> r -> t -> r
    foldArounds f r0 x = snd $ foldl (\ (a1, r) (_, a2) -> (a2, f a1 a2 r)) (leftmost x, r0) (pairs x)

    foldBetweens :: (between -> r -> r) -> r -> t -> r
    foldBetweens f r0 x = foldl (\ r (b, _) -> (f b r)) r0 (pairs x)

-- | An example
data Splits = Splits Double [(String, Double)] deriving Show

instance Interspersed Splits Double String where
    leftmost (Splits x _) = x
    pairs (Splits _ x) = x

_splits :: Splits
_splits = Splits 1.0 [("between 1 and 2", 2.0), ("between 2 and 3", 3.0)]

_test1 :: ()
_test1 = foldTriples (\ l s r () -> trace ("l=" ++ show l ++ " s=" ++ show s ++ " r=" ++ show r) ()) () _splits

_test2 :: ()
_test2 = foldInverted (\ sl f sr () -> trace ("sl=" ++ show sl ++ " f=" ++ show f ++ " sr=" ++ show sr) ()) () _splits