# rewrites the expressions generated by clang

# dictionary to map pointer types to desired type

ptr_dict = Dict{Any, Any} (
:(Ptr{PetscInt}) => :(Union(Ptr{PetscInt}, AbstractArray{PetscInt}, Ptr{Void}))
)

ccall_dict = Dict{Any, Any} (
:Mat => :(Ptr{Void}),
)

function petsc_rewriter(obuf)

  for i=1:length(obuf)
    println("i = ", i)
    ex_i = obuf[i]  # obuf is an array of expressions

    println("ex_i = ", ex_i)
    println("typeof(ex_i) = ", typeof(ex_i))

    if typeof(ex_i) == Expr
      head_i = ex_i.head  # function
      # each ex_i has 2 args, a function signature and a function body
      println("head_i = ", head_i)

      # figure out what kind of expression it is, do the right modification
      if head_i == :function
        rewrite_sig(ex_i.args[1])
        rewrite_body(ex_i.args[2])
      elseif ex_i.head == :const
        make_global_const(ex_i)
      elseif ex_i.head == :typealias
        make_typealias_void(ex_i)
      else
        println("not processing expression", ex_i)
      end

    else
      println("not processing ", typeof(ex_i))
    end  # end if Expr


  end

  return obuf  # return modified obuf
end


##### functions to rewrite function signature #####
function rewrite_sig(ex)  # rewrite the function signature

  @assert ex.head == :call  # verify this is a function signature expression

  # ex.args[1] = function name, as a symbol

  for i=2:length(ex.args)  # process all arguments of the function,
                           # each of which is an expression containing arg name, argtype
    println("typeof(ex.args[$i]) = ", typeof(ex.args[i]))
    @assert typeof(ex.args[i]) == Expr  || typeof(ex.args[i]) == Symbol  # verify these are all expressions
    process_sig_arg(ex.args[i])  # process each expression
  end

end

function process_sig_arg(ex)  # take the expression that is an argument to the function and rewrite it

   @assert ex.head == :(::)
   
   # modify args here
#   arg_name = ex.args[1]  # symbol
#   arg_type = ex.args[2]  # Expr contianing type tag
   println("ex.args[2] = ", ex.args[2])
   println("type = ", typeof(ex.args[2]))
   ex.args[2] = modify_typetag(ex.args[2])

end

function modify_typetag(ex)

#  @assert ex.head == :curly  # verify this is a typetag
  return get(ptr_dict, ex, ex)  # get new typetag if one was given, either an Expr or a Symbol
end



#####  function to  rewrite the body of the function #####
function rewrite_body(ex)  # rewrite body of a function


  @assert ex.head == :block
  # ex has only one argument, the ccall
  # could insert other statements arond the ccall here?

  process_ccall(ex.args[1])
end


function process_ccall(ex)

  @assert ex.head == :ccall  # verify this is the ccall statement
  # args[1] = Expr, tuple of fname, libname
  # args[2] = return type, symbol
  # args[3] = Expr, tuple of types to ccall
  # args[4] = symbol, first argument name,
  # ...
  modify_types(ex.args[3])

end

function modify_types(ex)

  @assert ex.head == :tuple

  for i=1:length(ex.args)
    ex.args[i] = get(ccall_dict, ex.args[i], ex.args[i])  # get the new argument type symbol from dictionary
  end                                                      # use existing type of no key found

  return nothing
end
   
    
##### functions to make consts into global consts #####

function make_global_const(ex)

  @assert ex.head == :const
 
  val = ex.args[1]  # get the assignment
  ex.args[1] = :(global $val)  # make it global

end


function make_typealias_void(ex)

  @assert ex.head == :typealias

  if typeof(ex.args[2]) == Expr  # this a compound type declaration
    ex2 = ex.args[2]
    @assert ex2.head == :curly
    str = string(ex2.args[2])  # get the pointee type name
    println("str = ", str)
    println("str[1] = ", str[1])
    if str[1] == '_'  # if it begin with an underscore
      println("rewriting typealias")
      ex2.args[2] = :Void  # make it a Ptr{Void}
      println("ex2 = ", ex2)
      println("ex = ", ex)
    end
    # else do nothing
  end
end


