macro(specialize Name Dim)
  set(Name ${ARGV0})
  string(TOUPPER ${Name} NAME)
  string(TOLOWER ${Name} name)
  set(Dim ${ARGV1})
  math(EXPR DimMax "${Dim} - 1")
  foreach(i RANGE 0 ${DimMax})
    if(i)
      set(SizeArgDecl "${SizeArgDecl}, size_t size${i}")
      set(SizeArgs    "${SizeArgs}, size${i}")
      set(OfsArgDecl  "${OfsArgDecl}, size_t ofs${i}")
      set(OfsArgs     "${OfsArgs}, ofs${i}")
    else(i)
      set(SizeArgDecl "size_t size${i}")
      set(SizeArgs    "size${i}")
      set(OfsArgDecl  "size_t ofs${i}")
      set(OfsArgs     "ofs${i}")
    endif(i)
  endforeach(i)
  configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${specdim_template}.hpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/specdim_${name}${Dim}d.hpp")
  set(HEADERS "${HEADERS}#include \"specdim_${name}${Dim}d.hpp\"\n")
endmacro(specialize)

macro(specialize_dims Name Base Ptr)
  set(Name ${ARGV0})
  set(Base ${ARGV1})
  string(TOUPPER ${Name} NAME)
  string(TOLOWER ${Name} name)
  set(HEADERS "")
  if(${ARGV2} EQUAL 1)
    set(Pointer "")
  else(${ARGV2} EQUAL 1)
    set(Pointer "// ")
  endif(${ARGV2} EQUAL 1)
  specialize(${ARGV0} 1)
  specialize(${ARGV0} 2)
  specialize(${ARGV0} 3)
  file(WRITE "specdim_${name}.hpp" ${HEADERS})
  configure_file("${CMAKE_CURRENT_SOURCE_DIR}/copy.hpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/copy_${name}.hpp")
endmacro(specialize_dims)

macro(specialize_vector Name Type TemplArg)
  set(Name ${ARGV0})
  set(Type ${ARGV1})
  set(TemplArg ${ARGV2})
  string(TOUPPER ${Name} NAME)
  string(TOLOWER ${Name} name)
  if(TemplArg)
    set(TemplArg2 "${TemplArg}, ")
    set(TemplArg "class ${TemplArg}")
  else(TemplArg)
    set(TemplArg2 "")
  endif(TemplArg)
  set(HEADERS "")
  foreach(i RANGE 1 3)
    set(Dim ${i})
    math(EXPR im1 "${i} - 1")
    if(im1)
      set(VecArgsDecl "${VecArgsDecl}, ${Type} x${im1}")
      set(VecArgs     "${VecArgs}, x${im1}")
      set(VecArgsInit "${VecArgsInit} (*this)[${im1}] = x${im1};")
    else(im1)
      set(VecArgsDecl "${Type} x${im1}")
      set(VecArgs     "x${im1}")
      set(VecArgsInit "(*this)[${im1}] = x${im1};")
    endif(im1)
    set(filename "specdim_vector_${name}${i}.hpp")
    configure_file("${CMAKE_CURRENT_SOURCE_DIR}/specdim_vector.hpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/${filename}")
    set(HEADERS "${HEADERS}#include \"${filename}\"\n")
  endforeach(i)
  file(WRITE "specdim_vector_${name}.hpp" ${HEADERS})
endmacro(specialize_vector)

macro(specialize_plan type1 type2 Dim)
  set(type1 ${ARGV0})
  set(type2 ${ARGV1})
  set(Dim ${ARGV2})
  string(TOUPPER ${type1} TYPE1)
  string(TOUPPER ${type2} TYPE2)
  string(SUBSTRING ${TYPE1} 0 1 T1)
  string(SUBSTRING ${TYPE2} 0 1 T2)
  set(TYPE "${T1}2${T2}")
  math(EXPR DimMax "${Dim} - 1")
  foreach(i RANGE 0 ${DimMax})
    if(i)
      set(size_args_decl  "${size_args_decl}, size_t size${i}")
      set(size_args       "${size_args}, size${i}")
      set(size_args_array "${size_args_array}, size[${i}]")
    else(i)
      set(size_args_decl  "size_t size${i}")
      set(size_args       "size${i}")
      set(size_args_array "size[${i}]")
    endif(i)
  endforeach(i)

  # provide "batch" argument in one-dimensional case:
  if(${Dim} EQUAL 1)
    set(batch_arg ", batch")
    set(batch_arg_decl ", int batch = 1")
    set(batch_arg_doc "@param batch number of 1D transforms")
  else(${Dim} EQUAL 1)
    set(batch_arg "")
    set(batch_arg_decl "")
    set(batch_arg_doc "")
  endif(${Dim} EQUAL 1)

  # provide "dir" argument in complex-complex case:
  if(${type1} STREQUAL ${type2})
    set(dir_arg ", dir")
    set(dir_arg_decl ", int dir")
    set(dir_arg_doc "@param dir transform direction: CUFFT_FORWARD or CUFFT_INVERSE")
  else(${type1} STREQUAL ${type2})
    set(dir_arg "")
    set(dir_arg_decl "")
    set(dir_arg_doc "")
  endif(${type1} STREQUAL ${type2})

  configure_file("${CMAKE_CURRENT_SOURCE_DIR}/plan.hpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/plan_${type1}_${type2}_${Dim}d.hpp")
endmacro(specialize_plan)

macro(create_code name)
  set(name ${ARGV0})
  set(exe code_creator_${name})
  add_executable(${exe} ${name}.cpp)
  add_custom_command(OUTPUT ${name}.hpp COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${exe} > ${name}.hpp DEPENDS ${exe})
  add_custom_target(create_${name} ALL DEPENDS ${name}.hpp)
endmacro()

set(specdim_template specdim)
specialize_dims(DeviceMemoryLinear DeviceMemoryStorage 1)
specialize_dims(DeviceMemoryPitched DeviceMemoryStorage 1)
specialize_dims(HostMemoryHeap HostMemoryStorage 1)
specialize_dims(HostMemoryLocked HostMemoryStorage 1)
specialize_dims(Symbol Layout 1)
specialize_dims(Array Storage 0)

set(specdim_template specdim_opengl)
specialize_dims(Texture Storage 1)

set(ConstrExtraArgDecl ", GLenum t = GL_ARRAY_BUFFER, GLenum u = GL_STATIC_DRAW")
set(ConstrExtraArgs ", t, u")
specialize_dims(BufferObject DeviceMemoryStorage 1)

set(specdim_template specdim_reference)
specialize_dims(DeviceMemoryReference DeviceMemory 1)
specialize_dims(HostMemoryReference HostMemory 1)
specialize_dims(GilReference HostMemoryReference 1)
specialize_dims(ItkReference HostMemoryReference 1)

set(specdim_template specdim_iplreference)
specialize_dims(IplReference HostMemoryReference 1)

specialize_vector("Vector" "Type" "Type")
specialize_vector("Size" "size_t" "")
specialize_vector("SSize" "ssize_t" "")

specialize_plan(real    complex 1)
specialize_plan(complex real    1)
specialize_plan(complex complex 1)
specialize_plan(real    complex 2)
specialize_plan(complex real    2)
specialize_plan(complex complex 2)
specialize_plan(real    complex 3)
specialize_plan(complex real    3)
specialize_plan(complex complex 3)

create_code(pack)
