/*******************************************************************\
 
Module: Read goto object files.
 
Author: CM Wintersteiger
 
Date: June 2006
 
\*******************************************************************/

#include <xmllang/xml_parser.h>
#include <namespace.h>
#include <base_type.h>
#include <xml_irep_hashing.h>
#include <xml_symbol_hashing.h>

#define XML_VERSION "1.1"

#include "read_goto_object.h"
#include "xml_goto_function_hashing.h"

/*******************************************************************\
 
Function: duplicate_symbol
 
  Inputs: two symbols
 
 Outputs: true on error, false otherwise
 
 Purpose: decides if the new symbol should overwrite an old one and 
 					does so if necessary
 
\*******************************************************************/
bool duplicate_symbol(
  symbolt &in_context,
  namespacet &ns,
  symbolt &new_symbol)
{
  // see if it is a function or a variable
  
  try {
  
    bool is_code_in_context=in_context.type.id()=="code";
    bool is_code_new_symbol=new_symbol.type.id()=="code";
  
    if(is_code_in_context!=is_code_new_symbol)
    {
      std::cerr << new_symbol.type.location() << std::endl;
      std::cerr << "error: conflicting definition for symbol \""
          << in_context.display_name()
          << "\"" << std::endl;
      std::cerr << "old definition: " << in_context.type.pretty()
          << std::endl;
      std::cerr << "Module: " << in_context.module << std::endl;
      std::cerr << "new definition: " << new_symbol.type.pretty()
          << std::endl;
      std::cerr << "Module: " << new_symbol.module << std::endl;
      throw 0;
    }
  
    if(is_code_in_context)
    {
      // both are functions
  
      // we don't compare the types, they will be too different
  
      // care about code
  
      if(!new_symbol.value.is_nil())
      {
        if(in_context.value.is_nil())
        {
          // the one with body wins!
          in_context.value.swap(new_symbol.value);
          in_context.type.swap(new_symbol.type); // for argument identifiers
        }
        else if(in_context.type.get_bool("#inlined"))
        {
          // ok
        }
        else if(in_context.type==new_symbol.type) 
        {
          std::cout << "warning: duplicate function definition for `" <<
          in_context.name << "', but they're equal." << std::endl;
        }
        else
        {
          std::cerr << new_symbol.value.location() << std::endl;
          std::cerr << "error: duplicate definition of function `"
              << in_context.name
              << "'" << std::endl;
          std::cerr << "In module `" << in_context.module
              << "' and module `" << new_symbol.module << "'" << std::endl;
          std::cerr << "old definition: " << std::endl;
          in_context.show(std::cerr);
          std::cerr << "new definition: " << std::endl;
          new_symbol.show(std::cerr);
          throw 0;
        } 
      }
    }
    else
    {
      // both are variables
  
      if(!base_type_eq(in_context.type, new_symbol.type, ns))
      {
        if(in_context.type.id()=="incomplete_array" &&
           new_symbol.type.id()=="array")
        {
          // store new type
          in_context.type=new_symbol.type;
        }
        else if(in_context.type.id()=="array" &&
                new_symbol.type.id()=="incomplete_array")
        {
          // ignore
        }
        else if(in_context.type.id()=="incomplete_struct" &&
                new_symbol.type.id()=="struct")
        {
          // store new type
          in_context.type=new_symbol.type;
        }
        else if(in_context.type.id()=="struct" &&
                new_symbol.type.id()=="incomplete_struct")
        {
          // ignore
        }
        else
        {
          std::cerr << new_symbol.type.location() << std::endl;
          std::cerr << "error: conflicting definition for variable \""
              << in_context.name
              << "\"" << std::endl;
          std::cerr << "old definition: " << in_context.type.pretty()
              << std::endl;
          std::cerr << "Module: " << in_context.module << std::endl;
          std::cerr << "new definition: " << new_symbol.type.pretty()
              << std::endl;
          std::cerr << "Module: " << new_symbol.module << std::endl;
          throw 0;
        }
      }
  
      // care about initializers    
  
      if(!new_symbol.value.is_nil() &&
         !new_symbol.value.get_bool("#zero_initializer"))
      {
        if(in_context.value.is_nil() ||
           in_context.value.get_bool("#zero_initializer"))
        {
          in_context.value.swap(new_symbol.value);
        }
        else if(in_context.value!=new_symbol.value)
        {
          std::cerr << new_symbol.value.location() << std::endl;
          std::cerr << "error: conflicting initializers for variable \""
              << in_context.name
              << "\"" << std::endl;
          std::cerr << "old value: " << in_context.value.pretty()
              << std::endl;
          std::cerr << "Module: " << in_context.module << std::endl;
  				std::cerr << "new value: " << new_symbol.value.pretty()
              << std::endl;
          std::cerr << "Module: " << new_symbol.module << std::endl;
          throw 0;
        }
      }
    }
  } catch(int i) {
    std::cout << "Integer exception thrown while checking for duplicate symbol (" 
    << i << ")" << std::endl;
    return true;
  } catch (const std::string s) {
    std::cout << "Exception thrown while checking for duplicate symbol:" <<
    s << std::endl;
    return true;
  }
  return false;
}

/*******************************************************************\
 
Function: read_goto_object
 
  Inputs: input stream, context, functions
 
 Outputs: true on error, false otherwise
 
 Purpose: reads a goto object xml file back into a symbol and a 
          function table
 
\*******************************************************************/

bool read_goto_object(
  std::istream &in,
  const std::string &filename,
  contextt &context,
  goto_functionst &functions,
  message_handlert &msg_hndlr)
{
  // we parse to a temporary context
  contextt temp_context;
  
  xml_parser.clear();
  xml_parser.filename = filename;
  xml_parser.in = &in;
  xml_parser.set_message_handler(&msg_hndlr);

  if (xml_parser.parse())
    return true;

  xmlt &top = xml_parser.parse_tree.element;
  
  if (top.get("version")!=XML_VERSION) 
  {
    std::cerr << "The input was compiled with a different version of" <<
    "goto-cc, please recompile." << std::endl;
    return false;
  }
  
  xml_irep_convertt::ireps_containert ic;
  xml_irep_convertt irepconverter(ic);
  xml_symbol_convertt symbolconverter(ic);
  xml_goto_function_convertt gfconverter(ic);
  
  if(top.name.substr(0, 11)=="goto-object")
  {    
    for(xmlt::elementst::const_iterator
        sec_it=top.elements.begin();
        sec_it != top.elements.end();
        sec_it++)
    {
      xmlt sec = *sec_it;
      if (sec.name=="irep_hash_map") 
      {
        for(xmlt::elementst::const_iterator
            irep_it = sec.elements.begin();
            irep_it != sec.elements.end();
            irep_it++)
        {
          irept i;
          irepconverter.convert(*irep_it, i);
          irepconverter.insert(irep_it->get("id"), i);
        }        
      }
      else if (sec.name=="symbols")
      {        
        for(xmlt::elementst::const_iterator
            sym_it = sec.elements.begin();
            sym_it != sec.elements.end();
            sym_it++)
        {
          symbolt symbol;
          symbolconverter.convert(*sym_it, symbol);
          // std::cout << "Adding Symbol: " << symbol.name << std::endl;
          if(!symbol.is_type &&
             symbol.type.id()=="code")
          {
            // makes sure there is an empty function
            // for this symbol. if we got code for it,
            // it will be added lateron.
            functions.function_map[symbol.name].type=
              to_code_type(symbol.type);
          }
          temp_context.add(symbol);          
        }
      }
      else if (sec.name=="functions")
      {        
        for(xmlt::elementst::const_iterator
            fun_it = sec.elements.begin();
            fun_it != sec.elements.end();
            fun_it++)
        {
          std::string fname = fun_it->get("name");
          //std::cout << "Adding function body: " << fname << std::endl;          
          goto_functiont &f = functions.function_map[fname];
          gfconverter.convert(*fun_it, f);
        }
      }
      else
      {
        std::cerr << "Unknown Section '" << sec.name <<
        "' in object file." << std::endl;
        return true;
      }

    }
  }
  else
  {
    std::cerr << "no goto-object" << std::endl;
    return true;
  }  
  
  // move stuff into the real context and do the duplicate check
  namespacet ns = namespacet(context, temp_context);
  forall_symbols(it, temp_context.symbols) {
      // search for another occurence of the symbol
      symbolt symbol = it->second;
      symbolst::iterator i = context.symbols.find(symbol.name);
      
      if (i!=context.symbols.end())
      {        
        if (duplicate_symbol(i->second, ns, symbol)) return true;            
      }
      else
      {
        // just copy it over
        context.add(symbol);
      }
  }
    
  xml_parser.clear();
  return false;
}
