-- GenI surface realiser
-- Copyright (C) 2005 Carlos Areces and Eric Kow
--
-- This program is free software; you can redistribute it and/or
-- modify it under the terms of the GNU General Public License
-- as published by the Free Software Foundation; either version 2
-- of the License, or (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program; if not, write to the Free Software
-- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings     #-}
{-# LANGUAGE TypeSynonymInstances  #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module NLP.GenI.GraphvizShowPolarity
where

import           Data.List                         (intercalate)
import qualified Data.Map                          as Map
import           Data.Maybe                        (catMaybes)
import qualified Data.Text.Lazy                    as TL

import           Data.GraphViz
import           Data.GraphViz.Attributes.Complete

import           NLP.GenI.General                  (showInterval)
import           NLP.GenI.Graphviz                 (GraphvizShow (..),
                                                    gvUnlines)
import           NLP.GenI.Polarity                 (NFA (states, transitions),
                                                    PolAut, PolState (PolSt),
                                                    finalSt)
import           NLP.GenI.Pretty
import           NLP.GenI.Tag                      (idname)

instance GraphvizShow PolAut where
    -- we want a directed graph (arrows)
    graphvizShowGraph aut =
        DotGraph False True Nothing $ DotStmts
            atts
            (graphvizShowAsSubgraph "aut" aut)
            [] -- all nodes are in the subgraph
            []
     where
       atts =
            [ GraphAttrs [RankDir FromLeft, RankSep [0.02], Pack (PackMargin 1)]
            , NodeAttrs [FontSize 10]
            , EdgeAttrs [FontSize 10]
            ]

    graphvizShowAsSubgraph prefix aut =
        [ DotSG False Nothing
            $ DotStmts [ NodeAttrs [ Shape Ellipse, Peripheries 1 ] ]
                       []
                       (zipWith (gvShowState fin) ids st)
                       (concat $ zipWith (gvShowTrans aut stmap) ids st)
        ]
      where
        st  = (concat.states) aut
        fin = finalSt aut
        ids = map (\x -> prefix <> TL.pack (show x)) ([0..] :: [Int])
        -- map which permits us to assign an id to a state
        stmap = Map.fromList $ zip st ids

gvShowState :: [PolState] -> TL.Text -> PolState -> DotNode TL.Text
gvShowState fin stId st =
    DotNode stId $ decorate [ Label . StrLabel . showSt $ st ]
  where
    showSt (PolSt _ ex po) = gvUnlines . catMaybes $
        [ Nothing -- Just (snd3 pr)
        , if null ex then Nothing else Just (TL.fromChunks [pretty ex])
        , Just . TL.pack . intercalate "," $ map showInterval po
        ]
    decorate =
        if st `elem` fin then (Peripheries 2 :) else id

gvShowTrans :: PolAut -> Map.Map PolState TL.Text
               -> TL.Text -> PolState -> [DotEdge TL.Text]
gvShowTrans aut stmap idFrom st =
    map drawTrans (Map.toList trans)
  where
    -- outgoing transition labels from st
    trans = Map.findWithDefault Map.empty st $ transitions aut
    -- returns the graphviz dot command to draw a labeled transition
    drawTrans (stTo,x) =
        case Map.lookup stTo stmap of
            Nothing   -> drawTrans' ("id_error_" `TL.append` (TL.pack (sem_ stTo))) x
            Just idTo -> drawTrans' idTo x
      where
        sem_ (PolSt i _ _) = show i
        --showSem (PolSt (_,pred,_) _ _) = pred
    drawTrans' idTo x = DotEdge idFrom idTo [Label (drawLabel x)]
    drawLabel labels  =
        StrLabel . gvUnlines $ labs
      where
        lablen  = length labels
        maxlabs = 6
        excess = TL.pack $ "...and " ++ show (lablen - maxlabs) ++ " more"
        --
        name t  = TL.fromChunks [ idname t ]
        labstrs = map (maybe "EMPTY" name) labels
        labs = if lablen > maxlabs
               then take maxlabs labstrs ++ [ excess ]
               else labstrs