use std::rc::Rc;
use std::fmt;
use parser::*;

pub fn env() -> LetRecLangEnv {
     LetRecLangEnv::new_env()
}
pub type Identifier = String;

pub trait Clone {
    fn clone(&self) -> Self;
}

#[derive(Clone)]
pub enum LetRecLangEnv{
    EmptyEnv,
    ExtendEnv(String, IntBoolProc, Rc<LetRecLangEnv>),
    // ExtendEnvRec(String, String,IntBoolProc, Rc<LetRecLangEnv>),
}
#[derive(Clone)]
pub enum IntBoolProc{
Integer(i32),
Boolean(bool),
Procedure(String,Rc<ProcLangExp>,Rc<LetRecLangEnv>),
}
impl IntBoolProc {
    pub fn to_string(&self) -> String {
        match self.clone() {
            IntBoolProc::Integer(i) => i.to_string(),
            IntBoolProc::Boolean(b) => b.to_string(),
            IntBoolProc::Procedure(v,e1,e2)=>{let mut temp = "Intboolproc ".to_string();
                                                temp.push_str(&(v.to_string()));
                                                temp.push_str(&(e1.to_string()));
                                                temp.push_str(&(e2.to_string()));
                                                temp},
                                            
        }
    }
}

impl  LetRecLangEnv{
    //  fn option_to_IntBool(&mut self,Env: Option<IntBoolProc>)->IntBoolProc{
    //     match Env {
    //         Some(v) =>{v},
    //         None =>{IntBoolProc::Boolean(false)},
    //     }
    //  }

    pub fn new_env()-> Self{
    LetRecLangEnv::EmptyEnv
    }

    pub fn extend_env(&self, s: &String, val: IntBoolProc)->Self{
        LetRecLangEnv::ExtendEnv(s.clone(), val.clone(),Rc::new((*self).clone()))
        }



    pub fn apply_env(&self, s: &String)->Option<IntBoolProc>{
        match (*self).clone(){
                    LetRecLangEnv::ExtendEnv(chk, val,env_1) => {if s[..]==chk[..]{Some(val)}
                                                            else{env_1.apply_env(&s)}},
                    LetRecLangEnv::EmptyEnv=>{None},
        }
    }

    pub fn is_null_env(&self) -> bool { 
        match (*self).clone(){
                    LetRecLangEnv::ExtendEnv(chk, val,env_1) => {false},//if some thing there false
                    LetRecLangEnv::EmptyEnv=>{true},//if it is empty give true
        }}
   //to print the encvironment
    pub fn to_string(&self) -> String {
        match self.clone() {
            LetRecLangEnv::EmptyEnv => "[]".to_string(),
            LetRecLangEnv::ExtendEnv(var,val,env) => {let mut temp = "[".to_string();
                                                temp.push_str(&(var.to_string()));
                                                temp.push_str(&(", ".to_string()));
                                                temp.push_str(&(val.to_string()));
                                                temp.push_str(&(" ".to_string()));
                                                temp.push_str(&(env.to_string()));
                                                temp.push_str(&("]".to_string()));
                                                temp},
        }}

}
impl fmt::Display for LetRecLangEnv {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut s = format!("");
        let s1 = self.to_string();
        s.push_str(&s1);
        write!(f, "{}", s)
    }}


    /*    
impl Clone for EnvEnv { 

    fn clone(&self) -> EnvEnv {
        match *self {
            EnvEnv::EmptyEnv => EnvEnv::EmptyEnv,
            EnvEnv::ExtendEnv(a, b, env_1) => EnvEnv::ExtendEnv(a.clone(), &b.clone(), env_1.clone())
        }
            
    }}


        
  
*/
