#' Plot formant object
#'
#' Function for plotting formant objects called by [praatpicture]. Instead of
#' using this function directly, just use
#' `praatpicture('my_sound_file', frames='formant')`.
#'
#' @param fm Formant object loaded using [rPraat::formant.read] or similar
#' object, i.e. a `list` object containing the elements `t` (time),
#' `frequencyArray` (array or formant frequencies with number of rows
#' corresponding to the length of `t`), `maxnFormants` (integer giving the
#' number of formants), `intensityVector` (vector of intensity values of
#' the same length as `t`), and `conv2db` (logical).
#' @param start Start time (in seconds) of desired plotted area.
#' @param end End time (in seconds) of desired plotted area.
#' @param tfrom0 Logical; should time on the x-axis run from 0 or from the
#' original time? Default is `TRUE`.
#' @param tgbool Logical; should dotted lines be plotted corresponding to
#' locations in a TextGrid? Default is `FALSE`.
#' @param lines Numeric vector giving locations in seconds of locations from
#' a TextGrid to be plotted with dotted lines. Default is `NULL`.
#' @param focusTierColor String or vector of strings giving the color(s) to
#' use for plotting focus tier lines. If multiple tiers are focused, a vector
#' of the same length can be passed, and the nth tier will be plotted in the
#' nth color. Default is `'black'`.
#' @param focusTierLineType String or vector of strings giving the line
#' type(s) for plotting focus tier lines. If multiple tiers are focused, a
#' vector of the same length can be passed, and the nth tier will be plotted in
#' the nth line type. Default is `'dotted'`.
#' @param dynamicRange Dynamic range in dB for producing formant plots.
#' When a formant plot of `plotType='speckle'` is drawn, no formants are
#' shown in frames with intensity level `dynamicRange` below the maximum
#' intensity. Default is `30`. If set to `0`, all formants are shown.
#' @param freqRange Vector of two integers giving the frequency range to be
#' used for producing formant plots. Default is `c(0,5500)`.
#' @param plotType String giving the type of formant plot to produce;
#' default is `speckle` (a point plot), the only other option is `draw` (a line
#' plot). Alternatively a vector `c('draw','speckle')` can be passed, in which
#' case both are used.
#' @param color String or vector of strings giving the name(s) of
#' colors to be used for plotting formants. If one color is provided, all
#' formants will be plotted in this color. If multiple colors are provided,
#' different formants will be shown in different colors. Default is `'black'`.
#' @param ind Integer indexing formants relative to other plot components.
#' Default is `NULL`.
#' @param min_max_only Logical; should only minimum and maximum values be given
#' on the y-axis? Default is `FALSE`. Can also be a logical vector if some but
#' not all plot components should have minimum and maximum values on the y-axis.
#' Ignored for TextGrid component.
#' @param highlight Named list giving parameters for differential
#' highlighting of formants based on the time domain. This list
#' should contain information about which parts of the plot to highlight, either
#' done with the `start` and `end` arguments which must be numbers or numeric
#' vectors, or using the `tier` and `label` arguments to highlight based on
#' information in a plotted TextGrid. Further contains the optional arguments
#' `color` (string or vector of strings, see `color`),
#' `drawSize` or `speckleSize` (both numeric), and `background`
#' (a string specifying a background color). Can be nested lists to highlight
#' different parts of a figure in different ways.
#' @param axisLabel String giving the name of the label to print along the
#' y-axis when plotting formants. Default is `Frequency (Hz)`.
#' @param drawSize Number indicating the line width if
#' `plotType` is `'draw'`. Default is `1`. Controls the `lwd` argument of
#' [graphics::lines].
#' @param speckleSize Number indicating the point size of if `_plotType` is
#' `'speckle'`. Default is `1`. Controls the `cex` arguments of
#' [graphics::points].
#' @param suppressAxes Should all axes and the box around plots be suppressed?
#' Default is `FALSE`. Generally not recommended.
#'
#' @return No return values, called internally by [praatpicture] and sibling
#' functions.
#' @export
#'
#' @examples
#' # Don't use directly
#' datapath <- system.file('extdata', package='praatpicture')
#' soundFile <- paste0(datapath, '/1.wav')
#' praatpicture(soundFile, frames='formant')
formantplot <- function(fm, start, end, tfrom0=TRUE, tgbool=FALSE, lines=NULL,
                        focusTierColor='black', focusTierLineType='dotted',
                        dynamicRange=30, freqRange=c(0,5500),
                        plotType='speckle', color='black',
                        ind=NULL, min_max_only=FALSE, highlight=NULL,
                        axisLabel='Frequency (Hz)', drawSize=1, speckleSize=1,
                        suppressAxes=FALSE) {

  if (!min_max_only[ind]) {
    if (ind == 1) {
      yax <- 's'
    } else {
      ytix <- grDevices::axisTicks(freqRange, log=F)
      ytix <- ytix[-length(ytix)]
      yax <- 'n'
    }
  } else {
    yax <- 'n'
    ytix <- freqRange
  }

  nf <- fm$maxnFormants
  if (length(color) == 1) color <- rep(color, nf)

  if (tfrom0) {
    fm$t <- fm$t - start
    start <- 0
  }

  if (fm$conv2db) {
    db <- gsignal::pow2db(fm$intensityVector)
  } else {
    db <- fm$intensityVector
  }
  if (dynamicRange != 0) {
    subdr <- which(db < max(db)-dynamicRange)
    if (length(subdr) == 0) subdr <- 1
  } else {
    subdr <- 1
  }

  create_highlighting <- function(highlight, fm, nf, dynamicRange) {
    hl_t <- 0
    if (nf > 1) {
      hl_f <- array(rep(NA, nf), dim = c(nf,1))
    } else {
      hl_f <- NA
    }
    hl_i <- c()
    for (int in 1:length(highlight$start)) {
      times <- which(fm$t > highlight$start[int] & fm$t < highlight$end[int])
      hl_t <- c(hl_t, fm$t[times], max(fm$t[times] + 0.0001))
      if (nf > 1) {
        hl_f <- cbind(hl_f, fm$frequencyArray[,times], rep(NA, nf))
      } else {
        hl_f <- c(hl_f, fm$frequencyArray[times], NA)
      }
      hl_i <- c(hl_i, db[times], 0)
    }
    if (any(highlight$start < start)) {
      highlight$start[which(highlight$start < start)] <- start
    }
    if (any(highlight$end > end)) {
      highlight$end[which(highlight$end > end)] <- end
    }

    if (dynamicRange != 0) {
      hsubdr <- which(hl_i < max(db)-dynamicRange)
      if (length(hsubdr) == 0) hsubdr <- 1
    } else {
      hsubdr <- 1
    }

    if (!'color' %in% names(highlight)) highlight$color <- color
    if (!'drawSize' %in% names(highlight)) highlight$drawSize <- drawSize
    if (!'speckleSize' %in% names(highlight)) highlight$speckleSize <-
      speckleSize
    if (length(highlight$color) == 1) highlight$color <-
      rep(highlight$color, nf)
    return(list(params = highlight, hl_t = hl_t, hl_f = hl_f, subdr = hsubdr))
  }

  s <- freqRange[1]:freqRange[2]
  freql <- s[s %% 1000 == 0]

  if ('draw' %in% plotType) {
    if (nf > 1) {
      plot(fm$t, fm$frequencyArray[1,], xlim=c(start, end+start),
           xaxt='n', ylim=freqRange, yaxt=yax, type='l', col=color[1],
           lwd=drawSize)

      for (i in 2:nf) {
        graphics::lines(fm$t, fm$frequencyArray[i,], col=color[i], lwd=drawSize)
      }
    } else {
      plot(fm$t, fm$frequencyArray, xlim=c(start, end+start),
           xaxt='n', ylim=freqRange, yaxt=yax, type='l', col=color[1],
           lwd=drawSize)
    }
    if (!suppressAxes) {
      if (!min_max_only[ind] & ind != 1) graphics::axis(2, at=ytix)
      if (min_max_only[ind]) graphics::axis(2, at=ytix, padj=c(0,1), las=2,
                                            tick=F)
    }

    if ('speckle' %in% plotType) {
      if (nf > 1) {
        graphics::points(fm$t[-subdr], fm$frequencyArray[1,-subdr], pch=20,
                         col=color[1], cex=speckleSize)
        for (i in 2:nf) {
          graphics::points(fm$t[-subdr], fm$frequencyArray[i,-subdr], pch=20,
                           col=color[i], cex=speckleSize)
        }
      } else {
        graphics::points(fm$t[-subdr], fm$frequencyArray[-subdr], pch=20,
                         col=color[1], cex=speckleSize)
      }

    }

    if (!is.null(highlight)) {
      if (any(sapply(highlight, is.list))) {
        highlightLoopDepth <- sum(sapply(highlight, is.list))
      } else {
        highlightLoopDepth <- 1
      }
      for (i in 1:highlightLoopDepth) {
        if (any(sapply(highlight, is.list))) {
          thisHL <- create_highlighting(highlight[[i]], fm, nf, dynamicRange)
        } else {
          thisHL <- create_highlighting(highlight, fm, nf, dynamicRange)
        }
        if ('background' %in% names(thisHL$params)) {
          graphics::rect(thisHL$params$start,
                         freqRange[1] - freqRange[2] * 2,
                         thisHL$params$end,
                         freqRange[2] + freqRange[2] * 2,
                         col = thisHL$params$background, border = NA)
        }
        if (nf > 1) {
          graphics::lines(thisHL$hl_t, thisHL$hl_f[1,],
                          col=thisHL$params$color[1],
                          lwd=thisHL$params$drawSize)
          for (j in 2:nf) {
            graphics::lines(thisHL$hl_t, thisHL$hl_f[j,],
                            col=thisHL$params$color[j],
                            lwd=thisHL$params$drawSize)
          }
        } else {
          graphics::lines(thisHL$hl_t, thisHL$hl_f, col=thisHL$params$color[1],
                          lwd=thisHL$params$drawSize)
        }

        if ('speckle' %in% plotType) {
          if (nf > 1) {
            graphics::points(thisHL$hl_t[-thisHL$subdr],
                             thisHL$hl_f[1,-thisHL$subdr],
                             pch=20, col=thisHL$params$color[1],
                             cex=thisHL$params$speckleSize)
            for (j in 2:nf) {
              graphics::points(thisHL$hl_t[-thisHL$subdr],
                               thisHL$hl_f[j,-thisHL$subdr],
                               pch=20, col=thisHL$params$color[j],
                               cex=thisHL$params$speckleSize)
            }
          } else {
            graphics::points(thisHL$hl_t[-thisHL$subdr],
                             thisHL$hl_f[-thisHL$subdr], pch=20,
                             col=thisHL$params$color[1],
                             cex=thisHL$params$speckleSize)
          }
        }
      }
    }

    if (tgbool) {
      for (i in 1:length(lines)) {
        graphics::abline(v=lines[[i]], col=focusTierColor[i],
                         lty=focusTierLineType[i])
      }
    }

  }

  if (length(plotType) == 1) {
    if (plotType == 'speckle') {
      if (nf > 1) {
        plot(fm$t[-subdr], fm$frequencyArray[1,-subdr], pch=20,
             xlim=c(start, end+start), xaxt='n',
             ylim=freqRange, yaxt=yax, col=color[1], cex=speckleSize)
        for (i in 2:nf) {
          graphics::points(fm$t[-subdr], fm$frequencyArray[i,-subdr], pch=20,
                           col=color[i], cex=speckleSize)
        }
      } else {
        plot(fm$t[-subdr], fm$frequencyArray[-subdr], pch=20,
             xlim=c(start, end+start), xaxt='n',
             ylim=freqRange, yaxt=yax, col=color[1], cex=speckleSize)
      }

      if (!suppressAxes) {
        if (!min_max_only[ind] & ind != 1) graphics::axis(2, at=ytix)
        if (min_max_only[ind]) graphics::axis(2, at=ytix, padj=c(0,1), las=2,
                                              tick=F)
      }

      if (!is.null(highlight)) {
        if (any(sapply(highlight, is.list))) {
          highlightLoopDepth <- sum(sapply(highlight, is.list))
        } else {
          highlightLoopDepth <- 1
        }
        for (i in 1:highlightLoopDepth) {
          if (any(sapply(highlight, is.list))) {
            thisHL <- create_highlighting(highlight[[i]], fm, nf, dynamicRange)
          } else {
            thisHL <- create_highlighting(highlight, fm, nf, dynamicRange)
          }
          if ('background' %in% names(thisHL$params)) {
            graphics::rect(thisHL$params$start,
                           freqRange[1] - freqRange[2] * 2,
                           thisHL$params$end,
                           freqRange[2] + freqRange[2] * 2,
                           col = thisHL$params$background, border = NA)
          }
          if (nf > 1) {
            graphics::points(thisHL$hl_t[-thisHL$subdr],
                             thisHL$hl_f[1,-thisHL$subdr],
                             pch=20, col=thisHL$params$color[1],
                             cex=thisHL$params$speckleSize)
            for (j in 2:nf) {
              graphics::points(thisHL$hl_t[-thisHL$subdr],
                               thisHL$hl_f[j,-thisHL$subdr],
                               pch=20, col=thisHL$params$color[j],
                               cex=thisHL$params$speckleSize)
            }
          } else {
            graphics::points(thisHL$hl_t[-thisHL$subdr],
                             thisHL$hl_f[-thisHL$subdr], pch=20,
                             col=thisHL$params$color[1],
                             cex=thisHL$params$speckleSize)
          }
        }


      }

      if (tgbool) {
        for (i in 1:length(lines)) {
          graphics::abline(v=lines[[i]], col=focusTierColor[i],
                           lty=focusTierLineType[i])
        }
      }

    }
  }

  if (!suppressAxes) graphics::mtext(axisLabel, side=2, line=3.5, cex=0.8)

}
