module Camfort.Specification.Units.Debug where
import qualified Data.Vector as V
import qualified Debug.Trace as D
import Data.Data
import Data.Char
import Data.Maybe
import Data.Function
import Data.Matrix
import Data.List
import Data.Matrix
import Data.Ratio
import Data.Generics.Uniplate.Operations
import Data.Label.Monadic hiding (modify)
import Control.Monad.State.Strict hiding (gets)
import Control.Monad
import Camfort.Analysis.Annotations hiding (Unitless)
import Camfort.Specification.Units.Environment
import Camfort.Transformation.Syntax
import qualified Language.Fortran.AST as F
import qualified Language.Fortran.Util.Position as FU
fooMatrix :: Matrix Rational
fooMatrix = matrix 4 4 $ (\(i,j) -> if (i==j) then (toInteger i) % 1 else 0)
debugGaussian :: State UnitEnv String
debugGaussian = do grid' <- debugGaussian'
                   report <<++ ("Dump of units-of-measure system matrix\n" ++ grid')
                   return grid'
debugGaussian' = do ucats   <- gets unitVarCats
                    (matrix,rowv)  <- gets linearSystem
                    varenv  <- gets varColEnv
                    debugs  <- gets debugInfo
                    procenv <- gets procedureEnv
                    let 
                        grid = ["" : map show [1..(ncols matrix)], []]
                        
                            ++ map (\r -> (show r) : (map showRational $ V.toList $ getRow r matrix) ++ [show $ rowv !! (r  1)]) [1..(nrows matrix)]
                        
                            ++ [[], "" : map showCat ucats]
                        
                            ++ ["" : map (showExpr ucats varenv procenv debugs) [1.. (ncols matrix)]]
                        
                            ++ ["" : map (showArgVars ucats varenv) [1..(ncols matrix)]]
                    let colSize = maximum' (map maximum' (map (notLast . (map length)) grid))
                    let expand r = r ++ (replicate (colSize  length r) ' ')
                    let showLine x = (concatMap expand x) ++ "\n"
                    let grid' = concatMap showLine grid
                    return grid'
   where maximum' [] = 0
         maximum' xs = maximum xs
         notLast xs = take (length xs  1) xs
showExpr cats vars procs debugInfo c =
             case (cats !! (c  1)) of
               Variable  -> case (lookupVarsByCols vars [c]) of
                              []    -> case (lookupProcByCols procs [c]) of
                                         []    -> "?"
                                         (x:_) -> "=" ++ x
                              (x:_) -> x
               Temporary -> snd $ case (lookup c debugInfo) of
                                    Just x -> x
                                    Nothing -> (undefined, "") 
               Argument  -> case (lookupProcByArgCol procs [c]) of
                              []    -> "?"
                              (x:_) -> x
               Literal _  -> snd $ case (lookup c debugInfo) of
                                    Just x -> x
                                    Nothing -> show c `D.trace` error "Literal fail"
               Magic     -> ""
lineCol :: FU.Position -> (Int, Int)
lineCol p  = (fromIntegral $ FU.posLine p, fromIntegral $ FU.posColumn p)
showSrcLoc loc = show (lineCol loc) ++ ":" ++ show (lineCol loc)
showSrcSpan (FU.SrcSpan l u) = "(" ++ showSrcLoc l ++ " - " ++ showSrcLoc u ++ ")"
showExprLines cats vars procs debugInfo c =
             case (cats !! (c  1)) of
               Variable  -> case (lookup c debugInfo) of
                              Just (sp, expr) -> (showSrcSpan sp) ++ "\t" ++ expr
                              Nothing ->
                                case (lookupVarsByCols vars [c]) of
                                  []    -> case (lookupProcByCols procs [c]) of
                                             []    -> "?"
                                             (x:_) -> "=" ++ x
                                  (x:_) -> x
               Temporary -> let (sp, expr) = fromJust $ lookup c debugInfo
                            in (showSrcSpan sp) ++ "\t" ++ expr
               Argument  -> case (lookupProcByArgCol procs [c]) of
                              []    -> "?"
                              (x:_) -> x
               Literal _ -> let (sp, expr) = fromJust $ lookup c debugInfo
                            in (showSrcSpan sp) ++ "\t" ++ expr
               Magic     -> ""
showArgVars cats vars c =
             case (cats !! (c  1)) of
               Argument -> case (lookupVarsByCols vars [c]) of
                             []    -> ""
                             (x:_) -> x
               _        -> ""
showCat Variable  = "Var"
showCat Magic     = "Magic"
showCat Temporary = "Temp"
showCat Argument  = "Arg"
showCat (Literal False) = "Lit"
showCat (Literal True)  = "Lit="
lookupProcByArgCol :: ProcedureEnv -> [Int] -> [String]
lookupProcByArgCol penv cols =
             mapMaybe (\j -> lookupEnv j penv) cols
                 where lookupEnv j [] = Nothing
                       lookupEnv j ((p, (_, args)):penv)
                           | elem (VarCol j) args  = Just (p ++ "#" ++ (show $ fromJust $ elemIndex (VarCol j) args))
                           | otherwise    = lookupEnv j penv
lookupProcByCols :: ProcedureEnv -> [Int] -> [String]
lookupProcByCols penv cols =
             mapMaybe (\j -> lookupEnv j penv) cols
                 where lookupEnv j [] = Nothing
                       lookupEnv j ((p, (Just (VarCol i), _)):penv)
                                    | i == j    = Just p
                                    | otherwise = lookupEnv j penv
                       lookupEnv j ((p, (Nothing, _)):penv) = lookupEnv j penv
lookupVarsByCols :: VarColEnv -> [Int] -> [F.Name]
lookupVarsByCols uenv cols = mapMaybe (\j -> lookupEnv j uenv) cols
                 where lookupEnv j [] = Nothing
                       lookupEnv j ((VarBinder (v, _), (VarCol i, _)):uenv)
                                    | i == j    = Just v
                                    | otherwise = lookupEnv j uenv
lookupVarBindersByCols :: VarColEnv -> [Int] -> [VarBinder]
lookupVarBindersByCols uenv cols = mapMaybe (\j -> lookupEnv j uenv) cols
                 where lookupEnv j [] = Nothing
                       lookupEnv j ((vb@(VarBinder (v, _)), (VarCol i, _)):uenv)
                                    | i == j    = Just vb
                                    | otherwise = lookupEnv j uenv
showRational r = show (numerator r) ++ if ((denominator r) == 1) then "" else "%" ++ (show $ denominator r)