IMPLEMENTATION MODULE YaflController;

----------------------------------------------------

  IMPORT SYSTEM;                   
  IMPORT Comparable;

  FROM Streams     IMPORT StdOut;
  FROM Conversions IMPORT IntConversions;
  FROM YaflCfg     IMPORT YaflCfg;

----------------------------------------------------
  
  CLASS AtomicOperation;
    INHERITS Comparable;
    VAR   
      TheName  : ARRAY OF CHAR;
      TheCode,
      TheTimer : INTEGER;      
      
    REDEFINE METHOD CREATE (Code : INTEGER; Name : ARRAY OF CHAR);  
      BEGIN
      TheName := Name;
      TheCode := Code;
      END CREATE;       
                    
    REDEFINE METHOD IsGreater (Other : Comparable) : BOOLEAN;
      BEGIN         
      WHAT Other OF
        IN AtomicOperation:
          RESULT := Code > TAG.Code;
          END;
       ELSE
        END; -- WHAT      
      END IsGreater;  
      
    METHOD IncTimer (Value : INTEGER);
      BEGIN        
      TheTimer := TheTimer + Value;
      END IncTimer;  
                     
    METHOD Timer : INTEGER;
      BEGIN
      RESULT := TheTimer;            
      END Timer;      
      
    METHOD Name : ARRAY OF CHAR;
      BEGIN    
      RESULT := TheName;
      END Name;                         
      
    METHOD Code : INTEGER;
      BEGIN    
      RESULT := TheCode;
      END Code;  
      
    END AtomicOperation;
    
----------------------------------------------------

  CLASS Controller;
    VAR
      TheMessage       : ARRAY OF CHAR;
      TheOperations    : List (AtomicOperation);
                        
      TheStart,
      TheLastEvent     : Instant;
      TheLastEventCode : INTEGER;   
      TheLastEventName : ARRAY OF CHAR;
      
    REDEFINE METHOD CREATE;
      BEGIN        
      TheOperations.CREATE;
      
      TheStart.CREATE (Today.Instant, Now.Instant);
      
      TheLastEvent     := TheStart;
      TheLastEventCode := -1;
      END CREATE;
       
    METHOD Capture (Event : INTEGER);
      BEGIN                         
      IF YaflCfg.StatLevel > 1 THEN
        CASE Event OF 
          C:
            Register (C, "C compilation");
            END;  
          Yafl:
            Register (Yafl, "Yafl compilation");
            END;              
          LinkPrepare:
            Register (LinkPrepare, "Prepare link");
            END;  
          Link:
            Register (Link, "Link");
            END;  
          LinkDone:
            Register (LinkDone, "Link done");
            END;  
          Tag:
            Register (Tag, "Tag");
            END;
          CheckType:
            Register (CheckType, "Check type");
            END;    
          ExtendedChecks:
            Register (ExtendedChecks, "Extended checks");
            END;
          GenerateC:
            Register (GenerateC, "Generate code");
            END;    
          GenerateYam:
            Register (GenerateYam, "Generate Yam");
            END;    
          GenerateMetrics:
            Register (GenerateMetrics, "Generate metrics");
            END;    
          --ReadingDef:
          --  Register (ReadingDef, "Reading definition module");
          --  END;
          --ReadingImp:
          --  Register (ReadingImp, "Reading implementation module");
          --  END;
          --Entering:
          --  Register (Entering, "Entering module in dictionary");    
          --  END;
         ELSE
          --Register (Unrecognized, "Unrecognized");
          END; -- CASE
        END; -- IF
      END Capture;
                         
    METHOD Register (Code : INTEGER; Name : ARRAY OF CHAR);
      VAR         
        ThePos,
        TheLapTime   : INTEGER;
        TheOperation : AtomicOperation;
        TheInstant   : Instant;
      BEGIN                    
      IF YaflCfg.VerboseLevel > 2 THEN
        StdOut.WriteLine (Name);
        END; -- IF
                
      IF (TheLastEventCode > 0) THEN
        TheInstant.CREATE (Today.Instant, Now.Instant);
        TheLapTime := TheLastEvent.Interval (TheInstant);

        ThePos := -1;
        FOR TheCount := 0 TO Operations.Size -1 WHILE ThePos < 0 DO
          IF TheLastEventCode = Operations.Get (TheCount).Code THEN
            ThePos := TheCount;
            END; -- IF                              
          END; -- FOR

        IF ThePos < 0 THEN
          TheOperation.CREATE (TheLastEventCode, TheLastEventName);
          Operations.Append (TheOperation);
         ELSE
          TheOperation := Operations.Get (ThePos); 
          END; -- IF                                 

        TheOperation.IncTimer (TheLapTime);
        TheLastEvent := TheInstant;
        END; -- IF
          
      TheLastEventCode := Code;  
      TheLastEventName := Name;
      END Register;  
                     
    METHOD StartInstant : Instant;
      BEGIN            
      RESULT := TheStart;
      END StartInstant;

    METHOD Operations : List (AtomicOperation);
      BEGIN          
      RESULT := TheOperations;
      END Operations;

    METHOD Report;
      VAR
        TheLapTime   : INTEGER;           
        TheOperation : AtomicOperation;
        TheInstant   : Instant;
      BEGIN                    
      TheInstant.CREATE (Today.Instant, Now.Instant);
      TheLapTime := StartInstant.Interval (TheInstant);

      IF Operations.Size > 0 THEN                    
        Operations.Sort;

        StdOut.WriteLine ("--------------------------");
        StdOut.WriteLine ("Statistic report:");
        StdOut.WriteLine ("--------------------------");
        FOR TheCount := 0 TO Operations.Size -1 DO
          TheOperation := Operations.Get (TheCount);
          StdOut.WriteLine (                                        
                 IntConversions.IntToString (TheOperation.Code, 4) +
                 IntConversions.IntToString (SYSTEM.TRUNC (
                                SYSTEM.FLOAT (TheOperation.Timer) /
                                SYSTEM.FLOAT (TheLapTime) * 100.0), 3) +
                 "% " + TheOperation.Name);            
          END; -- FOR
        END; -- IF
      END Report;  
      
    METHOD SetMessage (Msg : ARRAY OF CHAR);
      BEGIN                      
      TheMessage := Msg;
      END SetMessage;   
      
    METHOD Message : ARRAY OF CHAR;
      BEGIN      
      RESULT := TheMessage;
      END Message;  
      
    END Controller;
    
----------------------------------------------------

  END YaflController;  
