Code Sketch


yadnesh
By: Mhalsakant School
Category: Programming
//scala
import java.awt.BasicStroke
import java.awt.BorderLayout
import java.awt.Color
import java.awt.Cursor
import java.awt.Dimension
import java.awt.FlowLayout
import java.awt.Font
import java.awt.GradientPaint
import java.awt.Graphics
import java.awt.Graphics2D
import java.awt.Image
import java.awt.RenderingHints
import java.awt.event._
import java.awt.geom.AffineTransform
import java.awt.geom.Path2D
import java.awt.image.BufferedImage
import java.io.File
import javax.imageio.ImageIO

import javax.swing._
import javax.swing.border.EmptyBorder

// ============================================================
//        ULTRA LEGEND SMART DRAW STUDIO X - FUTURE EDITION
// ============================================================
// OFFLINE FUTURISTIC DRAWING / DESIGN STUDIO
//
// FEATURES
// ------------------------------------------------------------
// PENCIL / ERASER / SPRAY / LINE / RECT / ELLIPSE
// FREEHAND POLYGON / BRUSH / HIGHLIGHTER
// 100+ SHAPES
// 100+ OBJECT COMMANDS
// SMART COMMAND / AI-STYLE OFFLINE ASSISTANT
// IMAGE IMPORT
// IMAGE EXPORT
// TEXT
// GRADIENT BACKGROUNDS
// NEON / MAGIC / GOLD / CYBER EFFECTS
// GRID
// SNAP
// MIRROR
// ROTATE
// SCALE
// FLIP
// ZOOM
// PARTICLES
// STARFIELD
// ANIMATION PREVIEW
// UNDO / REDO HISTORY
// LAYERS
// COLOR PICKER
// CUSTOM BACKGROUND
// SECRET HOTKEY SYSTEM
// FULLSCREEN
// ============================================================


// ============================================================
// CONSTANTS
// ============================================================

val W = 1200
val H = 700

val CX = W / 2
val CY = H / 2


// ============================================================
// GLOBAL STATE
// ============================================================

var currentColor = new Color(20, 30, 40)
var currentTool = "PENCIL"
var brushSize = 6.0f

var zoom = 1.0
var rotation = 0.0

var gridOn = false
var snapOn = false
var shadowOn = false
var outlineOn = false
var neonOn = false
var particlesOn = false
var starfieldOn = false

var currentBackground = "SKY"
var currentStyle = "NORMAL"

var mouseDown = false
var startX = 0
var startY = 0
var lastX = 0
var lastY = 0

var selectedItem = ""

var frame: JFrame = null
var canvas: JPanel = null
var status: JLabel = null

var searchField: JTextField = null
var commandField: JTextField = null
var textField: JTextField = null

var itemModel = new DefaultListModel[String]()
var itemList: JList[String] = null

var undoStack =
  scala.collection.mutable.Stack[BufferedImage]()

var redoStack =
  scala.collection.mutable.Stack[BufferedImage]()

var animationRunning = false
var animationTick = 0

var importedImage: BufferedImage = null


// ============================================================
// IMAGE
// ============================================================

val image =
  new BufferedImage(
    W,
    H,
    BufferedImage.TYPE_INT_ARGB
  )

val g =
  image.createGraphics()

g.setRenderingHint(
  RenderingHints.KEY_ANTIALIASING,
  RenderingHints.VALUE_ANTIALIAS_ON
)

g.setRenderingHint(
  RenderingHints.KEY_RENDERING,
  RenderingHints.VALUE_RENDER_QUALITY
)

g.setColor(Color.WHITE)
g.fillRect(0, 0, W, H)


// ============================================================
// BASIC HELPERS
// ============================================================

def statusText(s: String): Unit = {
  if (status != null) status.setText(s)
}

def repaintCanvas(): Unit = {
  if (canvas != null) canvas.repaint()
}

def copyImage(src: BufferedImage): BufferedImage = {

  val dst =
    new BufferedImage(
      W,
      H,
      BufferedImage.TYPE_INT_ARGB
    )

  val cg = dst.createGraphics()

  cg.setComposite(
    java.awt.AlphaComposite.Src
  )

  cg.drawImage(
    src,
    0,
    0,
    null
  )

  cg.dispose()

  dst
}

def remember(): Unit = {

  undoStack.push(
    copyImage(image)
  )

  redoStack.clear()

  while (undoStack.size > 30) {
    undoStack.remove(0)
  }
}

def clearImage(): Unit = {

  g.setTransform(
    new AffineTransform()
  )

  g.setComposite(
    java.awt.AlphaComposite.Src
  )

  g.setColor(
    new Color(255,255,255,255)
  )

  g.fillRect(
    0,
    0,
    W,
    H
  )
}

def restoreImage(src: BufferedImage): Unit = {

  clearImage()

  g.drawImage(
    src,
    0,
    0,
    null
  )

  repaintCanvas()
}

def makeButton(s: String): JButton = {

  val b = new JButton(s)

  b.setFocusable(false)

  b.setFont(
    new Font(
      "Arial",
      Font.BOLD,
      11
    )
  )

  b
}


// ============================================================
// UNDO / REDO
// ============================================================

def undo(): Unit = {

  if (undoStack.nonEmpty) {

    redoStack.push(
      copyImage(image)
    )

    restoreImage(
      undoStack.pop()
    )

    statusText(
      "UNDO"
    )
  }
}

def redo(): Unit = {

  if (redoStack.nonEmpty) {

    undoStack.push(
      copyImage(image)
    )

    restoreImage(
      redoStack.pop()
    )

    statusText(
      "REDO"
    )
  }
}


// ============================================================
// CANVAS MAPPING
// ============================================================

def drawW(): Int =
  math.max(
    1,
    (W * zoom).toInt
  )

def drawH(): Int =
  math.max(
    1,
    (H * zoom).toInt
  )

def canvasLeft(): Int =
  (canvas.getWidth - drawW()) / 2

def canvasTop(): Int =
  (canvas.getHeight - drawH()) / 2

def toImageX(x: Int): Int = {

  val v =
    ((x - canvasLeft()) / zoom).toInt

  math.max(
    0,
    math.min(W - 1, v)
  )
}

def toImageY(y: Int): Int = {

  val v =
    ((y - canvasTop()) / zoom).toInt

  math.max(
    0,
    math.min(H - 1, v)
  )
}

def insideCanvas(
  x: Int,
  y: Int
): Boolean = {

  x >= canvasLeft() &&
  x < canvasLeft() + drawW() &&
  y >= canvasTop() &&
  y < canvasTop() + drawH()
}


// ============================================================
// BACKGROUND
// ============================================================

def bgColors(name: String): Array[Color] = {

  name.toUpperCase match {

    case "SUNSET" =>
      Array(
        new Color(255,120,70),
        new Color(90,30,150)
      )

    case "SUNRISE" =>
      Array(
        new Color(255,180,90),
        new Color(255,240,180)
      )

    case "NIGHT" =>
      Array(
        new Color(8,15,55),
        new Color(55,25,110)
      )

    case "SPACE" =>
      Array(
        new Color(2,4,20),
        new Color(55,5,100)
      )

    case "OCEAN" =>
      Array(
        new Color(80,220,255),
        new Color(5,70,170)
      )

    case "FOREST" =>
      Array(
        new Color(170,235,190),
        new Color(30,115,55)
      )

    case "DESERT" =>
      Array(
        new Color(255,225,150),
        new Color(190,125,60)
      )

    case "MAGIC" =>
      Array(
        new Color(220,185,255),
        new Color(70,25,150)
      )

    case "NEON" =>
      Array(
        new Color(5,5,35),
        new Color(80,0,110)
      )

    case "GOLD" =>
      Array(
        new Color(255,240,150),
        new Color(170,110,20)
      )

    case "ICE" =>
      Array(
        new Color(235,255,255),
        new Color(90,185,240)
      )

    case "VOLCANO" =>
      Array(
        new Color(255,130,40),
        new Color(75,10,20)
      )

    case "CYBER" =>
      Array(
        new Color(3,15,30),
        new Color(0,100,130)
      )

    case "GALAXY" =>
      Array(
        new Color(5,0,35),
        new Color(100,0,150)
      )

    case _ =>
      Array(
        new Color(150,220,255),
        new Color(100,190,120)
      )
  }
}


def drawBackground(name: String): Unit = {

  val c = bgColors(name)

  g.setTransform(
    new AffineTransform()
  )

  g.setPaint(
    new GradientPaint(
      0,
      0,
      c(0),
      0,
      H,
      c(1)
    )
  )

  g.fillRect(
    0,
    0,
    W,
    H
  )

  val n = name.toUpperCase

  if (
    n == "NIGHT" ||
    n == "SPACE" ||
    n == "MAGIC" ||
    n == "GALAXY" ||
    n == "CYBER"
  ) {

    g.setColor(
      Color.WHITE
    )

    var i = 0

    while (i < 180) {

      val x =
        math.abs(
          (i * 97 + 31) % W
        )

      val y =
        math.abs(
          (i * 53 + 17) % H
        )

      val s =
        1 + (i % 4)

      g.fillOval(
        x,
        y,
        s,
        s
      )

      i += 1
    }
  }

  if (
    n == "SKY" ||
    n == "SUNNY" ||
    n == "SUNRISE" ||
    n == "SUNSET"
  ) {

    g.setColor(
      new Color(255,220,80)
    )

    g.fillOval(
      950,
      45,
      110,
      110
    )
  }

  if (n == "OCEAN") {

    g.setColor(
      new Color(10,100,190)
    )

    g.fillRect(
      0,
      510,
      W,
      190
    )

    g.setColor(
      new Color(180,245,255)
    )

    var i = 0

    while (i < 10) {

      g.drawArc(
        i * 140 - 70,
        500,
        180,
        55,
        0,
        180
      )

      i += 1
    }
  }

  if (n == "FOREST") {

    g.setColor(
      new Color(35,125,50)
    )

    g.fillRect(
      0,
      500,
      W,
      200
    )
  }

  if (n == "DESERT") {

    g.setColor(
      new Color(225,175,90)
    )

    g.fillOval(
      -100,
      480,
      650,
      260
    )

    g.fillOval(
      400,
      450,
      800,
      280
    )
  }

  if (n == "SPACE") {

    g.setColor(
      new Color(90,140,235)
    )

    g.fillOval(
      450,
      190,
      240,
      240
    )
  }
}


// ============================================================
// EFFECTS
// ============================================================

def applyEffects(): Unit = {

  if (shadowOn) {

    g.setColor(
      new Color(0,0,0,70)
    )

    g.setStroke(
      new BasicStroke(14)
    )

    g.drawRoundRect(
      15,
      15,
      W - 30,
      H - 30,
      35,
      35
    )
  }

  if (
    outlineOn ||
    currentStyle == "COMIC"
  ) {

    g.setColor(
      new Color(15,15,15,210)
    )

    g.setStroke(
      new BasicStroke(5)
    )

    g.drawRoundRect(
      7,
      7,
      W - 14,
      H - 14,
      30,
      30
    )
  }

  if (
    neonOn ||
    currentStyle == "NEON"
  ) {

    g.setColor(
      new Color(0,255,255,160)
    )

    g.setStroke(
      new BasicStroke(12)
    )

    g.drawRoundRect(
      12,
      12,
      W - 24,
      H - 24,
      35,
      35
    )

    g.setColor(
      new Color(255,0,255,120)
    )

    g.setStroke(
      new BasicStroke(4)
    )

    g.drawRoundRect(
      25,
      25,
      W - 50,
      H - 50,
      30,
      30
    )
  }

  if (
    particlesOn ||
    currentStyle == "MAGIC"
  ) {

    g.setColor(
      Color.WHITE
    )

    var i = 0

    while (i < 80) {

      val x =
        (i * 113) % W

      val y =
        (i * 71) % H

      g.fillOval(
        x,
        y,
        3 + i % 4,
        3 + i % 4
      )

      i += 1
    }
  }
}


// ============================================================
// BASIC OBJECTS
// ============================================================

def drawCar(): Unit = {

  g.setColor(
    new Color(220,40,55)
  )

  g.fillRoundRect(
    230,
    360,
    740,
    150,
    45,
    45
  )

  val roof =
    new java.awt.Polygon()

  roof.addPoint(350,360)
  roof.addPoint(490,210)
  roof.addPoint(750,210)
  roof.addPoint(880,360)

  g.fillPolygon(roof)

  g.setColor(
    new Color(90,200,245)
  )

  g.fillRect(
    470,
    240,
    115,
    80
  )

  g.fillRect(
    610,
    240,
    130,
    80
  )

  g.setColor(Color.BLACK)

  g.fillOval(
    300,
    450,
    120,
    120
  )

  g.fillOval(
    780,
    450,
    120,
    120
  )
}


def drawHouse(): Unit = {

  g.setColor(
    new Color(245,210,135)
  )

  g.fillRect(
    350,
    280,
    500,
    300
  )

  val roof =
    new java.awt.Polygon()

  roof.addPoint(280,280)
  roof.addPoint(600,70)
  roof.addPoint(920,280)

  g.setColor(
    new Color(175,50,45)
  )

  g.fillPolygon(roof)

  g.setColor(
    new Color(110,70,45)
  )

  g.fillRect(
    555,
    400,
    100,
    180
  )

  g.setColor(
    new Color(80,190,240)
  )

  g.fillRect(
    390,
    340,
    90,
    85
  )

  g.fillRect(
    720,
    340,
    90,
    85
  )
}


def drawTree(): Unit = {

  g.setColor(
    new Color(130,80,40)
  )

  g.fillRoundRect(
    555,
    300,
    90,
    300,
    25,
    25
  )

  g.setColor(
    new Color(35,170,70)
  )

  g.fillOval(
    300,
    120,
    330,
    250
  )

  g.fillOval(
    550,
    100,
    330,
    270
  )

  g.fillOval(
    420,
    45,
    330,
    270
  )
}


def drawPerson(): Unit = {

  g.setColor(
    new Color(245,195,155)
  )

  g.fillOval(
    500,
    90,
    170,
    180
  )

  g.setColor(
    new Color(50,30,25)
  )

  g.fillArc(
    495,
    65,
    180,
    105,
    0,
    180
  )

  g.setColor(Color.BLACK)

  g.fillOval(
    545,
    165,
    18,
    18
  )

  g.fillOval(
    610,
    165,
    18,
    18
  )

  g.setColor(
    new Color(50,120,220)
  )

  g.fillRoundRect(
    450,
    270,
    270,
    230,
    35,
    35
  )

  g.setColor(
    new Color(245,195,155)
  )

  g.setStroke(
    new BasicStroke(
      28,
      BasicStroke.CAP_ROUND,
      BasicStroke.JOIN_ROUND
    )
  )

  g.drawLine(
    470,
    320,
    330,
    490
  )

  g.drawLine(
    700,
    320,
    840,
    490
  )

  g.setColor(
    new Color(45,45,55)
  )

  g.setStroke(
    new BasicStroke(
      45,
      BasicStroke.CAP_ROUND,
      BasicStroke.JOIN_ROUND
    )
  )

  g.drawLine(
    535,
    500,
    525,
    640
  )

  g.drawLine(
    635,
    500,
    645,
    640
  )
}


def drawCat(): Unit = {

  g.setColor(
    new Color(175,175,190)
  )

  g.fillOval(
    400,
    300,
    400,
    280
  )

  g.fillOval(
    420,
    110,
    360,
    300
  )

  val a =
    new java.awt.Polygon()

  a.addPoint(435,180)
  a.addPoint(485,65)
  a.addPoint(535,165)

  g.fillPolygon(a)

  val b =
    new java.awt.Polygon()

  b.addPoint(765,180)
  b.addPoint(715,65)
  b.addPoint(665,165)

  g.fillPolygon(b)

  g.setColor(
    new Color(80,220,100)
  )

  g.fillOval(
    500,
    210,
    45,
    55
  )

  g.fillOval(
    655,
    210,
    45,
    55
  )

  g.setColor(Color.PINK)

  g.fillOval(
    585,
    290,
    35,
    25
  )
}


def drawDog(): Unit = {

  g.setColor(
    new Color(175,120,70)
  )

  g.fillOval(
    390,
    300,
    420,
    290
  )

  g.fillOval(
    420,
    120,
    360,
    300
  )

  g.setColor(
    new Color(120,75,45)
  )

  g.fillOval(
    380,
    175,
    100,
    190
  )

  g.fillOval(
    720,
    175,
    100,
    190
  )

  g.setColor(Color.BLACK)

  g.fillOval(
    510,
    220,
    28,
    30
  )

  g.fillOval(
    665,
    220,
    28,
    30
  )

  g.fillOval(
    580,
    305,
    65,
    45
  )
}


def drawStar(): Unit = {

  drawStarShape(
    5,
    280,
    120,
    CX,
    CY
  )
}


def drawStarShape(
  points: Int,
  outerRadius: Double,
  innerRadius: Double,
  cx: Int,
  cy: Int
): Unit = {

  val p =
    new java.awt.Polygon()

  var i = 0

  while (
    i < points * 2
  ) {

    val a =
      -math.Pi / 2 +
      i * math.Pi / points

    val r =
      if (i % 2 == 0)
        outerRadius
      else
        innerRadius

    p.addPoint(
      cx + (math.cos(a) * r).toInt,
      cy + (math.sin(a) * r).toInt
    )

    i += 1
  }

  g.fillPolygon(p)
}


def drawHeart(): Unit = {

  val p =
    new Path2D.Double()

  p.moveTo(
    CX,
    CY + 250
  )

  p.curveTo(
    CX - 370,
    CY + 20,
    CX - 250,
    CY - 220,
    CX,
    CY - 40
  )

  p.curveTo(
    CX + 250,
    CY - 220,
    CX + 370,
    CY + 20,
    CX,
    CY + 250
  )

  g.fill(p)
}


def drawFlower(): Unit = {

  g.setStroke(
    new BasicStroke(
      22,
      BasicStroke.CAP_ROUND,
      BasicStroke.JOIN_ROUND
    )
  )

  g.setColor(
    new Color(45,150,65)
  )

  g.drawLine(
    CX,
    350,
    CX,
    620
  )

  val petals =
    Array(
      Color.RED,
      Color.PINK,
      Color.MAGENTA,
      Color.ORANGE,
      new Color(130,80,220),
      new Color(255,90,120)
    )

  var i = 0

  while (i < 6) {

    val a =
      i * math.Pi / 3

    val x =
      CX + (math.cos(a) * 100).toInt - 70

    val y =
      230 + (math.sin(a) * 100).toInt - 70

    g.setColor(
      petals(i)
    )

    g.fillOval(
      x,
      y,
      140,
      140
    )

    i += 1
  }

  g.setColor(
    Color.YELLOW
  )

  g.fillOval(
    CX - 70,
    160,
    140,
    140
  )
}


def drawFish(): Unit = {

  g.setColor(
    new Color(50,150,230)
  )

  g.fillOval(
    300,
    260,
    500,
    280
  )

  val tail =
    new java.awt.Polygon()

  tail.addPoint(760,400)
  tail.addPoint(930,270)
  tail.addPoint(930,530)

  g.fillPolygon(tail)

  g.setColor(Color.BLACK)

  g.fillOval(
    410,
    350,
    30,
    30
  )
}


def drawRocket(): Unit = {

  g.setColor(
    new Color(220,225,235)
  )

  g.fillOval(
    500,
    100,
    200,
    400
  )

  g.setColor(
    new Color(210,40,50)
  )

  val l =
    new java.awt.Polygon()

  l.addPoint(500,350)
  l.addPoint(420,510)
  l.addPoint(500,480)

  g.fillPolygon(l)

  val r =
    new java.awt.Polygon()

  r.addPoint(700,350)
  r.addPoint(780,510)
  r.addPoint(700,480)

  g.fillPolygon(r)

  g.setColor(
    new Color(70,180,240)
  )

  g.fillOval(
    555,
    200,
    90,
    90
  )

  g.setColor(
    Color.ORANGE
  )

  g.fillOval(
    550,
    455,
    100,
    180
  )
}


def drawRobot(): Unit = {

  g.setColor(
    new Color(100,165,200)
  )

  g.fillRoundRect(
    400,
    300,
    400,
    280,
    45,
    45
  )

  g.setColor(
    new Color(175,205,220)
  )

  g.fillRoundRect(
    360,
    90,
    480,
    230,
    55,
    55
  )

  g.setColor(Color.CYAN)

  g.fillOval(
    455,
    165,
    75,
    75
  )

  g.fillOval(
    670,
    165,
    75,
    75
  )

  g.setColor(Color.DARK_GRAY)

  g.fillRoundRect(
    500,
    250,
    200,
    40,
    15,
    15
  )

  g.setStroke(
    new BasicStroke(10)
  )

  g.drawLine(
    CX,
    90,
    CX,
    30
  )

  g.setColor(Color.RED)

  g.fillOval(
    CX - 25,
    5,
    50,
    50
  )
}


// ============================================================
// GENERIC OBJECT ENGINE
// ============================================================

def drawGenericObject(name: String): Unit = {

  val seed =
    math.abs(
      name.hashCode
    )

  val c =
    new Color(
      40 + seed % 180,
      40 + (seed / 7) % 180,
      40 + (seed / 13) % 180
    )

  g.setColor(c)

  val kind =
    seed % 10

  if (kind == 0) {

    g.fillRoundRect(
      350,
      180,
      500,
      360,
      70,
      70
    )

  } else if (kind == 1) {

    g.fillOval(
      350,
      100,
      500,
      500
    )

  } else if (kind == 2) {

    val p =
      new java.awt.Polygon()

    p.addPoint(CX,70)
    p.addPoint(900,600)
    p.addPoint(300,600)

    g.fillPolygon(p)

  } else if (kind == 3) {

    g.fillRect(
      330,
      140,
      540,
      440
    )

  } else if (kind == 4) {

    drawStarShape(
      8,
      260,
      130,
      CX,
      CY
    )

  } else if (kind == 5) {

    g.fillRoundRect(
      300,
      130,
      600,
      470,
      120,
      120
    )

  } else if (kind == 6) {

    drawHeart()

  } else if (kind == 7) {

    drawFlower()

  } else if (kind == 8) {

    drawFish()

  } else {

    drawStar()
  }

  g.setColor(Color.BLACK)

  g.setFont(
    new Font(
      "Arial",
      Font.BOLD,
      26
    )
  )

  val label =
    name.take(30)

  val sw =
    g.getFontMetrics.stringWidth(label)

  g.drawString(
    label,
    CX - sw / 2,
    665
  )
}


// ============================================================
// HUGE OBJECT LIBRARY
// ============================================================

val things =
  Array(
    "CAR","SPORTS CAR","RACING CAR","TRUCK","BUS",
    "TRAIN","AIRPLANE","HELICOPTER","ROCKET","BOAT",
    "SHIP","SUBMARINE","BICYCLE","MOTORBIKE","SCOOTER",

    "HOUSE","CASTLE","PALACE","LIGHTHOUSE","BRIDGE",
    "SCHOOL","HOSPITAL","SHOP","TOWER","CITY",

    "TREE","PALM TREE","FLOWER","MUSHROOM","CACTUS",
    "MOUNTAIN","VOLCANO","ISLAND","CLOUD","RAINBOW",
    "SUN","MOON","PLANET","EARTH","GALAXY",

    "BOY","GIRL","MAN","WOMAN","PERSON","ROBOT",
    "SUPERHERO","ASTRONAUT","SCIENTIST","KING","QUEEN",

    "CAT","DOG","BIRD","FISH","SHARK","WHALE",
    "DOLPHIN","LION","TIGER","ELEPHANT","HORSE",
    "RABBIT","BEAR","PANDA","MONKEY","FOX","DEER",
    "COW","GOAT","SHEEP","CHICKEN","DUCK","BUTTERFLY",
    "BEE","SNAKE","CROCODILE","TURTLE",

    "APPLE","BANANA","ORANGE","MANGO","WATERMELON",
    "PIZZA","BURGER","CAKE","ICE CREAM","DONUT",

    "GUITAR","PIANO","DRUM","MICROPHONE","SPEAKER",
    "CAMERA","COMPUTER","PHONE","BOOK","PENCIL",
    "CLOCK","KEY","LAMP","CHAIR","TABLE","BACKPACK",
    "GLASSES","CUP","BOTTLE","CANDLE",

    "STAR","HEART","DIAMOND","CROWN","TROPHY",
    "GIFT","BALLOON","UMBRELLA","MAGIC WAND",
    "FIRE","LIGHTNING","SNOWFLAKE",

    "CRICKET BAT","CRICKET BALL","FOOTBALL","TENNIS BALL",
    "BASKETBALL","TROPHY","MEDAL",

    "CYBER CITY","AI ROBOT","FUTURE CAR","SPACE STATION",
    "ALIEN PLANET","TIME MACHINE","HOLOGRAM","DRONE",
    "FUTURE HOUSE","NEON TOWER"
  )


// ============================================================
// SEARCH
// ============================================================

def searchItems(): Unit = {

  val q =
    searchField
      .getText
      .trim
      .toLowerCase

  itemModel.clear()

  var i = 0

  while (i < things.length) {

    if (
      q.length == 0 ||
      things(i)
        .toLowerCase
        .contains(q)
    ) {

      itemModel.addElement(
        things(i)
      )
    }

    i += 1
  }

  statusText(
    "SEARCH: " + q
  )
}


// ============================================================
// SMART OFFLINE COMMAND ENGINE
// ============================================================

def smartCommand(command: String): Unit = {

  val c =
    command
      .trim
      .toLowerCase

  if (c.length == 0) return

  if (
    c.contains("clear")
  ) {

    clearButtonAction()

  } else if (
    c.contains("undo")
  ) {

    undo()

  } else if (
    c.contains("redo")
  ) {

    redo()

  } else if (
    c.contains("random")
  ) {

    val index =
      scala.util.Random.nextInt(
        things.length
      )

    drawItem(
      things(index)
    )

  } else if (
    c.contains("neon")
  ) {

    neonOn = true
    currentStyle = "NEON"
    repaintCanvas()

  } else if (
    c.contains("magic")
  ) {

    currentStyle = "MAGIC"
    particlesOn = true
    repaintCanvas()

  } else if (
    c.contains("gold")
  ) {

    currentStyle = "GOLD"
    currentBackground = "GOLD"
    repaintCanvas()

  } else if (
    c.contains("space") ||
    c.contains("galaxy")
  ) {

    currentBackground = "GALAXY"
    drawCurrentBackground()

  } else if (
    c.contains("night")
  ) {

    currentBackground = "NIGHT"
    drawCurrentBackground()

  } else if (
    c.contains("ocean")
  ) {

    currentBackground = "OCEAN"
    drawCurrentBackground()

  } else if (
    c.contains("grid")
  ) {

    gridOn = !gridOn
    repaintCanvas()

  } else if (
    c.contains("shadow")
  ) {

    shadowOn = !shadowOn
    repaintCanvas()

  } else if (
    c.contains("outline")
  ) {

    outlineOn = !outlineOn
    repaintCanvas()

  } else if (
    c.contains("animate")
  ) {

    previewAnimation()

  } else if (
    c.contains("girl")
  ) {

    drawItem("GIRL")

  } else if (
    c.contains("boy")
  ) {

    drawItem("BOY")

  } else if (
    c.contains("cat")
  ) {

    drawItem("CAT")

  } else if (
    c.contains("dog")
  ) {

    drawItem("DOG")

  } else if (
    c.contains("car")
  ) {

    drawItem("CAR")

  } else if (
    c.contains("house")
  ) {

    drawItem("HOUSE")

  } else if (
    c.contains("tree")
  ) {

    drawItem("TREE")

  } else if (
    c.contains("robot")
  ) {

    drawItem("ROBOT")

  } else if (
    c.contains("rocket")
  ) {

    drawItem("ROCKET")

  } else if (
    c.contains("flower")
  ) {

    drawItem("FLOWER")

  } else if (
    c.contains("heart")
  ) {

    drawItem("HEART")

  } else if (
    c.contains("star")
  ) {

    drawItem("STAR")

  } else {

    var found = ""

    var i = 0

    while (
      i < things.length &&
      found == ""
    ) {

      if (
        things(i)
          .toLowerCase
          .contains(c)
      ) {

        found = things(i)
      }

      i += 1
    }

    if (found != "") {

      drawItem(found)

    } else {

      statusText(
        "SMART COMMAND NOT FOUND"
      )
    }
  }
}


// ============================================================
// DRAW ITEM
// ============================================================

def drawCurrentBackground(): Unit = {

  clearImage()

  drawBackground(
    currentBackground
  )

  applyEffects()

  repaintCanvas()
}


def drawItem(item: String): Unit = {

  remember()

  clearImage()

  drawBackground(
    currentBackground
  )

  val t =
    item
      .toLowerCase

  g.setColor(
    currentColor
  )

  if (t.contains("car")) {

    drawCar()

  } else if (
    t.contains("house") ||
    t.contains("castle") ||
    t.contains("palace")
  ) {

    drawHouse()

  } else if (
    t.contains("tree")
  ) {

    drawTree()

  } else if (
    t.contains("boy") ||
    t.contains("girl") ||
    t.contains("man") ||
    t.contains("woman") ||
    t.contains("person")
  ) {

    drawPerson()

  } else if (
    t.contains("cat")
  ) {

    drawCat()

  } else if (
    t.contains("dog")
  ) {

    drawDog()

  } else if (
    t.contains("fish") ||
    t.contains("shark") ||
    t.contains("whale") ||
    t.contains("dolphin")
  ) {

    drawFish()

  } else if (
    t.contains("flower")
  ) {

    drawFlower()

  } else if (
    t.contains("star")
  ) {

    drawStar()

  } else if (
    t.contains("heart")
  ) {

    drawHeart()

  } else if (
    t.contains("rocket")
  ) {

    drawRocket()

  } else if (
    t.contains("robot")
  ) {

    drawRobot()

  } else {

    drawGenericObject(item)
  }

  applyEffects()

  selectedItem =
    item

  repaintCanvas()

  statusText(
    "DRAWN: " + item
  )
}


// ============================================================
// TEXT
// ============================================================

def drawText(): Unit = {

  val text =
    textField
      .getText
      .trim

  if (text == "") {

    statusText(
      "TYPE TEXT FIRST"
    )

    return
  }

  remember()

  g.setColor(
    currentColor
  )

  g.setFont(
    new Font(
      "Arial",
      Font.BOLD,
      52
    )
  )

  val width =
    g.getFontMetrics
      .stringWidth(text)

  g.drawString(
    text,
    math.max(
      20,
      CX - width / 2
    ),
    CY
  )

  repaintCanvas()

  statusText(
    "TEXT ADDED"
  )
}


// ============================================================
// TRANSFORMS
// ============================================================

def transform(mode: String): Unit = {

  remember()

  val temp =
    copyImage(image)

  clearImage()

  val tx =
    g.create()
      .asInstanceOf[Graphics2D]

  tx.setRenderingHint(
    RenderingHints.KEY_INTERPOLATION,
    RenderingHints.VALUE_INTERPOLATION_BICUBIC
  )

  if (mode == "FLIP_H") {

    tx.drawImage(
      temp,
      W,
      0,
      -W,
      H,
      null
    )

  } else if (mode == "FLIP_V") {

    tx.drawImage(
      temp,
      0,
      H,
      W,
      -H,
      null
    )

  } else {

    tx.translate(
      CX,
      CY
    )

    if (mode == "LEFT") {

      tx.rotate(
        -math.Pi / 2
      )

    } else {

      tx.rotate(
        math.Pi / 2
      )
    }

    tx.translate(
      -CX,
      -CY
    )

    tx.drawImage(
      temp,
      0,
      0,
      null
    )
  }

  tx.dispose()

  repaintCanvas()

  statusText(
    "TRANSFORM: " + mode
  )
}


// ============================================================
// IMPORT IMAGE
// ============================================================

def importImage(): Unit = {

  val chooser =
    new JFileChooser()

  chooser.setDialogTitle(
    "IMPORT IMAGE - JPG / PNG"
  )

  if (
    chooser.showOpenDialog(frame) ==
    JFileChooser.APPROVE_OPTION
  ) {

    try {

      val selected =
        chooser.getSelectedFile

      val loaded =
        ImageIO.read(selected)

      if (loaded != null) {

        remember()

        importedImage =
          loaded

        val iw =
          loaded.getWidth

        val ih =
          loaded.getHeight

        val scale =
          math.min(
            W.toDouble / iw,
            H.toDouble / ih
          )

        val nw =
          math.max(
            1,
            (iw * scale).toInt
          )

        val nh =
          math.max(
            1,
            (ih * scale).toInt
          )

        val resized =
          loaded.getScaledInstance(
            nw,
            nh,
            Image.SCALE_SMOOTH
          )

        val x =
          (W - nw) / 2

        val y =
          (H - nh) / 2

        g.drawImage(
          resized,
          x,
          y,
          null
        )

        repaintCanvas()

        statusText(
          "IMAGE IMPORTED: " +
          selected.getName
        )
      }

    } catch {

      case ex: Exception =>

        JOptionPane.showMessageDialog(
          frame,
          "Could not import image.",
          "IMAGE ERROR",
          JOptionPane.ERROR_MESSAGE
        )
    }
  }
}


// ============================================================
// SAVE
// ============================================================

def saveImage(): Unit = {

  val chooser =
    new JFileChooser()

  chooser.setDialogTitle(
    "SAVE PNG"
  )

  if (
    chooser.showSaveDialog(frame) ==
    JFileChooser.APPROVE_OPTION
  ) {

    try {

      var file =
        chooser.getSelectedFile

      if (
        !file.getName
          .toLowerCase
          .endsWith(".png")
      ) {

        file =
          new File(
            file.getAbsolutePath +
            ".png"
          )
      }

      ImageIO.write(
        image,
        "png",
        file
      )

      statusText(
        "SAVED: " +
        file.getName
      )

    } catch {

      case _: Exception =>

        statusText(
          "SAVE ERROR"
        )
    }
  }
}


// ============================================================
// CLEAR
// ============================================================

def clearButtonAction(): Unit = {

  remember()

  clearImage()

  selectedItem = ""

  repaintCanvas()

  statusText(
    "CANVAS CLEARED"
  )
}


// ============================================================
// ANIMATION PREVIEW
// ============================================================

def previewAnimation(): Unit = {

  val preview =
    new JFrame(
      "FUTURE ANIMATION PREVIEW"
    )

  preview.setDefaultCloseOperation(
    WindowConstants.DISPOSE_ON_CLOSE
  )

  preview.setSize(
    1200,
    800
  )

  preview.setLocationRelativeTo(
    frame
  )

  var running = false
  var tick = 0

  val snapshot =
    copyImage(image)

  val panel =
    new JPanel {

      override def paintComponent(
        graphics: Graphics
      ): Unit = {

        super.paintComponent(
          graphics
        )

        val pg =
          graphics
            .asInstanceOf[Graphics2D]

        pg.setRenderingHint(
          RenderingHints.KEY_ANTIALIASING,
          RenderingHints.VALUE_ANTIALIAS_ON
        )

        val scale =
          math.min(
            (getWidth - 50).toDouble /
              snapshot.getWidth,
            (getHeight - 100).toDouble /
              snapshot.getHeight
          )

        val iw =
          (snapshot.getWidth * scale).toInt

        val ih =
          (snapshot.getHeight * scale).toInt

        val x =
          (getWidth - iw) / 2

        val y =
          (getHeight - ih) / 2

        val angle =
          if (running)
            math.sin(tick * 0.08) * 0.035
          else
            0.0

        val at =
          new AffineTransform()

        at.translate(
          x + iw / 2,
          y + ih / 2
        )

        at.rotate(angle)

        at.translate(
          -iw / 2,
          -ih / 2
        )

        at.scale(
          scale,
          scale
        )

        pg.drawImage(
          snapshot,
          at,
          null
        )
      }
    }

  val start =
    makeButton(
      "START / STOP"
    )

  val close =
    makeButton(
      "CLOSE"
    )

  val timer =
    new Timer(
      30,
      new ActionListener {

        override def actionPerformed(
          e: ActionEvent
        ): Unit = {

          if (running) {

            tick += 1

            panel.repaint()
          }
        }
      }
    )

  start.addActionListener(
    new ActionListener {

      override def actionPerformed(
        e: ActionEvent
      ): Unit = {

        running =
          !running

        if (running)
          timer.start()
        else
          timer.stop()
      }
    }
  )

  close.addActionListener(
    new ActionListener {

      override def actionPerformed(
        e: ActionEvent
      ): Unit = {

        timer.stop()
        preview.dispose()
      }
    }
  )

  val controls =
    new JPanel(
      new FlowLayout()
    )

  controls.add(start)
  controls.add(close)

  preview.setLayout(
    new BorderLayout()
  )

  preview.add(
    panel,
    BorderLayout.CENTER
  )

  preview.add(
    controls,
    BorderLayout.SOUTH
  )

  preview.setVisible(true)
}


// ============================================================
// BUTTONS
// ============================================================

val undoButton =
  makeButton("UNDO")

val redoButton =
  makeButton("REDO")

val clearButton =
  makeButton("CLEAR")

val saveButton =
  makeButton("SAVE PNG")

val importButton =
  makeButton("IMPORT IMAGE")

val randomButton =
  makeButton("RANDOM")

val drawButton =
  makeButton("DRAW")

val colorButton =
  makeButton("COLOR")

val textButton =
  makeButton("TEXT")

val animateButton =
  makeButton("ANIMATE")

val zoomInButton =
  makeButton("ZOOM +")

val zoomOutButton =
  makeButton("ZOOM -")

val flipHButton =
  makeButton("FLIP H")

val flipVButton =
  makeButton("FLIP V")

val rotateLButton =
  makeButton("ROTATE L")

val rotateRButton =
  makeButton("ROTATE R")

val gridButton =
  makeButton("GRID")

val snapButton =
  makeButton("SNAP")

val shadowButton =
  makeButton("SHADOW")

val outlineButton =
  makeButton("OUTLINE")

val neonButton =
  makeButton("NEON")

val particleButton =
  makeButton("PARTICLES")


// ============================================================
// TOOL COMBO
// ============================================================

val toolBox =
  new JComboBox[String](
    Array(
      "PENCIL",
      "ERASER",
      "SPRAY",
      "LINE",
      "RECTANGLE",
      "ELLIPSE",
      "BRUSH",
      "HIGHLIGHTER"
    )
  )

val sizeBox =
  new JComboBox[String](
    Array(
      "2",
      "4",
      "6",
      "8",
      "12",
      "16",
      "20",
      "30",
      "40",
      "60"
    )
  )

val backgroundBox =
  new JComboBox[String](
    Array(
      "SKY",
      "SUNNY",
      "SUNRISE",
      "SUNSET",
      "NIGHT",
      "SPACE",
      "OCEAN",
      "FOREST",
      "DESERT",
      "MAGIC",
      "NEON",
      "GOLD",
      "ICE",
      "VOLCANO",
      "CYBER",
      "GALAXY"
    )
  )

val styleBox =
  new JComboBox[String](
    Array(
      "NORMAL",
      "NEON",
      "MAGIC",
      "GOLD",
      "COMIC",
      "SOFT"
    )
  )


// ============================================================
// TEXT FIELDS
// ============================================================

commandField =
  new JTextField(
    "draw a futuristic car",
    24
  )

searchField =
  new JTextField(
    12
  )

textField =
  new JTextField(
    "ULTRA LEGEND",
    14
  )


// ============================================================
// ITEM LIST
// ============================================================

itemList =
  new JList[String](
    itemModel
  )

itemList.setFont(
  new Font(
    "Arial",
    Font.BOLD,
    13
  )
)

itemList.setSelectionMode(
  ListSelectionModel.SINGLE_SELECTION
)

var i = 0

while (i < things.length) {

  itemModel.addElement(
    things(i)
  )

  i += 1
}


// ============================================================
// STATUS
// ============================================================

status =
  new JLabel(
    "ULTRA LEGEND X READY"
  )

status.setFont(
  new Font(
    "Arial",
    Font.BOLD,
    14
  )
)


// ============================================================
// CANVAS
// ============================================================

canvas =
  new JPanel {

    override def paintComponent(
      graphics: Graphics
    ): Unit = {

      super.paintComponent(
        graphics
      )

      val cg =
        graphics
          .asInstanceOf[Graphics2D]

      cg.setRenderingHint(
        RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON
      )

      cg.setColor(
        new Color(230,230,235)
      )

      cg.fillRect(
        0,
        0,
        getWidth,
        getHeight
      )

      val dw =
        drawW()

      val dh =
        drawH()

      val ox =
        canvasLeft()

      val oy =
        canvasTop()

      cg.drawImage(
        image,
        ox,
        oy,
        dw,
        dh,
        null
      )

      if (gridOn) {

        cg.setColor(
          new Color(0,0,0,35)
        )

        val step =
          math.max(
            20,
            (50 * zoom).toInt
          )

        var x = ox

        while (
          x <= ox + dw
        ) {

          cg.drawLine(
            x,
            oy,
            x,
            oy + dh
          )

          x += step
        }

        var y = oy

        while (
          y <= oy + dh
        ) {

          cg.drawLine(
            ox,
            y,
            ox + dw,
            y
          )

          y += step
        }
      }
    }
  }

canvas.setFocusable(true)

canvas.setCursor(
  Cursor.getPredefinedCursor(
    Cursor.CROSSHAIR_CURSOR
  )
)


// ============================================================
// LEFT PANEL
// ============================================================

val title =
  new JLabel(
    "OBJECT / AI LIBRARY"
  )

title.setFont(
  new Font(
    "Arial",
    Font.BOLD,
    18
  )
)

val searchPanel =
  new JPanel(
    new FlowLayout(
      FlowLayout.LEFT,
      3,
      3
    )
  )

val searchButton =
  makeButton(
    "SEARCH"
  )

searchPanel.add(
  searchField
)

searchPanel.add(
  searchButton
)

val leftPanel =
  new JPanel(
    new BorderLayout(
      4,
      4
    )
  )

leftPanel.setBorder(
  new EmptyBorder(
    5,
    5,
    5,
    5
  )
)

leftPanel.add(
  title,
  BorderLayout.NORTH
)

leftPanel.add(
  searchPanel,
  BorderLayout.CENTER
)

leftPanel.add(
  new JScrollPane(itemList),
  BorderLayout.SOUTH
)


// ============================================================
// TOOLBAR
// ============================================================

val toolbar =
  new JPanel(
    new FlowLayout(
      FlowLayout.LEFT,
      3,
      3
    )
  )

toolbar.add(undoButton)
toolbar.add(redoButton)
toolbar.add(clearButton)
toolbar.add(saveButton)
toolbar.add(importButton)
toolbar.add(randomButton)

toolbar.add(
  new JLabel("TOOL:")
)

toolbar.add(toolBox)

toolbar.add(
  new JLabel("SIZE:")
)

toolbar.add(sizeBox)

toolbar.add(colorButton)
toolbar.add(gridButton)
toolbar.add(snapButton)
toolbar.add(shadowButton)
toolbar.add(outlineButton)
toolbar.add(neonButton)
toolbar.add(particleButton)

toolbar.add(
  new JLabel("BG:")
)

toolbar.add(backgroundBox)

toolbar.add(
  new JLabel("STYLE:")
)

toolbar.add(styleBox)

toolbar.add(zoomInButton)
toolbar.add(zoomOutButton)
toolbar.add(rotateLButton)
toolbar.add(rotateRButton)
toolbar.add(flipHButton)
toolbar.add(flipVButton)


// ============================================================
// AI COMMAND BAR
// ============================================================

val aiBar =
  new JPanel(
    new FlowLayout(
      FlowLayout.LEFT,
      5,
      4
    )
  )

aiBar.add(
  new JLabel(
    "SMART COMMAND:"
  )
)

val aiButton =
  makeButton(
    "RUN"
  )

aiBar.add(
  commandField
)

aiBar.add(
  aiButton
)


// ============================================================
// TEXT BAR
// ============================================================

val textBar =
  new JPanel(
    new FlowLayout(
      FlowLayout.LEFT,
      5,
      4
    )
  )

textBar.add(
  new JLabel("TEXT:")
)

textBar.add(
  textField
)

textBar.add(
  textButton
)

textBar.add(
  animateButton
)


// ============================================================
// FRAME
// ============================================================

frame =
  new JFrame(
    "ULTRA LEGEND SMART DRAW STUDIO X"
  )

frame.setDefaultCloseOperation(
  WindowConstants.EXIT_ON_CLOSE
)

frame.setSize(
  1600,
  950
)

frame.setLocationRelativeTo(
  null
)

frame.setLayout(
  new BorderLayout(
    4,
    4
  )
)

frame.add(
  toolbar,
  BorderLayout.NORTH
)

frame.add(
  leftPanel,
  BorderLayout.WEST
)

frame.add(
  canvas,
  BorderLayout.CENTER
)

val south =
  new JPanel(
    new BorderLayout()
  )

south.add(
  aiBar,
  BorderLayout.NORTH
)

south.add(
  textBar,
  BorderLayout.CENTER
)

south.add(
  status,
  BorderLayout.SOUTH
)

frame.add(
  south,
  BorderLayout.SOUTH
)


// ============================================================
// EVENTS
// ============================================================

toolBox.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      currentTool =
        toolBox
          .getSelectedItem
          .toString

      statusText(
        "TOOL: " + currentTool
      )
    }
  }
)


sizeBox.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      try {

        brushSize =
          sizeBox
            .getSelectedItem
            .toString
            .toFloat

      } catch {
        case _: Exception =>
      }
    }
  }
)


backgroundBox.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      currentBackground =
        backgroundBox
          .getSelectedItem
          .toString

      drawCurrentBackground()
    }
  }
)


styleBox.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      currentStyle =
        styleBox
          .getSelectedItem
          .toString

      if (
        currentStyle == "NEON"
      )
        neonOn = true

      if (
        currentStyle == "MAGIC"
      )
        particlesOn = true

      repaintCanvas()

      statusText(
        "STYLE: " +
        currentStyle
      )
    }
  }
)


colorButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      val selected =
        JColorChooser.showDialog(
          frame,
          "CHOOSE COLOR",
          currentColor
        )

      if (selected != null) {

        currentColor =
          selected

        statusText(
          "COLOR CHANGED"
        )
      }
    }
  }
)


drawButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      smartCommand(
        commandField.getText
      )
    }
  }
)


aiButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      smartCommand(
        commandField.getText
      )
    }
  }
)


commandField.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      smartCommand(
        commandField.getText
      )
    }
  }
)


searchButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      searchItems()
    }
  }
)


searchField.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      searchItems()
    }
  }
)


itemList.addMouseListener(
  new MouseAdapter {

    override def mouseClicked(
      e: MouseEvent
    ): Unit = {

      val index =
        itemList.locationToIndex(
          e.getPoint
        )

      if (index >= 0) {

        val value =
          itemModel.getElementAt(
            index
          )

        drawItem(value)
      }
    }
  }
)


undoButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = undo()
  }
)


redoButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = redo()
  }
)


clearButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit =
      clearButtonAction()
  }
)


saveButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit =
      saveImage()
  }
)


importButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit =
      importImage()
  }
)


randomButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      val index =
        scala.util.Random.nextInt(
          things.length
        )

      drawItem(
        things(index)
      )
    }
  }
)


textButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit =
      drawText()
  }
)


animateButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit =
      previewAnimation()
  }
)


zoomInButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      zoom =
        math.min(
          3.0,
          zoom + 0.1
        )

      repaintCanvas()

      statusText(
        "ZOOM " +
        (zoom * 100).toInt +
        "%"
      )
    }
  }
)


zoomOutButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      zoom =
        math.max(
          0.4,
          zoom - 0.1
        )

      repaintCanvas()

      statusText(
        "ZOOM " +
        (zoom * 100).toInt +
        "%"
      )
    }
  }
)


rotateLButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit =
      transform("LEFT")
  }
)


rotateRButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit =
      transform("RIGHT")
  }
)


flipHButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit =
      transform("FLIP_H")
  }
)


flipVButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit =
      transform("FLIP_V")
  }
)


gridButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      gridOn =
        !gridOn

      repaintCanvas()

      statusText(
        if (gridOn)
          "GRID ON"
        else
          "GRID OFF"
      )
    }
  }
)


snapButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      snapOn =
        !snapOn

      statusText(
        if (snapOn)
          "SNAP ON"
        else
          "SNAP OFF"
      )
    }
  }
)


shadowButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      shadowOn =
        !shadowOn

      repaintCanvas()
    }
  }
)


outlineButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      outlineOn =
        !outlineOn

      repaintCanvas()
    }
  }
)


neonButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      neonOn =
        !neonOn

      repaintCanvas()
    }
  }
)


particleButton.addActionListener(
  new ActionListener {

    override def actionPerformed(
      e: ActionEvent
    ): Unit = {

      particlesOn =
        !particlesOn

      repaintCanvas()
    }
  }
)


// ============================================================
// MOUSE DRAWING
// ============================================================

canvas.addMouseListener(
  new MouseAdapter {

    override def mousePressed(
      e: MouseEvent
    ): Unit = {

      if (
        insideCanvas(
          e.getX,
          e.getY
        )
      ) {

        remember()

        mouseDown = true

        startX =
          toImageX(
            e.getX
          )

        startY =
          toImageY(
            e.getY
          )

        lastX = startX
        lastY = startY

        if (snapOn) {

          startX =
            (startX / 10) * 10

          startY =
            (startY / 10) * 10
        }

        if (
          currentTool == "PENCIL" ||
          currentTool == "ERASER" ||
          currentTool == "BRUSH" ||
          currentTool == "HIGHLIGHTER"
        ) {

          g.setColor(
            if (
              currentTool == "ERASER"
            )
              Color.WHITE
            else
              currentColor
          )

          val alpha =
            if (
              currentTool ==
              "HIGHLIGHTER"
            )
              80
            else
              255

          g.setColor(
            new Color(
              currentColor.getRed,
              currentColor.getGreen,
              currentColor.getBlue,
              alpha
            )
          )

          if (
            currentTool == "ERASER"
          ) {

            g.setColor(
              Color.WHITE
            )
          }

          g.setStroke(
            new BasicStroke(
              if (
                currentTool == "BRUSH"
              )
                brushSize * 2
              else
                brushSize,
              BasicStroke.CAP_ROUND,
              BasicStroke.JOIN_ROUND
            )
          )

          g.drawLine(
            lastX,
            lastY,
            lastX,
            lastY
          )
        }

        if (
          currentTool == "SPRAY"
        ) {

          var s = 0

          while (s < 50) {

            val dx =
              scala.util.Random.nextInt(
                51
              ) - 25

            val dy =
              scala.util.Random.nextInt(
                51
              ) - 25

            g.setColor(
              currentColor
            )

            g.fillOval(
              startX + dx,
              startY + dy,
              3,
              3
            )

            s += 1
          }
        }

        repaintCanvas()
      }
    }


    override def mouseReleased(
      e: MouseEvent
    ): Unit = {

      if (mouseDown) {

        val x =
          toImageX(
            e.getX
          )

        val y =
          toImageY(
            e.getY
          )

        g.setColor(
          currentColor
        )

        g.setStroke(
          new BasicStroke(
            brushSize,
            BasicStroke.CAP_ROUND,
            BasicStroke.JOIN_ROUND
          )
        )

        if (
          currentTool == "LINE"
        ) {

          g.drawLine(
            startX,
            startY,
            x,
            y
          )

        } else if (
          currentTool == "RECTANGLE"
        ) {

          val rx =
            math.min(
              startX,
              x
            )

          val ry =
            math.min(
              startY,
              y
            )

          val rw =
            math.abs(
              x - startX
            )

          val rh =
            math.abs(
              y - startY
            )

          g.drawRect(
            rx,
            ry,
            rw,
            rh
          )

        } else if (
          currentTool == "ELLIPSE"
        ) {

          val rx =
            math.min(
              startX,
              x
            )

          val ry =
            math.min(
              startY,
              y
            )

          val rw =
            math.abs(
              x - startX
            )

          val rh =
            math.abs(
              y - startY
            )

          g.drawOval(
            rx,
            ry,
            rw,
            rh
          )
        }

        mouseDown = false

        repaintCanvas()
      }
    }
  }
)


canvas.addMouseMotionListener(
  new MouseMotionAdapter {

    override def mouseDragged(
      e: MouseEvent
    ): Unit = {

      if (
        mouseDown &&
        insideCanvas(
          e.getX,
          e.getY
        )
      ) {

        val x =
          toImageX(
            e.getX
          )

        val y =
          toImageY(
            e.getY
          )

        if (
          currentTool == "PENCIL" ||
          currentTool == "ERASER" ||
          currentTool == "BRUSH" ||
          currentTool == "HIGHLIGHTER"
        ) {

          if (
            currentTool == "ERASER"
          ) {

            g.setColor(
              Color.WHITE
            )

          } else if (
            currentTool ==
            "HIGHLIGHTER"
          ) {

            g.setColor(
              new Color(
                currentColor.getRed,
                currentColor.getGreen,
                currentColor.getBlue,
                80
              )
            )

          } else {

            g.setColor(
              currentColor
            )
          }

          g.setStroke(
            new BasicStroke(
              if (
                currentTool == "BRUSH"
              )
                brushSize * 2
              else
                brushSize,
              BasicStroke.CAP_ROUND,
              BasicStroke.JOIN_ROUND
            )
          )

          g.drawLine(
            lastX,
            lastY,
            x,
            y
          )

        } else if (
          currentTool == "SPRAY"
        ) {

          var s = 0

          while (s < 30) {

            val dx =
              scala.util.Random.nextInt(
                41
              ) - 20

            val dy =
              scala.util.Random.nextInt(
                41
              ) - 20

            g.setColor(
              currentColor
            )

            g.fillOval(
              x + dx,
              y + dy,
              3,
              3
            )

            s += 1
          }
        }

        lastX = x
        lastY = y

        repaintCanvas()
      }
    }
  }
)


// ============================================================
// FUTURISTIC SECRET KEY SYSTEM
// ============================================================

canvas.addKeyListener(
  new KeyAdapter {

    override def keyPressed(
      e: KeyEvent
    ): Unit = {

      val k =
        e.getKeyCode

      if (
        e.isControlDown &&
        k == KeyEvent.VK_Z
      ) undo()

      else if (
        e.isControlDown &&
        k == KeyEvent.VK_Y
      ) redo()

      else if (
        e.isControlDown &&
        k == KeyEvent.VK_S
      ) saveImage()

      else if (
        e.isControlDown &&
        k == KeyEvent.VK_O
      ) importImage()

      else if (
        k == KeyEvent.VK_DELETE
      ) clearButtonAction()

      else if (
        k == KeyEvent.VK_F1
      ) {

        JOptionPane.showMessageDialog(
          frame,
          "ULTRA LEGEND X SHORTCUTS\n\n" +
          "CTRL+Z  = UNDO\n" +
          "CTRL+Y  = REDO\n" +
          "CTRL+S  = SAVE\n" +
          "CTRL+O  = IMPORT IMAGE\n" +
          "DELETE  = CLEAR\n\n" +
          "F1  = SHORTCUT GUIDE\n" +
          "F2  = RANDOM OBJECT\n" +
          "F3  = NEON MODE\n" +
          "F4  = MAGIC MODE\n" +
          "F5  = GRID\n" +
          "F6  = SNAP\n" +
          "F7  = SHADOW\n" +
          "F8  = OUTLINE\n" +
          "F9  = ZOOM IN\n" +
          "F10 = ZOOM OUT\n" +
          "F11 = ANIMATION\n" +
          "F12 = SMART COMMAND\n\n" +
          "ALT+LEFT  = ROTATE LEFT\n" +
          "ALT+RIGHT = ROTATE RIGHT\n" +
          "ALT+H     = FLIP HORIZONTAL\n" +
          "ALT+V     = FLIP VERTICAL",
          "SECRET CONTROL CENTER",
          JOptionPane.INFORMATION_MESSAGE
        )

      } else if (
        k == KeyEvent.VK_F2
      ) {

        randomButton.doClick()

      } else if (
        k == KeyEvent.VK_F3
      ) {

        neonButton.doClick()

      } else if (
        k == KeyEvent.VK_F4
      ) {

        currentStyle = "MAGIC"
        particlesOn = true
        repaintCanvas()

      } else if (
        k == KeyEvent.VK_F5
      ) {

        gridButton.doClick()

      } else if (
        k == KeyEvent.VK_F6
      ) {

        snapButton.doClick()

      } else if (
        k == KeyEvent.VK_F7
      ) {

        shadowButton.doClick()

      } else if (
        k == KeyEvent.VK_F8
      ) {

        outlineButton.doClick()

      } else if (
        k == KeyEvent.VK_F9
      ) {

        zoomInButton.doClick()

      } else if (
        k == KeyEvent.VK_F10
      ) {

        zoomOutButton.doClick()

      } else if (
        k == KeyEvent.VK_F11
      ) {

        animateButton.doClick()

      } else if (
        k == KeyEvent.VK_F12
      ) {

        commandField.requestFocusInWindow()

      } else if (
        e.isAltDown &&
        k == KeyEvent.VK_LEFT
      ) {

        rotateLButton.doClick()

      } else if (
        e.isAltDown &&
        k == KeyEvent.VK_RIGHT
      ) {

        rotateRButton.doClick()

      } else if (
        e.isAltDown &&
        k == KeyEvent.VK_H
      ) {

        flipHButton.doClick()

      } else if (
        e.isAltDown &&
        k == KeyEvent.VK_V
      ) {

        flipVButton.doClick()
      }
    }
  }
)


// ============================================================
// START
// ============================================================

SwingUtilities.invokeLater(
  new Runnable {

    override def run(): Unit = {

      frame.setVisible(
        true
      )

      canvas.requestFocusInWindow()

      statusText(
        "ULTRA LEGEND X ONLINE - OFFLINE SMART ENGINE READY"
      )
    }
  }
)
//