-- Summarize CPSA output as a formula in coherent logic

-- Copyright (c) 2011 The MITRE Corporation
--
-- This program is free software: you can redistribute it and/or
-- modify it under the terms of the BSD License as published by the
-- University of California.

module Main (main) where

import System.IO
import CPSA.Lib.SExpr
import CPSA.Lib.Algebra
import CPSA.Lib.Entry
import CPSA.SAS.SAS
import qualified CPSA.Basic.Algebra
import qualified CPSA.DiffieHellman.Algebra

-- Algebra names
algs :: [String]
algs = [CPSA.Basic.Algebra.name, CPSA.DiffieHellman.Algebra.name]

main :: IO ()
main =
    do
      let options = algOptions CPSA.Basic.Algebra.name
      let interp = algInterp CPSA.Basic.Algebra.name algs
      (p, (output, alg, margin)) <- start options interp
      h <- outputHandle output
      -- Handle the herald
      x <- readSExpr p
      case x of
        Nothing -> abort "Empty input"
        Just (L _ (S _ "herald" : _ : xs)) ->
             selAlg p h (getName xs alg) margin Nothing
        Just x -> selAlg p h alg margin (Just x)

-- Select algebra and then continue with next
selAlg :: PosHandle -> Handle -> String -> Int -> Maybe (SExpr Pos) -> IO ()
selAlg p h alg margin sexpr =
    case () of
      _ | alg == CPSA.Basic.Algebra.name ->
           next p h margin sexpr
                    (step h alg CPSA.Basic.Algebra.origin margin)
        | alg == CPSA.DiffieHellman.Algebra.name ->
           next p h margin sexpr
                    (step h alg CPSA.DiffieHellman.Algebra.origin margin)
          | otherwise ->
               abort ("Bad algebra: " ++ alg)

-- Continue and ignore header if one was found
next :: PosHandle -> Handle -> Int ->
        Maybe (SExpr Pos) -> (State t g c -> Maybe (SExpr Pos)
                              -> IO (State t g c)) -> IO ()
next p h margin Nothing f = -- Found header
    do
      writeComment h margin cpsaVersion
      writeComment h margin "Coherent logic"
      go f p ([], [])
      hClose h
next p h margin sexpr f =   -- No header found
    do
      writeComment h margin cpsaVersion
      writeComment h margin "Coherent logic"
      st <- f ([], []) sexpr -- Process first S-expression.
      go f p st
      hClose h

go :: (a -> Maybe (SExpr Pos) -> IO a) -> PosHandle -> a -> IO ()
go f p a =
    loop a
    where
      loop a =
          do
            x <- readSExpr p
            case x of
              Nothing ->
                  do
                    _ <- f a x
                    return ()
              Just _ ->
                  do
                    a <- f a x
                    loop a

step :: Algebra t p g s e c => Handle -> String ->
        g -> Int -> State t g c ->
        Maybe (SExpr Pos) -> IO (State t g c)
step output _ _ margin state
         (Just sexpr@(L _ (S _ "comment" : _))) =
         do
           writeLnSExpr output margin sexpr
           return state
step output name origin margin state sexpr =
    do
      x <- tryIO (sas name origin state sexpr)
      case x of
        Right (acc, Nothing) ->
            after output margin acc sexpr
        Right (acc, Just x) ->
            do
              writeLnSExpr output margin x
              after output margin acc sexpr
        Left err ->
            abort (show err)

after :: Handle -> Int -> State t g c ->
         Maybe (SExpr Pos) -> IO (State t g c)
after output margin state (Just sexpr@(L _ (S _ "defprotocol" : _))) =
    do
      writeLnSExpr output margin sexpr
      return state
after _ _ state _ =
    return state

getName :: [SExpr a] -> String -> String
getName xs name =
    case assoc "algebra" xs of
      [S _ nom] -> nom
      _ -> name

-- Lookup value in alist, appending values with the same key
assoc :: String -> [SExpr a] -> [SExpr a]
assoc key alist =
    concat [ rest | L _ (S _ head : rest) <- alist, key == head ]