ext_tam = function(resobj, pp=NULL, debug=TRUE){

  if (debug) message("Entering TAM-extractor...")

  if (min(resobj$resp) > 0) warning(paste("Responses must be coded 0..k for TAM.\n  Current code range is",paste(range(resobj$resp,na.rm=TRUE),collapse="-"),"\n  Estimates might be wrong/misleading. Check your data!"))

# ...grepl(".2pl",as.character(resobj$CALL)[1],fixed=TRUE)

  D = dim(resobj$B)[3]
  dims_list = vector(mode="list", length=D) 
  names(dims_list) = paste0("F",1:D)

  if (is.character(pp) && pp=="auto") {
     pp = cbind(resobj$person[,seq(6,ncol(resobj$person),2)]) # TODO specify global pp
  } else {
     #stopifnot(inherits(pp,"..."))
  }


  for(d in 1:D){
    all_alphas = resobj$B[,2,d]
    valid_itm  = all_alphas != 0
    alpha      = all_alphas[valid_itm]
    dd         = rbind(apply(resobj$AXsi_[valid_itm,], 1, diff))
    TT         = sweep(dd, 2, alpha, FUN="/")

#   colnames(TT) = rownames(resobj$item) # upd 2023-07-26: no rownames with 3PL
    colnames(TT) = resobj$item$item[resobj$item[,4+resobj$maxK-1+d] != 0] # extract names for the valid dimension.  2024-04-06

    if(is.null(resobj$guess)) gg = rep(0, ncol(TT))
    else                      gg = resobj$guess
    
    # prep for SE_par (for now only empty container)
    
    se.thresholds = t(matrix(NA, dim(TT)[1], dim(TT)[2])) # resobj$xsi$se.xsi # caution: SE of cumulative intercepts+alpha
    se.discrimination = matrix(NA, 1, length(alpha))
    se.guessing = rep(NA, ncol(TT))
    se.laziness = rep(NA, ncol(TT))
    
    dims_list[[d]]$thresholds     = TT
    dims_list[[d]]$discrimination = rbind(alpha)        # check for NRM
    dims_list[[d]]$guessing       = gg
    dims_list[[d]]$laziness       = rep(1, ncol(TT))
    dims_list[[d]]$model          = rep(resobj$irtmodel, ncol(TT))
    dims_list[[d]]$person_par     = pp[,d]
    dims_list[[d]]$SE_par         = list(se.thresholds=se.thresholds,
                                         se.discrimination=se.discrimination,
                                         se.guessing=se.guessing,
                                         se.laziness=se.laziness)

  } # end for d in D

  dims_list$freq = apply(resobj$resp,2,table,simplify=FALSE)
  data = resobj$resp

  attr(dims_list,"source") = paste0("TAM/",resobj$irtmodel)
  attr(dims_list,"data")   = data

  if (debug) message("Leaving TAM-extractor...")

  return(dims_list)
}# end of ext_tam

# --------------------------------------------------------------- from ?tam.mml:
# Option 'irtmodel':
# For fixed item slopes (in tam.mml) options include
# * PCM (partial credit model),
# * PCM2 (partial credit model with ConQuest parametrization 'item+item*step' and
# * RSM (rating scale model; the ConQuest parametrization 'item+step').
#
# For estimated item slopes (only available in tam.mml.2pl) options are
# * 2PL (all slopes of item categories are estimated; Nominal Item Response Model),
# * GPCM (generalized partial credit model in which every item gets one and only slope parameter per dimension) and
# * 2PL.groups or GPCM.groups (subsets of items get same item slope estimates)
#   and a design matrix E on item slopes in the generalized partial credit model (GPCM.design, see Examples).
#
# Note that item slopes can not be estimated with faceted designs using the function tam.mml.mfr.
# However, it is easy to use pre-specified design matrices and apply some restrictions to tam.mml.2pl (see Example 14, Model 3).