--
-- Copyright (c) 2009-2011, ERICSSON AB
-- All rights reserved.
-- 
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
-- 
--     * Redistributions of source code must retain the above copyright notice, 
--       this list of conditions and the following disclaimer.
--     * Redistributions in binary form must reproduce the above copyright
--       notice, this list of conditions and the following disclaimer in the
--       documentation and/or other materials provided with the distribution.
--     * Neither the name of the ERICSSON AB nor the names of its contributors
--       may be used to endorse or promote products derived from this software
--       without specific prior written permission.
-- 
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
-- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--

{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE OverlappingInstances #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}

module Feldspar.Transformation.Framework where

import Feldspar.Compiler.Error

transformationError :: String -> a
transformationError = handleError "PluginArch/TransformationFramework" InternalError

-- ===========
-- == Utils ==
-- ===========

class Default t where
    def :: t
    def = transformationError "Default value requested."

class Combine t where
    combine :: t -> t -> t
    combine = transformationError "Default combination function used."

instance Default () where
    def = ()

instance Default [a] where
    def = []

instance Default Int where
    def = 0

instance (Default a, Default b) => Default (a,b) where
    def = (def, def)

instance (Default a, Default b, Default c) => Default (a,b,c) where
    def = (def, def, def)

instance Combine () where
    combine _ _ = ()

instance Combine String where
    combine s1 s2 = s1 ++ s2

instance Combine Int where
    combine i1 i2 = i1 + i2

instance (Combine a, Combine b)
    => Combine (a,b) where
        combine (x,y) (v,w) = (combine x v, combine y w)

-- =============================
-- == TransformationFramework ==
-- =============================

class (Default (Up t), Combine (Up t))
    => Transformation t where
        type From t
        type To t

        type State t
        type Down t
        type Up t

data Result t s
        = Result
        { result    :: s (To t)
        , state     :: State t
        , up        :: Up t
        }

deriving instance (Transformation t, Show (s (To t)), Show (State t), Show (Up t)) => Show (Result t s)

data Result1 t s a
        = Result1
        { result1   :: s (a (To t))
        , state1    :: State t
        , up1       :: Up t
        }

deriving instance (Transformation t, Show (s (b (To t))), Show (State t), Show (Up t)) => Show (Result1 t s b)

-- The following classes used to have `Transformation t` as super-class, but
-- this resulted in looping dictionaries (at run time) after switching to
-- GHC-7.4. This may or may not be related to the following (unconfirmed) bug:
--
--   http://hackage.haskell.org/trac/ghc/ticket/5913
--
-- The constraint `Transformation t` has currently been moved to the relevant
-- instances.

class Transformable t s where
        transform :: t -> State t -> Down t -> s (From t) -> Result t s

class Transformable1 t s a where
        transform1 :: t -> State t -> Down t -> s (a (From t)) -> Result1 t s a

class DefaultTransformable t s where
        defaultTransform :: t -> State t -> Down t -> s (From t) -> Result t s

class DefaultTransformable1 t s a where
        defaultTransform1 :: t -> State t -> Down t -> s (a (From t)) -> Result1 t s a

instance (DefaultTransformable t s)
    => Transformable t s where
        transform = defaultTransform

instance (DefaultTransformable1 t s a)
    => Transformable1 t s a where
        transform1 = defaultTransform1