 (* Copyright (C) 2012, SRI International.  All Rights Reserved. *)

 (* 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. *)


open Printf;;

let inputFile = String.concat "" [Sys.argv.(1); Sys.argv.(2); "_subsystems"]


let listOfLines = ref []
let listOfUnions = ref []
let mergedList = ref []
let stableUnions = ref []
let restOfUnions = ref []
let newRestOfUnions = ref []
let listOfInterfaceVariables = ref []
let listOfAnnotatedVariables = ref []
let listOfConstantVariables = ref []
let listOfPotentialVariablesForAnnotation = ref []
let listOfSubsystemsRequiresAnnotations = ref []

let count = ref 0
let numberOfRequiredAnnotations = ref 0
let totalNumberOfRequiredAnnotations = ref 0

let updateFlag = ref true
let existFlag = ref false
let annotationFlag = ref false
let constantDimensionFlag = ref false
let subsystemRequiresAnnotationFlag = ref false

let rec findClasses startCount =
begin
  count := 0; 
  stableUnions := [];
  while !count < startCount do  
     stableUnions := List.append !stableUnions [(List.nth !listOfUnions !count)]; 
     count := !count + 1 
  done;

  count := startCount + 1; 
  restOfUnions := [];
  while !count < (List.length !listOfUnions)  do  
     restOfUnions := List.append !restOfUnions [(List.nth !listOfUnions !count)]; 
     count := !count + 1 
  done;

  updateFlag := true;
  while !updateFlag == true && (List.length !listOfUnions) > 1 do
    updateFlag := false;
    mergedList := (List.nth !listOfUnions startCount);

    List.iter (
      fun element ->
        newRestOfUnions := [];
        List.iter (
          fun list_element ->
            existFlag := false;
            List.iter (
              fun l ->
                if (String.compare l element) == 0 then
                  existFlag := true;
            ) list_element;
             if !existFlag == true then
              begin
                List.iter (
                  fun l1 -> 
                    existFlag := false;
                    List.iter (
                      fun l2 ->
                        if (String.compare l1 l2) == 0 then
                        begin
                          existFlag := true;
                          updateFlag := true
                        end
                    ) !mergedList;
                    if !existFlag == false then
                      mergedList := List.append !mergedList [l1];
                ) list_element;
              end
              else
                newRestOfUnions := List.append !newRestOfUnions [list_element]
        ) !restOfUnions;
        restOfUnions := !newRestOfUnions;
    ) (List.nth !listOfUnions startCount);
    listOfUnions := List.append !stableUnions [!mergedList];
    listOfUnions := List.append !listOfUnions !restOfUnions;
  done;
  if startCount + 1 < List.length !listOfUnions then
    findClasses (startCount + 1);
end;;


let main () =
begin
  let ocw = open_out (String.concat "" [Sys.argv.(1); "results"])  in 
  let oc = open_in inputFile in
    try
      while true do
        subsystemRequiresAnnotationFlag := false;
        let subsystemName = input_line oc in 
          listOfLines := [];
          let inputFile1 = String.concat "" [Sys.argv.(1); subsystemName; "_equ_constraints"] in
            let oc1 = open_in inputFile1 in
              try
                while true do
                  let line = input_line oc1 in
                    if (String.compare line "") != 0 then
                      listOfLines := List.append !listOfLines [line];		
                done;
              with End_of_file ->
                close_in oc1;
          
          listOfInterfaceVariables := [];
          let inputFile2 = String.concat "" [Sys.argv.(1); subsystemName; "_interface_vars"] in
            let oc2 = open_in inputFile2 in
              try 
                while true do
                  let line = input_line oc2 in
                    if (String.compare line "") != 0 then
                      listOfInterfaceVariables := List.append !listOfInterfaceVariables [line];
                done;
              with End_of_file ->
                close_in oc2;

          listOfAnnotatedVariables := [];
          let inputFile3 = String.concat "" [Sys.argv.(1); subsystemName; "_provided_dims"] in
            let oc3 = open_in inputFile3 in
              try 
                while true do
                  let line = input_line oc3 in
                    if (String.compare line "") != 0 then
                      listOfAnnotatedVariables := List.append !listOfAnnotatedVariables [line];
                done;
              with End_of_file ->
                close_in oc3; 
        
        listOfConstantVariables := [];
          let inputFile4 = String.concat "" [Sys.argv.(1); subsystemName; "_constant_dims"] in
            let oc4 = open_in inputFile4 in
              try
                while true do
                  let line = input_line oc4 in
                    if (String.compare line "") != 0 then
                      listOfConstantVariables := List.append !listOfConstantVariables [line];
                done;
              with End_of_file ->
                close_in oc4;

        listOfUnions := [];
        List.iter(
          fun line ->
            let lineComponents = Str.split (Str.regexp "[ \t]+") line in 
              let newUnion = [(List.nth lineComponents 1) ; (Str.global_replace (Str.regexp ")") "" (List.nth lineComponents 2))] in
                listOfUnions := List.append !listOfUnions [newUnion];
        ) !listOfLines;

        findClasses 0; 

        fprintf ocw "\n\n******** %s ********\n\n" subsystemName;
        numberOfRequiredAnnotations := 0;
        count := 1;
        List.iter (
          fun ll ->
            fprintf ocw "\nUnion %d\n" !count;
            count := !count + 1; 
            annotationFlag := false;
            constantDimensionFlag := false;
            List.iter (
              fun l ->
                fprintf ocw "%s" l;
	        let flag = List.exists (fun x -> ((String.compare x l) == 0)) !listOfAnnotatedVariables in
                  if flag == true then
                  begin
                    annotationFlag := true;
                    fprintf ocw "   annotated";
                  end
                  else
                  begin
	            let flag = List.exists (fun x -> ((String.compare x l) == 0)) !listOfConstantVariables in
                       if flag == true then
                       begin
                         constantDimensionFlag := true;
                         fprintf ocw "   constant";
                       end
                  end;
                  fprintf ocw "\n";
            ) ll;

            listOfPotentialVariablesForAnnotation := [];
            if !annotationFlag == false && !constantDimensionFlag == false then
            begin
              List.iter (
                fun l ->
	          let flag = List.exists (fun x -> ((String.compare x l) == 0)) !listOfInterfaceVariables in
                    if flag == true then
                    begin
                      listOfPotentialVariablesForAnnotation := List.append !listOfPotentialVariablesForAnnotation [l];
                      annotationFlag := true;
                      subsystemRequiresAnnotationFlag := true;
                    end
              ) ll;
            end;

            fprintf ocw "\nPotential Variables for Annotation:\n";
            List.iter (
              fun l ->
                fprintf ocw "%s\n" l;
            ) !listOfPotentialVariablesForAnnotation;
            
            if !annotationFlag == true && !constantDimensionFlag == false then
              numberOfRequiredAnnotations := !numberOfRequiredAnnotations + 1;
        ) !listOfUnions;

        fprintf ocw "\n\nNumber of annotations required = %d\n" !numberOfRequiredAnnotations;
        totalNumberOfRequiredAnnotations := !totalNumberOfRequiredAnnotations + !numberOfRequiredAnnotations;
        if !subsystemRequiresAnnotationFlag == true then      
          listOfSubsystemsRequiresAnnotations := List.append !listOfSubsystemsRequiresAnnotations [subsystemName];
    done;
  with End_of_file ->
    close_in oc;
    fprintf ocw "\n\n\nSummary:\n";
    fprintf ocw "\nMinimum number of annotations required = %d\n" !totalNumberOfRequiredAnnotations;
    if List.length !listOfSubsystemsRequiresAnnotations == 0 then
      fprintf ocw "\nDimensions of all subsystems can be uniquely determined\n"
    else
    begin
      fprintf ocw "\nThe following subsystems requires annotations for their dimensions to be uniquely determined:\n\n";
      List.iter (
        fun f ->
          fprintf ocw "%s\n" f;
      ) !listOfSubsystemsRequiresAnnotations;
    end;
    fprintf ocw "\n\n";
  close_out ocw;
end;;

main();;
