-- Dieses Modul stellt Funktionen zur Verfügung die beim erstellen von Beispielen nützlich sind. 

module System.ArrowVHDL.Circuit.Tools
where


-- Eingebunden werden für die Demo-Funktionen aus den
-- Systembibliotheken die folgenden Module:

import System.IO (writeFile)
import System.Cmd (system)

-- Daneben wird noch der \begriff{except}-Operator (\hsSource{(\\)})
-- aus dem Listen-Modul benötigt:

import Data.List ((\\))


-- Schließlich werden eine ganze Reihe von Modulen aus der
-- \hsSource{Circuit}-Reihe verwendet:

import System.ArrowVHDL.Circuit.Descriptor
import System.ArrowVHDL.Circuit.Show
import System.ArrowVHDL.Circuit.Graphs
import System.ArrowVHDL.Circuit.Auxillary
import System.ArrowVHDL.Circuit.Show.DOT
import System.ArrowVHDL.Circuit.Workers (mergeEdges)
import System.ArrowVHDL.Circuit.Tests


-- \subsection{Demo Funktionen} Mit \hsSource{write} und
-- \hsSource{genPicture} werden zwei Funktionen definiert, die auf das
-- Dateisystem zugreifen, und letztliche dazu verwendet werden, eine
-- Grafik einer Schaltung zu erstellen. Dazu wird mittels
-- \hsSource{show} die \begriff{.dot}-Darstellung des übergebenen
-- Schaltkreises erzeugt und im \hsSource{/tmp}-Folder abgespeichert.

-- \hsSource{genPicture} greift dann auf die soeben erzeugte Datei zu,
-- und erzeugt aus dem \begriff{.dot}-File ein Bild mit der
-- Grafik. Hierbei wird vorausgesetzt, das auf dem System die
-- \begriff{graphviz}-Umgebung vorinstalliert ist.

write x    = writeFile "/tmp/test.dot" (Circuit.Show.DOT.showCircuit x)
genPicture = system "dot /tmp/test.dot -Tjpg -o /tmp/test.jpg"



-- Mit der Funktion \hsSource{toCircuit} lässt sich aus einer minimal
-- Definition ein gültiger Schaltkreis erzeugen. Die minimale
-- Definition muss dabei wenigstens einen Namen, sowie die Anzahl der
-- eingehenden und Ausgehenden Pins enthalten.

toCircuit :: (String, Int, Int) -> CircuitDescriptor
toCircuit (name, inPins, outPins)
    = emptyCircuit { nodeDesc = nodedesc
                   }
      where nodedesc = MkNode { label   = name 
                              , nodeId  = 0 
                              , sinks   = [0..(inPins -1)]
                              , sources = [0..(outPins -1)]
                              } 


-- Die Funktion \hsSource{filterByName} ist ein Beispiel für die
-- Mächtigkeit des neuartigen Ansatzes. \hsSource{filterByName} geht
-- hier über eine Schaltung hinweg, und nimmt alle Vorkommenden
-- Bausteinen mit einem gewissen Namen heraus. So lassen sich zum
-- Testen sehr leicht von den enthaltenen Schaltungen, gewisse selbige
-- durch eine andere Version ersetzen.

filterByName :: CircuitDescriptor -> String -> CircuitDescriptor
filterByName s n 
    = if ((label . nodeDesc) s == n) && (length (nodes s) < 1) 
        then NoDescriptor
        else s { nodes = (map (flip filterByName n) $ nodes s) }

 
-- Zur Anwendung wird \hsSource{filterByName} in der nächsten
-- Funktion, nämlich in \hsSource{replace} gebracht. Hier übergibt man
-- die Schaltung in der man ändern möchte. Außerdem übergibt man ein
-- Tupel bestehenden aus einem \hsSource{from}-Baustein und einem
-- \hsSource{to}-Baustein, wobei nach dem \hsSource{from}-Baustein
-- gesucht wird, und dieser dann mit dem \hsSource{to}-Baustein
-- ersetzt wird. Als Ergebnis erhält man eine veränderte Schaltung

replace :: CircuitDescriptor -> (CircuitDescriptor, CircuitDescriptor) -> CircuitDescriptor
replace s ft@(from, to) 
    | not $ isAtomic s
    = s { nodes = map (flip replace $ ft) (nodes s) }
    
    |  (label . nodeDesc) s           == (label . nodeDesc) from
    && length (sinks   . nodeDesc $ s) == length (sinks   . nodeDesc $ from)
    && length (sources . nodeDesc $ s) == length (sources . nodeDesc $ from)
    && length (sinks   . nodeDesc $ s) == length (sinks   . nodeDesc $ to)
    && length (sources . nodeDesc $ s) == length (sources . nodeDesc $ to)
    = to { nodeDesc = (nodeDesc to) { nodeId = (nodeId . nodeDesc) s } }
  
    | otherwise = s

-- %% TODO : Programmiere: mark / cut / trim



-- Die Funktion \hsSource{bypass} ermöglicht eine ähnlich
-- Funktionalität, wie auch schon \hsSource{filterByName} oder
-- \hsSource{replace}.  Allerdings nimmt \hsSource{bypass} nur die
-- gefundenen Bausteine aus der Schaltung heraus.
-- 
-- %%% TODO : Erklären was rebuildIf macht, und warum ;) 
-- %%% TODO : grep wires usw. ist teil von rebuildIf

bypass :: CircuitDescriptor -> CircuitDescriptor -> CircuitDescriptor
bypass s item 
    = s { nodes = ns
        , edges = es
        }
    where (es, ns) = foldl (rebuildIf (\x -> label (nodeDesc x) == label (nodeDesc item))) (edges s, []) $ nodes s


rebuildIf :: (CircuitDescriptor -> Bool) ->  ([Edge], [CircuitDescriptor]) -> CircuitDescriptor -> ([Edge], [CircuitDescriptor])
rebuildIf isIt (super_es, new_ns) NoDescriptor = (super_es, new_ns)
rebuildIf isIt (super_es, new_ns) n
    |  isIt n       && length (sinks . nodeDesc $ n) == length (sources . nodeDesc $ n)
    = (new_es , new_ns)
  
    | otherwise 
    = (super_es, new_ns ++ [n'])
  
    where new_es  = (super_es \\ (lws ++ rws)) ++ nws
          lws     = leftWires super_es (nodeId . nodeDesc $ n)
          rws     = rightWires super_es (nodeId . nodeDesc $ n)
          nws     = zipWith MkEdge (map sourceInfo lws) (map sinkInfo rws)
          (es,ns) = foldl (rebuildIf isIt) (edges n, []) $ nodes n
          n'      = n { nodes = ns
                      , edges = es
                      }


grepWires :: (Edge -> Anchor) -> [Edge] -> CompID -> [Edge]
grepWires f es cid = filter (\e -> fst (f e) == Just cid) es

leftWires :: [Edge] -> CompID -> [Edge]
leftWires = grepWires sinkInfo

rightWires :: [Edge] -> CompID -> [Edge]
rightWires = grepWires sourceInfo

solderWires :: ([Edge], [Edge]) -> [Edge]
solderWires = mergeEdges
