/* user_types.cc */

#include "user_types.h"

struct user_types {
  dictionary<string,user_type> type_dict;
  dictionary<user_type,u_type_set *> impl_dict;
  
  user_types( void );
  ~user_types( void );
};

static user_types U;

user_types::user_types( void ) {
  type_dict.insert( string( "No_Type" ), No_Type );
}

user_types::~user_types( void ) {
  type_dict.clear();
  
  dic_item di;
  forall_items( di, impl_dict ) {
    delete( impl_dict.inf( di ) );
  }
  impl_dict.clear();
}

bool isdef_type( cstring type_label ) {
  return parse_u_type( type_label ) != Undefined_Type;
}

user_type define_u_type( cstring type_label ) {
  static user_type type_counter = 1;
  user_type check = parse_u_type( type_label );
  if( check == Undefined_Type ) {
    U.type_dict.insert( type_label, ++type_counter );
    return type_counter;
  } else return check;
}

bool undef_u_type( cstring type_label ) {
  dic_item di = U.type_dict.lookup( type_label );
  if( !di ) return true;
  U.type_dict.del_item( di );
  return false;
}

string u_type_string( const user_type u_type ) {
  dic_item it;
  forall_items( it, U.type_dict ) {
    if( U.type_dict.inf( it ) == u_type ) {
      return U.type_dict.key( it );
    }
  }
  return string( "Undefined_Type" );
}

user_type parse_u_type( cstring type_label ) {
  dic_item it = U.type_dict.lookup( type_label );
  if( !it ) return Undefined_Type;
  return U.type_dict.inf( it );
}

bool add_u_type_impl( const user_type base, const user_type implied ) {
  dic_item di = U.impl_dict.lookup( base );
  u_type_set *base_impl_set;
  if( di == nil ) {
    base_impl_set = new u_type_set;
    U.impl_dict.insert( base, base_impl_set );
  } else {
    base_impl_set = U.impl_dict.inf( di );
  }
  return base_impl_set->set_type( implied );
}

void list_u_types( ostream &stream = cout ) {
  dic_item it;
  stream << "USER_TYPES\n----------\n";
  forall_items( it, U.type_dict ) {
    user_type utype = U.type_dict.inf( it );
    string type_name = U.type_dict.key( it );
    stream << utype << " : " << type_name  << "\n";
    dic_item utit = U.impl_dict.lookup( utype );
    if( utit ) {
      u_type_set *uts = U.impl_dict.inf( utit );
      stream << "IMPLIES : ";
      stream << uts->list_types() << "\n";
    }
  }
}

void undef_all_u_types( void ) {
  U.type_dict.clear();
}

u_type_set::~u_type_set( void ) {
  D.clear();
}

bool u_type_set::set_type( const user_type u_type, const bool inh = false ) {
  if( D.lookup( u_type ) ) return true;
  D.insert( u_type, inh );

  dic_item di = U.impl_dict.lookup( u_type );
  if( di != nil ) {
    u_type_set *uts = U.impl_dict.inf( di );
    this->inherit_all( *uts );
  }
  return false;
}

/* bool u_type_set::unset_type( const user_type u_type ) {
   dic_item di = D.lookup( u_type );
   if( !di ) return true;
   D.del_item( di );
   
   return false;
   } */

bool u_type_set::is_type( const user_type u_type ) const {
  return ( D.lookup( u_type ) != nil );
}

bool u_type_set::is_type( const user_type u_type, bool &inh ) const {
  dic_item di = D.lookup( u_type );
  if( !di ) return false;
  inh = D.inf( di );
  return true;
}

string u_type_set::list_types( void ) const {
  string type_list;
  dic_item di;
  bool first = true;
  forall_items( di, D ) {
    if( first ) {
      first = false;
    } else {
      type_list += ", ";
    }
    if( D.inf( di ) ) type_list += "inherited ";
    else type_list += "local ";
    type_list += u_type_string( D.key( di ) );
  }
  return type_list;
}

void u_type_set::inherit( const u_type_set &uts ) {
  dic_item di;
  forall_items( di, uts.D ) {
    bool inh = D.inf( di );
    if( inh ) {
      user_type key = uts.D.key( di );
      dic_item uts_di = D.lookup( key );
      if( !uts_di ) {
	set_type( key, true );
      } else {
	D.change_inf( uts_di, true );
      }
    }
  }
}

void u_type_set::inherit_all( const u_type_set &uts ) {
  dic_item di;
  forall_items( di, uts.D ) {
    bool inh = uts.D.inf( di );
    user_type key = uts.D.key( di );
    dic_item uts_di = D.lookup( key );
    if( !uts_di ) {
      set_type( key, true );
    } else {
      D.change_inf( uts_di, inh );
    }
  }
}

u_type_set &u_type_set::operator=( const u_type_set &uts ) {
  D.clear();
  dic_item di;
  forall_items( di, uts.D ) {
    D.insert( uts.D.key( di ), uts.D.inf( di ) );
  }
  return *this;
}

u_type_set &u_type_set::operator+=( const u_type_set &uts ) {
  dic_item di;
  forall_items( di, uts.D ) {
    dic_item old = D.lookup( uts.D.key( di ) );
    if( old ) {
      if( uts.D.inf( di ) )
	D.change_inf( old, true );
    } else {
      D.insert( uts.D.key( di ), uts.D.inf( di ) );
    }
  }
  return *this;
}

int compare( const user_type a, const user_type b ) {
  return a - b;
}
