//
// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
//
// The contents of this file are covered under the licence agreement in the
// file "LICENCE" distributed with Cforall.
//
// Autogen.h --
//
// Author           : Rob Schluntz
// Created On       : Sun May 17 21:53:34 2015
// Last Modified By : Rob Schluntz
// Last Modified On : Tue May 19 16:49:43 2015
// Update Count     : 1
//

#ifndef AUTOGEN_H
#define AUTOGEN_H

#include <string>
#include "SynTree/Statement.h"
#include "SynTree/Expression.h"
#include "SynTree/Declaration.h"
#include "SynTree/Initializer.h"

namespace SymTab {
  static const std::list< std::string > noLabels;

  /// Generates assignment operators, constructors, and destructor for aggregate types as required
  void autogenerateRoutines( std::list< Declaration * > &translationUnit );

  // originally makeArrayAssignment - changed to Function because it is now used for ctors and dtors as well
  // admittedly not a great name change. This used to live in Validate.cc, but has been moved so it can be reused elsewhere

  /// Store in out a loop which calls fname on each element of the array with srcParam and dstParam as arguments.
  /// If forward is true, loop goes from 0 to N-1, else N-1 to 0
  template< typename OutputIterator >
  void makeArrayFunction( Expression *srcParam, Expression *dstParam, ArrayType *array, std::string fname, OutputIterator out, bool forward = true ) {
    static UniqueName indexName( "_index" );

    // for a flexible array member nothing is done -- user must define own assignment
    if ( ! array->get_dimension() ) return;

    Expression * begin, * end, * update, * cmp;
    if ( forward ) {
      // generate: for ( int i = 0; i < 0; ++i )
      begin = new NameExpr( "0" );
      end = array->get_dimension()->clone();
      cmp = new NameExpr( "?<?" );
      update = new NameExpr( "++?" );
    } else {
      // generate: for ( int i = N-1; i >= 0; --i )
      begin = new UntypedExpr( new NameExpr( "?-?" ) );
      ((UntypedExpr*)begin)->get_args().push_back( array->get_dimension()->clone() );
      ((UntypedExpr*)begin)->get_args().push_back( new NameExpr( "1" ) );
      end = new NameExpr( "0" );
      cmp = new NameExpr( "?>=?" );
      update = new NameExpr( "--?" );
    }

    ObjectDecl *index = new ObjectDecl( indexName.newName(), DeclarationNode::NoStorageClass, LinkageSpec::C, 0, new BasicType( Type::Qualifiers(), BasicType::SignedInt ), NULL );

    UntypedExpr *init = new UntypedExpr( new NameExpr( "?=?" ) );
    init->get_args().push_back( new AddressExpr( new VariableExpr( index ) ) );
    init->get_args().push_back( begin );
    index->set_init( new SingleInit( init, std::list<Expression*>() ) );

    UntypedExpr *cond = new UntypedExpr( cmp );
    cond->get_args().push_back( new VariableExpr( index ) );
    cond->get_args().push_back( end );

    UntypedExpr *inc = new UntypedExpr( update );
    inc->get_args().push_back( new AddressExpr( new VariableExpr( index ) ) );

    // want to be able to generate assignment, ctor, and dtor generically,
    // so fname is either ?=?, ?{}, or ^?{}
    UntypedExpr *fExpr = new UntypedExpr( new NameExpr( fname ) );

    UntypedExpr *dstIndex = new UntypedExpr( new NameExpr( "?+?" ) );
    dstIndex->get_args().push_back( dstParam );
    dstIndex->get_args().push_back( new VariableExpr( index ) );
    fExpr->get_args().push_back( dstIndex );

    // srcParam is NULL for default ctor/dtor
    if ( srcParam ) {
      UntypedExpr *srcIndex = new UntypedExpr( new NameExpr( "?[?]" ) );
      srcIndex->get_args().push_back( srcParam );
      srcIndex->get_args().push_back( new VariableExpr( index ) );
      fExpr->get_args().push_back( srcIndex );
    }

    std::list<Statement *> initList;
    CompoundStmt * block = new CompoundStmt( noLabels );
    block->get_kids().push_back( new DeclStmt( noLabels, index ) );
    block->get_kids().push_back( new ForStmt( noLabels, initList, cond, inc, new ExprStmt( noLabels, fExpr ) ) );
    *out++ = block;
  }
} // namespace SymTab
#endif // AUTOGEN_H

