#include "InitTweak.h"
#include "SynTree/Visitor.h"
#include "SynTree/Statement.h"
#include "SynTree/Initializer.h"
#include "SynTree/Expression.h"
#include "GenPoly/GenPoly.h"

namespace InitTweak {
  namespace {
    class HasDesignations : public Visitor {
    public:
      bool hasDesignations = false;
      template<typename Init>
      void handleInit( Init * init ) {
        if ( ! init->get_designators().empty() ) hasDesignations = true;
        else Visitor::visit( init );
      }
      virtual void visit( SingleInit * singleInit ) { handleInit( singleInit); }
      virtual void visit( ListInit * listInit ) { handleInit( listInit); }
    };

    class InitExpander : public Visitor {
      public:
      InitExpander() {}
      virtual void visit( SingleInit * singleInit );
      virtual void visit( ListInit * listInit );
      std::list< Expression * > argList;
    };

    void InitExpander::visit( SingleInit * singleInit ) {
      argList.push_back( singleInit->get_value()->clone() );
    }

    void InitExpander::visit( ListInit * listInit ) {
      // xxx - for now, assume no nested list inits
      std::list<Initializer*>::iterator it = listInit->begin_initializers();
      for ( ; it != listInit->end_initializers(); ++it ) {
        (*it)->accept( *this );
      }
    }
  }

  std::list< Expression * > makeInitList( Initializer * init ) {
    InitExpander expander;
    maybeAccept( init, expander );
    return expander.argList;
  }

  bool isDesignated( Initializer * init ) {
    HasDesignations finder;
    maybeAccept( init, finder );
    return finder.hasDesignations;
  }

  bool tryConstruct( ObjectDecl * objDecl ) {
    return ! LinkageSpec::isBuiltin( objDecl->get_linkage() ) &&
      (objDecl->get_init() == NULL ||
        ( objDecl->get_init() != NULL && objDecl->get_init()->get_maybeConstructed() )) &&
      ! isDesignated( objDecl->get_init() );
  }

  bool isInstrinsicSingleArgCallStmt( Statement * stmt ) {
    if ( stmt == NULL ) return false;
    if ( ExprStmt * exprStmt = dynamic_cast< ExprStmt * >( stmt ) ) {
      ApplicationExpr * appExpr = dynamic_cast< ApplicationExpr * >( exprStmt->get_expr() );
      assert( appExpr );
      VariableExpr * function = dynamic_cast< VariableExpr * >( appExpr->get_function() );
      assert( function );
      // check for Intrinsic only - don't want to remove all overridable ctor/dtors because autogenerated ctor/dtor
      // will call all member dtors, and some members may have a user defined dtor.
      FunctionType * funcType = GenPoly::getFunctionType( function->get_var()->get_type() );
      assert( funcType );
      return function->get_var()->get_linkage() == LinkageSpec::Intrinsic && funcType->get_parameters().size() == 1;
    } else if ( CompoundStmt * compoundStmt = dynamic_cast< CompoundStmt * >( stmt ) ) {
      // could also be a compound statement with a loop, in the case of an array
      assert( compoundStmt->get_kids().size() == 2 ); // loop variable and loop
      ForStmt * forStmt = dynamic_cast< ForStmt * >( compoundStmt->get_kids().back() );
      assert( forStmt && forStmt->get_body() );
      return isInstrinsicSingleArgCallStmt( forStmt->get_body() );
    } else {
      // should never get here
      assert( false && "encountered unknown call statement" );
    }
  }
}
