open Ast
module StringMap = Map.Make(String);;

type lattice_type = Alt | Seq | Predicate 
type data_type = Int | Str | Float | Bool | Anon | Error

type locals_t = {
	mutable members:  ( string (*parent*) * data_type * bool (*defined*)) StringMap.t;
	mutable parent: locals_t option;
	mutable name: string;
	mutable parent_name: string
	}
 
type env_t = {
	mutable globals: (string(*gen-code*) * data_type * bool (*defined*)) StringMap.t;
	mutable locals: locals_t	
	}
	
let env = {  globals = StringMap.empty;
			locals = {
                           members = StringMap.empty;
                           parent = None;
                           name = "$program";
                           parent_name = "";                                        
                  };
            };;

let program = [];;

let rec string_of_datatype var_type = 
	match var_type with
                  Float -> "float"
		| Int -> "int"
		| Str -> "string"
		| Anon -> "anon"
		| Error -> "error"
		| _ -> "uknown" 
			 
and
get_binop bop = 
			match bop with
                            Plus ->"plus"
                            | Minus -> "minus"
                            | Times -> "times"
                            | Divide ->"divide"
                            | Percent ->"mod"
                            | Or -> "or"
                            | And -> "and"
                            | Equal -> "equal"
                            | NotEqual -> "notEqual"
                            | Less -> "lessThan"
                            | LessOrEqual -> "lessThanOrEqual"
                            | Greater -> "greaterThan"
                            | GreaterOrEqual -> "greaterThanOrEqual"
                            | Tilde -> "tilde"

			
and
translate_variable id =  "$" ^ id 

and
string_map_pairs map pairs =
	List.fold_left (fun m (i, n) -> StringMap.add n i m) map pairs

and 
translate_label label =
	match label with
	Variable(label_name) -> translate_variable (label_name)

and 
translate_func name_trans param_list altlat func_type = 
	match func_type with
			FuncLabelDecl -> 
			    let param_trans = translate_params param_list in
                            let (altlat_trans,datatype,defined) = translate_altlat altlat in 
			    let func_trans = name_trans  ^ param_trans ^ ";"  in
                            let func_trans = func_trans ^name_trans ^ altlat_trans ^ ";" in 
			    (func_trans,datatype)
		|       FuncLocalDecl -> 
                            let param_trans = translate_params param_list in		
                            let (altlat_trans,datatype,defined) = translate_altlat altlat in 
                            let func_trans = name_trans  ^ param_trans ^ ";"  in
                            let func_trans = func_trans ^ name_trans ^ altlat_trans ^ ";" in
                            (func_trans,datatype)

and 
translate_params param_list = 
if ((List.length param_list) > 0) then
	(
		let head = (List.hd param_list) in 
		let head_trans = translate_label head in 
		let head_trans1 = ".addToParams(" ^ head_trans ^ ")" in
		let tail = (List.tl param_list) in 
		let tail_trans = translate_params tail in
		let param_trans = head_trans1 ^ tail_trans in
		param_trans 
	)
	else
		"" 
			
and 
translate_single_name node =
	match node with
			Label(label) -> translate_label label
		|	Attribute(attribute) -> attribute 
		(*| SeqlatIndexing(altlat) -> "getElementAtIndex(" ^ (translate_altlat altlat ) ^ ", Lattice.SEQ)" 
		| AltlatIndexing(altlat) -> "getElementOfIndex(" ^ (translate_altlat altlat ) ^ ", Lattice.OR)" *)
		(*| Application(name,seqlat) -> "(" ^ translate_seqlat seqlat ^ ")" *)

and 
translate_rest_of_name name =
	match name with 
			[] -> ""
		|	hd::tl -> (translate_single_name hd) ^ (
				if (List.length tl) == 0 then "" 
				else let tl_trans = (translate_rest_of_name tl) in
					(* if Application, no need for '.' *) 
					(if (String.get tl_trans 0)=='(' then "" else ".") ^ tl_trans)

and 
translate_name name  = 
	match name with 
	  NoName -> ""
	| NoHead(list_of_name) -> translate_rest_of_name (list_of_name)
	| This(list_of_name) ->
                if(List.length list_of_name > 0) then
                    let name_trans = env.locals.parent_name ^ "." ^ translate_rest_of_name (list_of_name) in
                    name_trans
                else
                    let name_trans = env.locals.parent_name in
                    name_trans
				
and 
translate_lvalue lvalue = 
	match lvalue with 
	 			Name(listOfNames) ->
		    let name_trans =	translate_name (listOfNames) in name_trans
    |   Seqlat2(lattice) -> ""(*
            let child_locals = {
                                    members = StringMap.empty;
                                    parent = Some env.locals;
                                    name =  "anon";
                                    parent_name = env.locals.name;
                                }
            in (*env.locals <- child_locals;*)
            let seq_lat_trans = translate_seqlat lattice in
            (*let new_globals = seq_lat_trans.globals in
            env.globals <- new_globals;   *)"seq"*)

and
translate_expr op = 
	match op with 
          NoAtom -> ("NoAtom",Anon,false,false)
				| Eps-> ("Lattice.EPSILON",Anon,false,false)
				| Nil -> ("Lattice.NIL",Anon,false,false)
				| True -> ("true",Bool,true,false)
				| False -> ("false",Bool,true,false)
				| StringLiteral(str) -> (str,Str,true,false)
				| IntLiteral(num) -> (string_of_int num, Int,true,false)
				| FloatLiteral(num) -> (string_of_float num,Float,true,false)
				| Neg(num) -> let (expr_trans,expr_type,is_defined,is_localvar) = translate_expr num in ("!" ^ expr_trans,expr_type,is_defined,is_localvar)
				| BinOp(lvalue, bop, rvalue) -> (*Heba: Complete this*)
					       let (lvalue_trans,lvalue_type,lvalue_defined,_) = translate_expr lvalue in                                               
                                               let (rvalue_trans,rvalue_type,rvalue_defined,_) = translate_expr rvalue in
                                               let rtype = (string_of_datatype rvalue_type) in
                                               let ltype = (string_of_datatype lvalue_type) in
                                               if(rvalue_type <> Anon && lvalue_type <> Anon && lvalue_type <> rvalue_type) then
                                                    ("Error", Error, false,true)
                                               else if(rvalue_type <> Anon && lvalue_type <> Anon) then
                                                    let code = "new(" ^ rtype ^ "_" ^ (get_binop bop ) ^ "_" ^ ltype ^ "("
                                                    ^ lvalue_trans ^ ","^ rvalue_trans ^ "))" in 
                                                    (code, lvalue_type, lvalue_defined,true)
                                                else if (rvalue_type <> Anon) then
                                                    let code = "new(" ^ lvalue_trans ^ "_" ^ (get_binop bop ) ^ "_" ^ rtype ^ "("
                                                    ^ lvalue_trans ^ ","^ rvalue_trans ^ "))" in 
                                                    (code, lvalue_type, lvalue_defined,true)
                                                else
                                                    let code = "new(" ^ ltype ^ "_" ^ (get_binop bop ) ^ "_" ^ rvalue_trans ^ "("
                                                    ^ lvalue_trans ^ ","^ rvalue_trans ^ "))" in 
                                                    (code, lvalue_type, lvalue_defined,true)
                                                    
						
				| Lvalue(lvalue) ->     let element_name = translate_lvalue lvalue in
                                                        let full_name = env.locals.parent_name ^ "_" ^ element_name  in 
                                                        if(StringMap.mem element_name env.globals) then
                                                        let (element_code,element_datatype,is_defined) = StringMap.find full_name env.globals in
                                                            (element_name,element_datatype,true,false)
                                                        else
                                                            (element_name,Anon,false,true)
			(*  |    _ -> "" *)
				
and 
translate_altlist alt_list  = 
	if (List.length alt_list > 0) then
	( 
		let (head_trans,head_type,head_defined,is_localvar) =  translate_expr (List.hd alt_list) in
		let (tail_trans,tail_type,tail_defined) = translate_altlist (List.tl alt_list) in
		if(head_trans = "") then 
		(        
		    (tail_trans,tail_type,head_defined)
    )
		else 
      (
						let head_code =   ".addToLocals(" ^ head_trans ^ ")" in
            let (tail_trans,tail_type,tail_defined) = translate_altlist (List.tl alt_list) in                                    
             let alt_list_trans = head_code ^ tail_trans in
						if(is_localvar = true) then						
								if((head_type <> tail_type) && (head_type <> Anon) && (tail_type <> Anon))then
              		env.locals.members <- StringMap.add head_trans  ("Type Mismatch",Error,false) env.locals.members
								else if((head_type <> Anon) && (head_defined = true))then
									env.locals.members <- StringMap.add head_trans (env.locals.name,head_type,true) env.locals.members
								else if(head_type <> Anon )then
										env.locals.members <- StringMap.add head_trans (env.locals.name,head_type,false) env.locals.members
								else if (tail_type <> Anon && head_defined = true ) then
										env.locals.members <- StringMap.add head_trans (env.locals.name,tail_type,true) env.locals.members
								else if (tail_type <> Anon ) then
										env.locals.members <- StringMap.add head_trans (env.locals.name,tail_type,false) env.locals.members
								else 
									env.locals.members <- StringMap.add head_trans (env.locals.name,Anon,head_defined) env.locals.members;
					     
             
             if((head_type <> tail_type) && (head_type <> Anon) && (tail_type <> Anon))then
             (
                  ("Type Error",Error,false)
             )
              else if(head_type <> Anon) then
              (
                  if(head_defined = true) then
										(alt_list_trans,head_type,true)
                  else
										(alt_list_trans,head_type,false)
                    
              )
							else if (tail_type <> Anon) then
              (
                  if(head_defined = true) then
                      (alt_list_trans,tail_type,true)
                  else
                      (alt_list_trans,tail_type,false)
              )
              else
              (
                  if(head_defined = true) then
                    (alt_list_trans,head_type,true)
                  else
                    (alt_list_trans,head_type,false)
               )
            )
	)
        else
        ("",Anon,false)

and 
translate_altlat alt_list = 
	 match alt_list with 
			Altlat(alt_list) -> translate_altlist alt_list

and
translate_element element = (* element = labeled_altlat *) 
 	 match element with LabelDecl(name, altlat) ->
		let name_trans = translate_name name in
		if(name_trans = "" ) then
		(
		    ("","",Anon,false)
		)
		else
    (	
        let old_locals = env.locals in
        let new_locals = 
		    {
					members = StringMap.empty;
					name = name_trans;
		      parent = Some env.locals;
          parent_name = env.locals.name;
		    } in
		    env.locals <- new_locals;
        let (altlat_trans,altlat_datatype,is_defined) = translate_altlat altlat in
        let altlat_trans =    env.locals.parent_name ^ ".addToLattice(" ^ env.locals.name ^ altlat_trans ^ ");" in
										let cur_name = env.locals.parent_name ^ "_" ^ name_trans in
										env.locals.name <- old_locals.name;
										env.locals.parent <- old_locals.parent;
										env.locals.parent_name <- old_locals.parent_name;
        					if(StringMap.mem cur_name env.globals) then
		   							(
                        let (old_val,old_datatype,old_defined) = StringMap.find cur_name env.globals in                                 
                        if((altlat_datatype <> Anon) && (altlat_datatype <> Anon) && (altlat_datatype <> old_datatype)) then
                          (*  env.globals <- StringMap.add env.locals.name (altlat_trans,altlat_datatype,true) env.globals*)
                            env.globals <- StringMap.add cur_name ("Type Mismatch",Error,true) env.globals
                        else if (altlat_datatype <> Anon) then
                            env.globals <- StringMap.add cur_name (altlat_trans,altlat_datatype,true) env.globals
                        else if (old_datatype <> Anon) then
                            env.globals <- StringMap.add cur_name (altlat_trans,old_datatype,true) env.globals
                        else
                            env.globals <- StringMap.add cur_name (altlat_trans,altlat_datatype,true) env.globals;
                        
                        if((altlat_datatype <> Anon) && (altlat_datatype <> Anon) && (altlat_datatype <> old_datatype)) then                                
                            (cur_name,"Type Mismatch",Error,true)
                        else if (altlat_datatype <> Anon) then                            
                            (cur_name,altlat_trans,altlat_datatype,true)
                        else if (old_datatype <> Anon) then                            
                            (cur_name,altlat_trans,old_datatype,true)
                        else                            
                            (cur_name,altlat_trans,Anon,true)                    
                    )
                    else
                    (
                        let new_globals = StringMap.add cur_name (altlat_trans,altlat_datatype,true) env.globals	in
                        env.globals <- new_globals;
                        (cur_name, altlat_trans,altlat_datatype,true)
                    )                    
								)
                        
                | LocalDecl(name, altlat) ->
                   let name_trans = translate_name name in
                   if(name_trans = "" ) then
                   (
                           ("","",Anon,false)
                   )
                   else(	
									let new_locals = 
								    {
											members = StringMap.empty;
											name = env.locals.name ^ "_" ^ name_trans;
							        parent = Some env.locals;
											parent_name = env.locals.name;
							      } in
							  let old_locals = env.locals in
					                        env.locals <- new_locals;
								let (altlat_trans,altlat_datatype,is_defined) = translate_altlat altlat in
					          if(altlat_datatype <> Error) then
					            (
					               let gen_code = name_trans  ^ altlat_trans ^ ";"^ env.locals.parent_name ^ ".addToLattice("  ^ name_trans ^ ");"   in
					               let name_trans = env.locals.name  in
					               env.locals <- old_locals;
					               (name_trans, gen_code,altlat_datatype,true)
					           )
					           else
					           (
					            	let gen_code = "Type Mismatch"   in
					             	let name_trans = env.locals.name  in
					              env.locals <- old_locals;
					              (name_trans, gen_code,altlat_datatype,is_defined)
					          )
								)
							| FunctionDecl(name,param_list,func_type,altlat) -> 
								let name_trans = translate_name name in
								let old_locals = env.locals in
								env.locals <- {
										members = StringMap.empty;
					                         		name = name_trans;
					                            		parent_name = old_locals.name;
										parent = Some old_locals;
									     };
								    (*let altlat_tran = translate_altlat in*)
								let (func_trans,ret_type) = translate_func name_trans param_list altlat func_type in 
								env.locals <- old_locals;
								(name_trans,func_trans,ret_type,true)
		
and
translate lattice = 
	let length = List.length lattice in 
	if (length > 0) then 
	(   
	    let head = (List.hd lattice) in let tail = (List.tl lattice) in 
            let head_trans = translate_element head in 
            let tail = List.tl lattice in 
            let tail_trans = translate tail in
            head_trans::tail_trans
                    
	)
        else	
	    []


let _ =
 let lexbuf = Lexing.from_channel stdin in
 let lattice = Latparser.lattice Scanner.token lexbuf in
	let result = translate lattice in
	let new_globals = StringMap.fold(fun x (a,b,c) z -> (x,a,b,c)::z) env.globals [] in
        let _ = print_endline "Globals: " in
	let _ = List.iter(fun element->Printf.printf "%s\n" ((match element with (first, second, third,fourth) ->first ^ "\t" ^ second ^ "\t" ^ (string_of_datatype third) ^ "\t" ^ string_of_bool fourth ))) new_globals in
        let new_locals = StringMap.fold(fun x (a,b,c) z -> (x,a,b,c)::z) env.locals.members [] in
        let _ = print_endline "\nLocals: " in	
        List.iter(fun element->Printf.printf "%s\n" ((match element with (first, second, third,fourth) ->first ^ "\t" ^ second  ^ "\t" ^ string_of_datatype third ^ "\t" ^ string_of_bool fourth))) new_locals;;
        
        
