# file for misc loopset transforms

function hoist_constant_memory_accesses!(ls::LoopSet)
  hoist_stores = false
  for op ∈ operations(ls)
    if isload(op)
      length(getindicesonly(op)) == 0 && host_constant_load!(ls, op)
    elseif isstore(op) && iszero(length(getindicesonly(op)))
      hoist_stores = true
    end
  end
  hoist_stores && return hoist_constant_memory_accesses_nocheck!(ls)
  ls.preamble
end

function hoist_constant_memory_accesses_nocheck!(ls::LoopSet)
  post = Expr(:block)
  for op ∈ operations(ls)
    if isstore(op) && length(getindicesonly(op)) == 0
      hoist_constant_store!(post, ls, op)
    end
  end
  post
end
function hoist_constant_vload!(ls::LoopSet, op::Operation)
  op.instr = LOOPCONSTANT
  op.node_type = constant
  add_constant_vload!(ls, op, ArrayReferenceMetaPosition(op.ref, parents(op), loopdependencies(op), reduceddependencies(op), name(op)), elementbytes)
end

function return_empty_reductinit(op::Operation, var::Symbol)
  for (i,opp) ∈ enumerate(parents(op))
    if (name(opp) === var) && (length(reduceddependencies(opp)) == 0) && (length(loopdependencies(opp)) == 0) && (length(children(opp)) == 1)
      return opp
    end
    opcheck = return_empty_reductinit(opp, var)
    opcheck === opp || return opcheck
  end
  return op
end



function constant_symbol!(ls::LoopSet, op::Operation)
  # hack
  # relowers, but should make it work
  # TODO: DRY with `lower_licm_constants!` from `src/codegen/lower_constants.jl`
  skip_constant(instruction(op)) || return instruction(op).instr
  idcheck = identifier(op)
  symname = constantopname(op)
  for (id, sym) ∈ ls.preamble_symsym
    (idcheck ≢ nothing) && ((idcheck == id) && continue)
    pushpreamble!(ls, Expr(:(=), symname, sym))
    return symname
    # setconstantop!(ls, op,  sym)
    # setconstantop!(ls, op, Expr(:call, lv(:maybeconvert), ls.T, sym))
  end
  for (id,(intval,intsz,signed)) ∈ ls.preamble_symint
    (idcheck ≢ nothing) && ((idcheck == id) && continue)
    if intsz == 1
      pushpreamble!(ls, Expr(:(=), symname, intval % Bool))
    else
      pushpreamble!(ls, Expr(:(=), symname, sizeequivalent_symint_expr(intval, signed)))
    end
    return symname
  end
  for (id,floatval) ∈ ls.preamble_symfloat
    (idcheck ≢ nothing) && ((idcheck == id) && continue)
    pushpreamble!(ls, Expr(:(=), symname, Expr(:call, lv(:sizeequivalentfloat), ELTYPESYMBOL, floatval)))
    return symname
  end
  for (id,typ) ∈ ls.preamble_zeros
    (idcheck ≢ nothing) && ((idcheck == id) && continue)
    instruction(op) === LOOPCONSTANT || continue
    if typ == IntOrFloat
      pushpreamble!(ls, Expr(:(=), symname, Expr(:call, :zero, ELTYPESYMBOL)))
    elseif typ == HardInt
      pushpreamble!(ls, Expr(:(=), symname, Expr(:call, lv(:zerointeger), ELTYPESYMBOL)))
    else#if typ == HardFloat
      pushpreamble!(ls, Expr(:(=), symname, Expr(:call, lv(:zerofloat), ELTYPESYMBOL)))
    end
    return symname
  end
  for (id,f) ∈ ls.preamble_funcofeltypes
    (idcheck ≢ nothing) && ((idcheck == id) && continue)
    pushpreamble!(ls, Expr(:(=), symname, Expr(:call, reduction_zero(f), ELTYPESYMBOL)))
    return symname
  end
  throw("Constant operation symbol not found.")
end

function hoist_constant_store!(q::Expr, ls::LoopSet, op::Operation)
  op.instruction = DROPPEDCONSTANT
  op.node_type = constant

  opr = only(parents(op))
  while opr.instruction.instr === :identity
    opr.instruction = DROPPEDCONSTANT
    opr.node_type = constant
    opr = only(parents(opr))
  end
  push!(ls.outer_reductions, identifier(opr))
  
  initop = return_empty_reductinit(opr, name(opr))
  # @show last(ls.preamble.args)
  init = constant_symbol!(ls, initop)
  # @show last(ls.preamble.args)
  pushpreamble!(ls, Expr(:(=), outer_reduct_init_typename(opr), Expr(:call, lv(:typeof), init)))
  qpre = Expr(:block)
  push!(q.args, Expr(:call, lv(:unsafe_store!), Expr(:call, lv(:pointer), op.ref.ptr), outer_reduction_to_scalar_reduceq!(qpre, opr, init)))
  length(qpre.args) == 0 || pushpreamble!(ls, qpre) # creating `Expr` and pushing because `outer_reduction_to_scalar_reduceq!` uses `pushfirst!(q.args`, and we don't want it at the start of the preamble
  return nothing
end

