package imagefilters

import java.awt.Color

/**
 * This class implements the various image filters
 */
object ImageFilters {

  def filter(src: Array[Array[Int]], func: (Int, Int, Int, Int, Int) => Int): Array[Array[Int]] = {
    val height: Int = src.length
    val width: Int = if (height == 0) 0 else src(0).length
    val dst: Array[Array[Int]] = Array.ofDim(height, width)
    for (y: Int <- 0 until height) {
      for (x: Int <- 0 until width) {
        dst(y)(x) = func(src(y)(x), x, y, width, height)
      }
    }
    return dst
  }
  def filter(src: Array[Array[Int]], func: (Int) => Int): Array[Array[Int]] = filter(src, (value, x, y, width, height) => func(value))
  def colorFilter(src: Array[Array[Color]], func: (Color, Int, Int, Int, Int) => Color): Array[Array[Color]] = {
    val height: Int = src.length
    val width: Int = if (height == 0) 0 else src(0).length
    val dst: Array[Array[Color]] = Array.ofDim(height, width)
    for (y: Int <- 0 until height) {
      for (x: Int <- 0 until width) {
        dst(y)(x) = func(src(y)(x), x, y, width, height)
      }
    }
    return dst
  }
  def colorFilter(src: Array[Array[Color]], func: (Color) => Color): Array[Array[Color]] = colorFilter(src, (value, x, y, width, height) => func(value))

  def duplicate(a: Array[Array[Int]]): Array[Array[Int]] = filter(a, (value) => value)
  def threshold(a: Array[Array[Int]], thresh: Int): Array[Array[Int]] = filter(a, (value) => if (value > thresh) 255 else 0)
  def mean(a: Array[Array[Int]]): Array[Array[Int]] = filter(a, (value, x, y, width, height) => {
    if (x == 0 || x == width - 1 || y == 0 || y == height - 1) value
    else {
      var avg: Double = 0
      for (dy: Int <- -1 to 1) {
        for (dx: Int <- -1 to 1) {
          avg += a(y+dy)(x+dx)
        }
      }
      (avg/9.0).toInt
    }
  })
  def mean(a: Array[Array[Int]], radius: Int): Array[Array[Int]] = {
    if (radius < 0) throw new IllegalArgumentException("radius must be >= 0")
    val diameter: Int = 2 * radius + 1

    filter(a, (value, x, y, width, height) => {
      if (x <= radius - 1 || x >= width - radius || y <= radius - 1 || y >= height - radius) value
      else {
        var avg: Double = 0
        for (dy: Int <- -radius to radius) {
          for (dx: Int <- -radius to radius) {
            avg += a(y+dy)(x+dx)
          }
        }
        (avg/(diameter * diameter)).toInt
      }
    })
  }

  def edges(a: Array[Array[Int]]): Array[Array[Int]] = filter(a, (value, x, y, width, height) => {
    if (x == 0 || x == width - 1 || y == 0 || y == height - 1) value
    else {
      val dx: Int = a(y)(x+1) - a(y)(x-1)
      val dy: Int = a(y+1)(x) - a(y-1)(x)
      Math.sqrt(dx*dx + dy*dy).toInt
    }
  })

  def sobel(a: Array[Array[Int]], intensityFactor: Double): Array[Array[Int]] = filter(a, (value, x, y, width, height) => {
    if (x == 0 || x == width - 1 || y == 0 || y == height - 1) value
    else {
      val dx: Int = a(y-1)(x+1) + 2*a(y)(x+1) + a(y+1)(x+1) - a(y-1)(x-1) - 2*a(y)(x-1) - a(y+1)(x-1)
      val dy: Int = a(y+1)(x-1) + 2*a(y+1)(x) + a(y+1)(x+1) - a(y-1)(x-1) - 2*a(y-1)(x) - a(y-1)(x+1)
      (Math.sqrt(dx*dx + dy*dy)*intensityFactor).toInt
    }
  })

  def noise(a: Array[Array[Int]], intensity: Double): Array[Array[Int]] = filter(a, (value) => {
    Math.max(0, Math.min(255, value + (Math.random()*2-1)*intensity)).toInt
  })

  def mask(a: Array[Array[Color]], maskImg: Array[Array[Int]]): Array[Array[Color]] = {
    val maskHeight: Int = maskImg.length
    val maskWidth: Int = if (maskHeight == 0) 0 else maskImg(0).length

    colorFilter(a, (col, x, y, width, height) => {
      if (x >= maskWidth || y >= maskHeight) col
      else {
        val factor: Double = maskImg(y)(x)/255.0
        new Color(
          (col.getRed * factor).toInt,
          (col.getGreen * factor).toInt,
          (col.getBlue * factor).toInt
        )
      }
    })
  }
}
