Code Sketch


graphic_7325*
By: Mhalsakant School
Category: Art
import javax.swing._
import javax.swing.border._
import javax.swing.table.DefaultTableModel
import java.awt._
import java.awt.event._
import java.awt.geom._
import java.security.MessageDigest
import java.text.SimpleDateFormat
import java.util.Date

// =============================================================================
// 1. CONFIGURATION
// =============================================================================

object AppConfig {
  val AppTitle = "ALL INDIA & MAHARASHTRA RTO MAP SUITE"
  val AppWidth = 1200
  val AppHeight = 780

  // Password: INDIA@RTO7313
  // MD5 hash is generated below from the password at runtime.
  private val LoginSecret = "INDIA@RTO7313"

  val AuthPasswordHash: String = {
    CryptoUtil.md5Hex(LoginSecret)
  }

  val PrimaryDark = new Color(24, 43, 73)
  val AccentBlue = new Color(41, 128, 185)
  val AccentGreen = new Color(39, 174, 96)
  val DangerRed = new Color(192, 57, 43)
  val CanvasBg = new Color(245, 248, 252)
  val TextPrimary = new Color(33, 33, 33)
  val TextSecondary = new Color(110, 110, 110)
}

case class MapCoordinate(xRatio: Double, yRatio: Double)

// =============================================================================
// 2. SECURITY
// =============================================================================

object CryptoUtil {

  def md5Hex(text: String): String = {
    try {
      val md = MessageDigest.getInstance("MD5")
      val bytes = md.digest(text.getBytes("UTF-8"))

      bytes.map { b =>
        "%02x".format(b & 0xff)
      }.mkString

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

  def verifyPassword(input: String, targetHash: String): Boolean = {
    md5Hex(input.trim).equalsIgnoreCase(targetHash.trim)
  }
}

// =============================================================================
// 3. RTO DATABASE
// =============================================================================

object RTODatabase {

  val statesAndUTs: Map[String, String] = Map(
    "AP" -> "Andhra Pradesh",
    "AR" -> "Arunachal Pradesh",
    "AS" -> "Assam",
    "BR" -> "Bihar",
    "CG" -> "Chhattisgarh",
    "GA" -> "Goa",
    "GJ" -> "Gujarat",
    "HR" -> "Haryana",
    "HP" -> "Himachal Pradesh",
    "JH" -> "Jharkhand",
    "KA" -> "Karnataka",
    "KL" -> "Kerala",
    "MP" -> "Madhya Pradesh",
    "MH" -> "Maharashtra",
    "MN" -> "Manipur",
    "ML" -> "Meghalaya",
    "MZ" -> "Mizoram",
    "NL" -> "Nagaland",
    "OD" -> "Odisha",
    "PB" -> "Punjab",
    "RJ" -> "Rajasthan",
    "SK" -> "Sikkim",
    "TN" -> "Tamil Nadu",
    "TS" -> "Telangana",
    "TR" -> "Tripura",
    "UK" -> "Uttarakhand",
    "UP" -> "Uttar Pradesh",
    "WB" -> "West Bengal",
    "AN" -> "Andaman & Nicobar Islands",
    "CH" -> "Chandigarh",
    "DH" -> "Dadra & Nagar Haveli and Daman & Diu",
    "DL" -> "Delhi",
    "JK" -> "Jammu & Kashmir",
    "LA" -> "Ladakh",
    "LD" -> "Lakshadweep",
    "PY" -> "Puducherry"
  )

  val maharashtraRTO: Map[String, String] = Map(
    "MH01" -> "Mumbai Central (Tardeo)",
    "MH02" -> "Mumbai West (Andheri)",
    "MH03" -> "Mumbai East (Wadala)",
    "MH04" -> "Thane",
    "MH05" -> "Kalyan (Dombivli)",
    "MH06" -> "Raigad (Pen)",
    "MH07" -> "Sindhudurg (Oros)",
    "MH08" -> "Ratnagiri",
    "MH09" -> "Kolhapur",
    "MH10" -> "Sangli",
    "MH11" -> "Satara",
    "MH12" -> "Pune City",
    "MH13" -> "Solapur",
    "MH14" -> "Pimpri-Chinchwad",
    "MH15" -> "Nashik",
    "MH16" -> "Ahmednagar",
    "MH17" -> "Shrirampur (Ahmednagar Rural)",
    "MH18" -> "Dhule",
    "MH19" -> "Jalgaon",
    "MH20" -> "Chhatrapati Sambhajinagar",
    "MH21" -> "Jalna",
    "MH22" -> "Parbhani",
    "MH23" -> "Beed",
    "MH24" -> "Latur",
    "MH25" -> "Dharashiv",
    "MH26" -> "Nanded",
    "MH27" -> "Amravati",
    "MH28" -> "Buldhana",
    "MH29" -> "Yavatmal",
    "MH30" -> "Akola",
    "MH31" -> "Nagpur Urban",
    "MH32" -> "Wardha",
    "MH33" -> "Gadchiroli",
    "MH34" -> "Chandrapur",
    "MH35" -> "Gondia",
    "MH36" -> "Bhandara",
    "MH37" -> "Washim",
    "MH38" -> "Hingoli",
    "MH39" -> "Nandurbar",
    "MH40" -> "Nagpur Rural",
    "MH41" -> "Malegaon",
    "MH42" -> "Baramati",
    "MH43" -> "Navi Mumbai",
    "MH44" -> "Ambejogai",
    "MH45" -> "Akluj",
    "MH46" -> "Panvel",
    "MH47" -> "Borivali",
    "MH48" -> "Vasai-Virar",
    "MH49" -> "Nagpur East",
    "MH50" -> "Karad"
  )

  val delhiRTO: Map[String, String] = Map(
    "DL01" -> "Civil Lines",
    "DL02" -> "IP Depot",
    "DL03" -> "Sheikh Sarai",
    "DL04" -> "Janakpuri",
    "DL05" -> "Loni Road",
    "DL06" -> "Sarai Kale Khan"
  )

  val karnatakaRTO: Map[String, String] = Map(
    "KA01" -> "Bengaluru Central",
    "KA02" -> "Bengaluru West",
    "KA03" -> "Bengaluru East",
    "KA09" -> "Mysuru West",
    "KA19" -> "Mangaluru"
  )

  val gujaratRTO: Map[String, String] = Map(
    "GJ01" -> "Ahmedabad",
    "GJ03" -> "Rajkot",
    "GJ05" -> "Surat",
    "GJ06" -> "Vadodara"
  )

  val uttarPradeshRTO: Map[String, String] = Map(
    "UP14" -> "Ghaziabad",
    "UP16" -> "Noida",
    "UP32" -> "Lucknow",
    "UP78" -> "Kanpur"
  )

  val madhyaPradeshRTO: Map[String, String] = Map(
    "MP04" -> "Bhopal",
    "MP09" -> "Indore",
    "MP20" -> "Jabalpur"
  )

  val masterDatabase: Map[String, String] =
    statesAndUTs ++
    maharashtraRTO ++
    delhiRTO ++
    karnatakaRTO ++
    gujaratRTO ++
    uttarPradeshRTO ++
    madhyaPradeshRTO

  // ---------------------------------------------------------------------------
  // Approximate visual coordinates
  // ---------------------------------------------------------------------------

  val coordinateMap: Map[String, MapCoordinate] = Map(
    "AP" -> MapCoordinate(0.46, 0.73),
    "AR" -> MapCoordinate(0.86, 0.33),
    "AS" -> MapCoordinate(0.79, 0.37),
    "BR" -> MapCoordinate(0.61, 0.43),
    "CG" -> MapCoordinate(0.51, 0.57),
    "GA" -> MapCoordinate(0.33, 0.72),
    "GJ" -> MapCoordinate(0.26, 0.49),
    "HR" -> MapCoordinate(0.36, 0.31),
    "HP" -> MapCoordinate(0.39, 0.22),
    "JH" -> MapCoordinate(0.62, 0.49),
    "KA" -> MapCoordinate(0.36, 0.76),
    "KL" -> MapCoordinate(0.38, 0.88),
    "MP" -> MapCoordinate(0.44, 0.49),
    "MH" -> MapCoordinate(0.38, 0.58),
    "MN" -> MapCoordinate(0.83, 0.42),
    "ML" -> MapCoordinate(0.76, 0.40),
    "MZ" -> MapCoordinate(0.81, 0.46),
    "NL" -> MapCoordinate(0.85, 0.37),
    "OD" -> MapCoordinate(0.58, 0.57),
    "PB" -> MapCoordinate(0.34, 0.26),
    "RJ" -> MapCoordinate(0.31, 0.39),
    "SK" -> MapCoordinate(0.69, 0.34),
    "TN" -> MapCoordinate(0.42, 0.85),
    "TS" -> MapCoordinate(0.45, 0.64),
    "TR" -> MapCoordinate(0.78, 0.44),
    "UK" -> MapCoordinate(0.43, 0.27),
    "UP" -> MapCoordinate(0.49, 0.39),
    "WB" -> MapCoordinate(0.68, 0.48),

    "DL" -> MapCoordinate(0.37, 0.30),
    "JK" -> MapCoordinate(0.35, 0.16),
    "LA" -> MapCoordinate(0.40, 0.13),
    "CH" -> MapCoordinate(0.36, 0.26),
    "DH" -> MapCoordinate(0.29, 0.58),
    "PY" -> MapCoordinate(0.44, 0.83),
    "LD" -> MapCoordinate(0.29, 0.86),
    "AN" -> MapCoordinate(0.79, 0.80),

    "MH01" -> MapCoordinate(0.30, 0.60),
    "MH02" -> MapCoordinate(0.30, 0.59),
    "MH03" -> MapCoordinate(0.31, 0.60),
    "MH04" -> MapCoordinate(0.32, 0.59),
    "MH05" -> MapCoordinate(0.33, 0.59),
    "MH06" -> MapCoordinate(0.31, 0.62),
    "MH07" -> MapCoordinate(0.33, 0.70),
    "MH08" -> MapCoordinate(0.32, 0.66),
    "MH09" -> MapCoordinate(0.34, 0.68),
    "MH10" -> MapCoordinate(0.36, 0.67),
    "MH11" -> MapCoordinate(0.34, 0.64),
    "MH12" -> MapCoordinate(0.34, 0.62),
    "MH13" -> MapCoordinate(0.39, 0.65),
    "MH14" -> MapCoordinate(0.34, 0.61),
    "MH15" -> MapCoordinate(0.33, 0.56),
    "MH16" -> MapCoordinate(0.36, 0.60),
    "MH17" -> MapCoordinate(0.36, 0.58),
    "MH18" -> MapCoordinate(0.34, 0.53),
    "MH19" -> MapCoordinate(0.37, 0.54),
    "MH20" -> MapCoordinate(0.38, 0.57),
    "MH21" -> MapCoordinate(0.40, 0.58),
    "MH22" -> MapCoordinate(0.42, 0.60),
    "MH23" -> MapCoordinate(0.39, 0.61),
    "MH24" -> MapCoordinate(0.41, 0.63),
    "MH25" -> MapCoordinate(0.39, 0.63),
    "MH26" -> MapCoordinate(0.44, 0.62),
    "MH27" -> MapCoordinate(0.43, 0.53),
    "MH28" -> MapCoordinate(0.40, 0.54),
    "MH29" -> MapCoordinate(0.44, 0.56),
    "MH30" -> MapCoordinate(0.41, 0.54),
    "MH31" -> MapCoordinate(0.49, 0.52),
    "MH32" -> MapCoordinate(0.47, 0.54),
    "MH33" -> MapCoordinate(0.51, 0.58),
    "MH34" -> MapCoordinate(0.48, 0.58),
    "MH35" -> MapCoordinate(0.52, 0.52),
    "MH36" -> MapCoordinate(0.50, 0.52),
    "MH37" -> MapCoordinate(0.42, 0.56),
    "MH38" -> MapCoordinate(0.43, 0.58),
    "MH39" -> MapCoordinate(0.33, 0.52),
    "MH40" -> MapCoordinate(0.49, 0.53),
    "MH41" -> MapCoordinate(0.33, 0.55),
    "MH42" -> MapCoordinate(0.35, 0.63),
    "MH43" -> MapCoordinate(0.31, 0.60),
    "MH44" -> MapCoordinate(0.40, 0.61),
    "MH45" -> MapCoordinate(0.38, 0.64),
    "MH46" -> MapCoordinate(0.32, 0.61),
    "MH47" -> MapCoordinate(0.30, 0.59),
    "MH48" -> MapCoordinate(0.30, 0.58),
    "MH49" -> MapCoordinate(0.50, 0.53),
    "MH50" -> MapCoordinate(0.34, 0.65),

    "DL01" -> MapCoordinate(0.37, 0.30),
    "DL04" -> MapCoordinate(0.36, 0.31),
    "KA01" -> MapCoordinate(0.37, 0.77),
    "KA09" -> MapCoordinate(0.36, 0.79),
    "GJ01" -> MapCoordinate(0.27, 0.49),
    "GJ05" -> MapCoordinate(0.28, 0.53),
    "UP32" -> MapCoordinate(0.52, 0.39),
    "UP14" -> MapCoordinate(0.38, 0.31),
    "MP04" -> MapCoordinate(0.42, 0.51),
    "MP09" -> MapCoordinate(0.38, 0.51)
  )
}

// =============================================================================
// 4. VECTOR MAP
// =============================================================================

class EnhancedIndiaMapPanel extends JPanel {

  private var activeCode = ""
  private var activeTitle = ""

  setDoubleBuffered(true)
  setBackground(AppConfig.CanvasBg)

  def setSelectedLocation(code: String, label: String): Unit = {
    activeCode = code
    activeTitle = label
    repaint()
  }

  def clearSelection(): Unit = {
    activeCode = ""
    activeTitle = ""
    repaint()
  }

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

    super.paintComponent(g)

    val g2 = g.asInstanceOf[Graphics2D]

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

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

    val w = getWidth
    val h = getHeight

    // Background grid
    g2.setColor(new Color(236, 240, 245))

    var gx = 0
    while (gx < w) {
      g2.drawLine(gx, 0, gx, h)
      gx += 40
    }

    var gy = 0
    while (gy < h) {
      g2.drawLine(0, gy, w, gy)
      gy += 40
    }

    // Header
    g2.setColor(AppConfig.PrimaryDark)
    g2.setFont(new Font("Arial", Font.BOLD, 18))
    g2.drawString("ALL INDIA VISUAL MAP ENGINE", 25, 30)

    g2.setColor(AppConfig.TextSecondary)
    g2.setFont(new Font("Arial", Font.PLAIN, 12))
    g2.drawString(
      "Territorial pinpointing & RTO coordinate tracker",
      25,
      48
    )

    val offsetTop = 60
    val offsetBottom = 40
    val offsetSide = 30

    val drawW = w - offsetSide * 2
    val drawH = h - offsetTop - offsetBottom

    val startX = offsetSide
    val startY = offsetTop

    // Approximate India outline
    val indiaShape = new Path2D.Double()

    val points = Array(
      (0.35, 0.08),
      (0.42, 0.12),
      (0.43, 0.20),
      (0.50, 0.25),
      (0.55, 0.32),
      (0.68, 0.33),
      (0.75, 0.30),
      (0.85, 0.32),
      (0.88, 0.38),
      (0.80, 0.42),
      (0.72, 0.44),
      (0.68, 0.52),
      (0.60, 0.58),
      (0.52, 0.72),
      (0.42, 0.92),
      (0.38, 0.94),
      (0.36, 0.88),
      (0.32, 0.75),
      (0.30, 0.65),
      (0.22, 0.58),
      (0.18, 0.50),
      (0.24, 0.42),
      (0.26, 0.30),
      (0.33, 0.18)
    )

    val first = points.head

    indiaShape.moveTo(
      startX + drawW * first._1,
      startY + drawH * first._2
    )

    for (pt <- points.tail) {
      indiaShape.lineTo(
        startX + drawW * pt._1,
        startY + drawH * pt._2
      )
    }

    indiaShape.closePath()

    // Shadow
    g2.setColor(new Color(0, 0, 0, 25))

    val shadowTransform =
      AffineTransform.getTranslateInstance(4, 4)

    g2.fill(
      shadowTransform.createTransformedShape(indiaShape)
    )

    // India
    g2.setColor(new Color(228, 241, 226))
    g2.fill(indiaShape)

    g2.setColor(new Color(95, 145, 100))
    g2.setStroke(new BasicStroke(2.0f))
    g2.draw(indiaShape)

    // Maharashtra highlight
    val mhArea = new Path2D.Double()

    val mhPts = Array(
      (0.30, 0.55),
      (0.42, 0.52),
      (0.50, 0.54),
      (0.48, 0.62),
      (0.38, 0.67),
      (0.32, 0.67),
      (0.30, 0.60)
    )

    mhArea.moveTo(
      startX + drawW * mhPts.head._1,
      startY + drawH * mhPts.head._2
    )

    for (pt <- mhPts.tail) {
      mhArea.lineTo(
        startX + drawW * pt._1,
        startY + drawH * pt._2
      )
    }

    mhArea.closePath()

    g2.setColor(new Color(254, 235, 185))
    g2.fill(mhArea)

    g2.setColor(new Color(214, 160, 50))
    g2.setStroke(
      new BasicStroke(
        1.5f,
        BasicStroke.CAP_ROUND,
        BasicStroke.JOIN_ROUND,
        0,
        Array(4.0f, 4.0f),
        0
      )
    )

    g2.draw(mhArea)

    // -------------------------------------------------------------------------
    // Pin
    // -------------------------------------------------------------------------

    val targetCoord =
      if (RTODatabase.coordinateMap.contains(activeCode)) {
        Some(RTODatabase.coordinateMap(activeCode))
      }
      else if (
        activeCode.length >= 2 &&
        RTODatabase.coordinateMap.contains(activeCode.substring(0, 2))
      ) {
        Some(
          RTODatabase.coordinateMap(
            activeCode.substring(0, 2)
          )
        )
      }
      else {
        None
      }

    if (activeCode.nonEmpty && targetCoord.isDefined) {

      val coord = targetCoord.get

      val pinPixelX =
        startX + (drawW * coord.xRatio).toInt

      val pinPixelY =
        startY + (drawH * coord.yRatio).toInt

      // Glow
      g2.setColor(new Color(231, 76, 60, 90))
      g2.fillOval(
        pinPixelX - 16,
        pinPixelY - 16,
        32,
        32
      )

      // Pin
      g2.setColor(AppConfig.DangerRed)

      g2.fillOval(
        pinPixelX - 7,
        pinPixelY - 7,
        14,
        14
      )

      g2.setColor(Color.WHITE)

      g2.fillOval(
        pinPixelX - 3,
        pinPixelY - 3,
        6,
        6
      )

      // Badge
      val badgeText =
        activeCode + " : " + activeTitle

      g2.setFont(
        new Font(
          "Arial",
          Font.BOLD,
          12
        )
      )

      val fm = g2.getFontMetrics

      val textW =
        fm.stringWidth(badgeText)

      val boxX = pinPixelX + 14
      val boxY = pinPixelY - 12
      val boxW = textW + 16
      val boxH = fm.getHeight + 6

      g2.setColor(AppConfig.PrimaryDark)

      g2.fillRoundRect(
        boxX,
        boxY,
        boxW,
        boxH,
        8,
        8
      )

      g2.setColor(Color.WHITE)

      g2.drawString(
        badgeText,
        boxX + 8,
        boxY + fm.getAscent + 2
      )

      g2.setColor(AppConfig.PrimaryDark)
      g2.setStroke(new BasicStroke(1.5f))

      g2.drawLine(
        pinPixelX,
        pinPixelY,
        boxX,
        boxY + boxH / 2
      )
    }

    // Legend
    val lx = w - 230
    val ly = h - 65

    g2.setColor(new Color(255, 255, 255, 220))

    g2.fillRoundRect(
      lx,
      ly,
      215,
      45,
      8,
      8
    )

    g2.setColor(new Color(200, 205, 215))

    g2.drawRoundRect(
      lx,
      ly,
      215,
      45,
      8,
      8
    )

    g2.setColor(AppConfig.DangerRed)

    g2.fillOval(
      lx + 12,
      ly + 16,
      12,
      12
    )

    g2.setColor(AppConfig.TextPrimary)
    g2.setFont(new Font("Arial", Font.PLAIN, 12))

    g2.drawString(
      "RTO Location",
      lx + 30,
      ly + 27
    )
  }
}

// =============================================================================
// 5. MAIN APPLICATION
// =============================================================================

class RTOMapApplication {

  val frame =
    new JFrame(AppConfig.AppTitle)

  frame.setSize(
    AppConfig.AppWidth,
    AppConfig.AppHeight
  )

  frame.setLocationRelativeTo(null)

  frame.setDefaultCloseOperation(
    WindowConstants.EXIT_ON_CLOSE
  )

  private val mapCanvas =
    new EnhancedIndiaMapPanel()

  private val searchTextField =
    new JTextField(10)

  private val searchResultArea =
    new JTextArea()

  private val historyTableModel =
    new DefaultTableModel()

  private val historyTable =
    new JTable(historyTableModel)

  private val statusLabel =
    new JLabel("Ready")

  private val resultCountBadge =
    new JLabel("0 results")

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

  def start(): Unit = {
    displayLoginScreen()
    frame.setVisible(true)
  }

  // ===========================================================================
  // LOGIN SCREEN
  // ===========================================================================

  def displayLoginScreen(): Unit = {

    frame.getContentPane.removeAll()

    val rootPanel =
      new JPanel(new BorderLayout())

    rootPanel.setBackground(
      AppConfig.CanvasBg
    )

    val headerPanel =
      new JPanel(
        new FlowLayout(
          FlowLayout.CENTER,
          20,
          25
        )
      )

    headerPanel.setBackground(
      AppConfig.PrimaryDark
    )

    val appHeaderTitle =
      new JLabel(
        "INDIA RTO DIRECTORY & GEOLOCATION SUITE"
      )

    appHeaderTitle.setFont(
      new Font(
        "Arial",
        Font.BOLD,
        24
      )
    )

    appHeaderTitle.setForeground(
      Color.WHITE
    )

    headerPanel.add(appHeaderTitle)

    rootPanel.add(
      headerPanel,
      BorderLayout.NORTH
    )

    val centerContainer =
      new JPanel(
        new GridBagLayout()
      )

    centerContainer.setOpaque(false)

    val cardPanel =
      new JPanel()

    cardPanel.setLayout(
      new BoxLayout(
        cardPanel,
        BoxLayout.Y_AXIS
      )
    )

    cardPanel.setBackground(
      Color.WHITE
    )

    cardPanel.setBorder(
      BorderFactory.createCompoundBorder(
        new LineBorder(
          new Color(220, 226, 235),
          1,
          true
        ),
        new EmptyBorder(
          35,
          45,
          35,
          45
        )
      )
    )

    val lockSymbol =
      new JLabel("LOCKED")

    lockSymbol.setFont(
      new Font(
        "Arial",
        Font.BOLD,
        28
      )
    )

    lockSymbol.setAlignmentX(
      Component.CENTER_ALIGNMENT
    )

    val loginPrompt =
      new JLabel(
        "Secure Login"
      )

    loginPrompt.setFont(
      new Font(
        "Arial",
        Font.BOLD,
        20
      )
    )

    loginPrompt.setForeground(
      AppConfig.PrimaryDark
    )

    loginPrompt.setAlignmentX(
      Component.CENTER_ALIGNMENT
    )

    val hintText =
      new JLabel(
        "Enter your password to continue"
      )

    hintText.setFont(
      new Font(
        "Arial",
        Font.PLAIN,
        13
      )
    )

    hintText.setForeground(
      AppConfig.TextSecondary
    )

    hintText.setAlignmentX(
      Component.CENTER_ALIGNMENT
    )

    val passwordField =
      new JPasswordField(16)

    passwordField.setFont(
      new Font(
        "Arial",
        Font.PLAIN,
        18
      )
    )

    passwordField.setMaximumSize(
      new Dimension(300, 42)
    )

    passwordField.setPreferredSize(
      new Dimension(300, 42)
    )

    passwordField.setHorizontalAlignment(
      SwingConstants.CENTER
    )

    // Password characters are hidden
    passwordField.setEchoChar('?')

    passwordField.setAlignmentX(
      Component.CENTER_ALIGNMENT
    )

    val errorNotice =
      new JLabel(" ")

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

    errorNotice.setForeground(
      AppConfig.DangerRed
    )

    errorNotice.setAlignmentX(
      Component.CENTER_ALIGNMENT
    )

    val submitButton =
      new JButton("LOGIN")

    submitButton.setFont(
      new Font(
        "Arial",
        Font.BOLD,
        15
      )
    )

    submitButton.setForeground(
      Color.WHITE
    )

    submitButton.setBackground(
      AppConfig.AccentBlue
    )

    submitButton.setFocusPainted(false)

    submitButton.setPreferredSize(
      new Dimension(300, 42)
    )

    submitButton.setMaximumSize(
      new Dimension(300, 42)
    )

    submitButton.setAlignmentX(
      Component.CENTER_ALIGNMENT
    )

    submitButton.setCursor(
      new Cursor(
        Cursor.HAND_CURSOR
      )
    )

    cardPanel.add(lockSymbol)
    cardPanel.add(
      Box.createVerticalStrut(12)
    )

    cardPanel.add(loginPrompt)

    cardPanel.add(
      Box.createVerticalStrut(6)
    )

    cardPanel.add(hintText)

    cardPanel.add(
      Box.createVerticalStrut(20)
    )

    cardPanel.add(passwordField)

    cardPanel.add(
      Box.createVerticalStrut(10)
    )

    cardPanel.add(errorNotice)

    cardPanel.add(
      Box.createVerticalStrut(10)
    )

    cardPanel.add(submitButton)

    centerContainer.add(cardPanel)

    rootPanel.add(
      centerContainer,
      BorderLayout.CENTER
    )

    def performAuthentication(): Unit = {

      val enteredString =
        new String(
          passwordField.getPassword
        )

      if (
        CryptoUtil.verifyPassword(
          enteredString,
          AppConfig.AuthPasswordHash
        )
      ) {

        displayMainApplicationView()

      } else {

        errorNotice.setText(
          "Incorrect password!"
        )

        passwordField.setText("")

        passwordField.requestFocus()
      }
    }

    submitButton.addActionListener(
      (_: ActionEvent) =>
        performAuthentication()
    )

    passwordField.addActionListener(
      (_: ActionEvent) =>
        performAuthentication()
    )

    frame.setContentPane(rootPanel)

    frame.revalidate()
    frame.repaint()

    passwordField.requestFocus()
  }

  // ===========================================================================
  // MAIN VIEW
  // ===========================================================================

  def displayMainApplicationView(): Unit = {

    frame.getContentPane.removeAll()

    val rootPanel =
      new JPanel(
        new BorderLayout()
      )

    rootPanel.setBackground(
      AppConfig.CanvasBg
    )

    rootPanel.add(
      constructTopNavigationBar(),
      BorderLayout.NORTH
    )

    rootPanel.add(
      constructCentralWorkspace(),
      BorderLayout.CENTER
    )

    rootPanel.add(
      constructBottomStatusBar(),
      BorderLayout.SOUTH
    )

    frame.setContentPane(rootPanel)

    frame.revalidate()
    frame.repaint()

    searchTextField.requestFocus()
  }

  // ===========================================================================
  // TOP BAR
  // ===========================================================================

  private def constructTopNavigationBar(): JPanel = {

    val topPanel =
      new JPanel(
        new BorderLayout()
      )

    topPanel.setBackground(
      AppConfig.PrimaryDark
    )

    topPanel.setBorder(
      new EmptyBorder(
        12,
        20,
        12,
        20
      )
    )

    val brandBox =
      new JPanel(
        new GridLayout(2, 1, 0, 2)
      )

    brandBox.setOpaque(false)

    val brandTitle =
      new JLabel(
        "ALL INDIA RTO INFORMATION & MAP SYSTEM"
      )

    brandTitle.setFont(
      new Font(
        "Arial",
        Font.BOLD,
        20
      )
    )

    brandTitle.setForeground(
      Color.WHITE
    )

    val brandSubtitle =
      new JLabel(
        "India States, Union Territories & RTO Code Directory"
      )

    brandSubtitle.setFont(
      new Font(
        "Arial",
        Font.PLAIN,
        12
      )
    )

    brandSubtitle.setForeground(
      new Color(
        189,
        195,
        199
      )
    )

    brandBox.add(brandTitle)
    brandBox.add(brandSubtitle)

    topPanel.add(
      brandBox,
      BorderLayout.WEST
    )

    val searchBarPanel =
      new JPanel(
        new FlowLayout(
          FlowLayout.RIGHT,
          10,
          5
        )
      )

    searchBarPanel.setOpaque(false)

    val searchPromptLabel =
      new JLabel(
        "State / RTO Code:"
      )

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

    searchPromptLabel.setForeground(
      Color.WHITE
    )

    searchTextField.setFont(
      new Font(
        "Arial",
        Font.BOLD,
        16
      )
    )

    searchTextField.setPreferredSize(
      new Dimension(130, 36)
    )

    searchTextField.setHorizontalAlignment(
      SwingConstants.CENTER
    )

    searchTextField.setToolTipText(
      "Examples: GJ, KA, UP, DL, MH12"
    )

    val searchActionBtn =
      new JButton("SEARCH")

    styleButton(
      searchActionBtn,
      AppConfig.AccentBlue,
      Color.WHITE
    )

    val resetActionBtn =
      new JButton("CLEAR")

    styleButton(
      resetActionBtn,
      new Color(127, 140, 141),
      Color.WHITE
    )

    val logoutActionBtn =
      new JButton("LOGOUT")

    styleButton(
      logoutActionBtn,
      AppConfig.DangerRed,
      Color.WHITE
    )

    searchBarPanel.add(
      searchPromptLabel
    )

    searchBarPanel.add(
      searchTextField
    )

    searchBarPanel.add(
      searchActionBtn
    )

    searchBarPanel.add(
      resetActionBtn
    )

    searchBarPanel.add(
      logoutActionBtn
    )

    topPanel.add(
      searchBarPanel,
      BorderLayout.EAST
    )

    searchActionBtn.addActionListener(
      (_: ActionEvent) =>
        executeLookup()
    )

    searchTextField.addActionListener(
      (_: ActionEvent) =>
        executeLookup()
    )

    resetActionBtn.addActionListener(
      (_: ActionEvent) =>
        clearAllFields()
    )

    logoutActionBtn.addActionListener(
      (_: ActionEvent) =>
        displayLoginScreen()
    )

    topPanel
  }

  // ===========================================================================
  // BUTTON STYLE
  // ===========================================================================

  private def styleButton(
    btn: JButton,
    bg: Color,
    fg: Color
  ): Unit = {

    btn.setBackground(bg)
    btn.setForeground(fg)

    btn.setFont(
      new Font(
        "Arial",
        Font.BOLD,
        12
      )
    )

    btn.setFocusPainted(false)

    btn.setBorder(
      new EmptyBorder(
        8,
        14,
        8,
        14
      )
    )

    btn.setCursor(
      new Cursor(
        Cursor.HAND_CURSOR
      )
    )
  }

  // ===========================================================================
  // CENTRAL WORKSPACE
  // ===========================================================================

  private def constructCentralWorkspace(): JPanel = {

    val workspacePanel =
      new JPanel(
        new GridLayout(
          1,
          2,
          10,
          0
        )
      )

    workspacePanel.setBackground(
      AppConfig.CanvasBg
    )

    workspacePanel.setBorder(
      new EmptyBorder(
        10,
        15,
        10,
        15
      )
    )

    workspacePanel.add(
      constructDataExplorerPanel()
    )

    workspacePanel.add(
      mapCanvas
    )

    workspacePanel
  }

  // ===========================================================================
  // DATA EXPLORER
  // ===========================================================================

  private def constructDataExplorerPanel(): JPanel = {

    val explorerPanel =
      new JPanel(
        new BorderLayout(
          0,
          10
        )
      )

    explorerPanel.setOpaque(false)

    val resultWrapper =
      new JPanel(
        new BorderLayout()
      )

    resultWrapper.setBackground(
      Color.WHITE
    )

    resultWrapper.setBorder(
      BorderFactory.createCompoundBorder(
        new LineBorder(
          new Color(
            218,
            225,
            233
          ),
          1,
          true
        ),
        new EmptyBorder(
          10,
          10,
          10,
          10
        )
      )
    )

    val resultHeader =
      new JPanel(
        new BorderLayout()
      )

    resultHeader.setOpaque(false)

    val resultTitle =
      new JLabel(
        "SEARCH OUTPUT"
      )

    resultTitle.setFont(
      new Font(
        "Arial",
        Font.BOLD,
        15
      )
    )

    resultTitle.setForeground(
      AppConfig.PrimaryDark
    )

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

    resultCountBadge.setForeground(
      AppConfig.AccentBlue
    )

    resultHeader.add(
      resultTitle,
      BorderLayout.WEST
    )

    resultHeader.add(
      resultCountBadge,
      BorderLayout.EAST
    )

    resultWrapper.add(
      resultHeader,
      BorderLayout.NORTH
    )

    searchResultArea.setEditable(false)

    searchResultArea.setLineWrap(true)

    searchResultArea.setWrapStyleWord(true)

    searchResultArea.setFont(
      new Font(
        "Monospaced",
        Font.PLAIN,
        13
      )
    )

    searchResultArea.setBackground(
      new Color(
        250,
        252,
        255
      )
    )

    searchResultArea.setBorder(
      new EmptyBorder(
        8,
        8,
        8,
        8
      )
    )

    populateDefaultInstructions()

    val textScroll =
      new JScrollPane(
        searchResultArea
      )

    textScroll.setBorder(
      new LineBorder(
        new Color(
          230,
          235,
          245
        ),
        1
      )
    )

    resultWrapper.add(
      textScroll,
      BorderLayout.CENTER
    )

    explorerPanel.add(
      resultWrapper,
      BorderLayout.CENTER
    )

    // History
    val historyPanel =
      new JPanel(
        new BorderLayout(
          0,
          5
        )
      )

    historyPanel.setPreferredSize(
      new Dimension(
        500,
        210
      )
    )

    historyPanel.setBackground(
      Color.WHITE
    )

    historyPanel.setBorder(
      BorderFactory.createCompoundBorder(
        new LineBorder(
          new Color(
            218,
            225,
            233
          ),
          1,
          true
        ),
        new EmptyBorder(
          8,
          10,
          8,
          10
        )
      )
    )

    val historyTitle =
      new JLabel(
        "RECENT SEARCHES"
      )

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

    historyTitle.setForeground(
      AppConfig.PrimaryDark
    )

    historyPanel.add(
      historyTitle,
      BorderLayout.NORTH
    )

    historyTableModel.addColumn("Time")
    historyTableModel.addColumn("Code")
    historyTableModel.addColumn("Location")
    historyTableModel.addColumn("Type")

    historyTable.setFont(
      new Font(
        "Arial",
        Font.PLAIN,
        11
      )
    )

    historyTable.setRowHeight(22)

    historyTable.getTableHeader.setFont(
      new Font(
        "Arial",
        Font.BOLD,
        11
      )
    )

    historyTable.getTableHeader.setBackground(
      new Color(
        240,
        243,
        248
      )
    )

    val tableScroll =
      new JScrollPane(
        historyTable
      )

    tableScroll.setBorder(
      new LineBorder(
        new Color(
          230,
          235,
          245
        ),
        1
      )
    )

    historyPanel.add(
      tableScroll,
      BorderLayout.CENTER
    )

    explorerPanel.add(
      historyPanel,
      BorderLayout.SOUTH
    )

    explorerPanel
  }

  // ===========================================================================
  // STATUS BAR
  // ===========================================================================

  private def constructBottomStatusBar(): JPanel = {

    val bar =
      new JPanel(
        new BorderLayout()
      )

    bar.setBackground(
      Color.WHITE
    )

    bar.setBorder(
      BorderFactory.createCompoundBorder(
        new MatteBorder(
          1,
          0,
          0,
          0,
          new Color(
            220,
            225,
            230
          )
        ),
        new EmptyBorder(
          6,
          20,
          6,
          20
        )
      )
    )

    statusLabel.setFont(
      new Font(
        "Arial",
        Font.BOLD,
        12
      )
    )

    statusLabel.setForeground(
      AppConfig.AccentGreen
    )

    bar.add(
      statusLabel,
      BorderLayout.WEST
    )

    val systemInfo =
      new JLabel(
        "Active Records: " +
        RTODatabase.masterDatabase.size +
        " | Vector Engine Active"
      )

    systemInfo.setFont(
      new Font(
        "Arial",
        Font.PLAIN,
        11
      )
    )

    systemInfo.setForeground(
      AppConfig.TextSecondary
    )

    bar.add(
      systemInfo,
      BorderLayout.EAST
    )

    bar
  }

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

  def executeLookup(): Unit = {

    val rawInput =
      searchTextField.getText

    val query =
      rawInput
        .trim
        .toUpperCase
        .replaceAll(
          "\\s+",
          ""
        )

    if (query.isEmpty) {

      statusLabel.setText(
        "Please enter a state or RTO code."
      )

      statusLabel.setForeground(
        AppConfig.DangerRed
      )

      searchResultArea.setText(
        """SEARCH HELP
          |
          |Try:
          |  GJ
          |  KA
          |  UP
          |  DL
          |  MH
          |  MH12
          |  MH14
          |""".stripMargin
      )

      mapCanvas.clearSelection()

      resultCountBadge.setText(
        "0 results"
      )

      return
    }

    val timestamp =
      new SimpleDateFormat(
        "HH:mm:ss"
      ).format(
        new Date()
      )

    if (
      RTODatabase.masterDatabase.contains(query)
    ) {

      val locationName =
        RTODatabase.masterDatabase(query)

      val isStateOnly =
        RTODatabase.statesAndUTs.contains(query)

      val childCodes =
        RTODatabase.masterDatabase.keys
          .filter(
            k =>
              k.startsWith(query) &&
              k != query
          )
          .toSeq
          .sorted

      val sb =
        new StringBuilder()

      sb.append(
        "=================================================================\n"
      )

      sb.append(
        "                       RTO SEARCH RESULT\n"
      )

      sb.append(
        "=================================================================\n\n"
      )

      sb.append(
        "Code      : " +
        query +
        "\n"
      )

      sb.append(
        "Location  : " +
        locationName +
        "\n"
      )

      sb.append(
        "Category  : " +
        (
          if (isStateOnly)
            "State / Union Territory"
          else
            "RTO Office"
        ) +
        "\n"
      )

      sb.append(
        "Time      : " +
        timestamp +
        "\n\n"
      )

      if (childCodes.nonEmpty) {

        sb.append(
          "-----------------------------------------------------------------\n"
        )

        sb.append(
          "Child RTO / Records (" +
          childCodes.size +
          "):\n"
        )

        sb.append(
          "-----------------------------------------------------------------\n"
        )

        for (c <- childCodes) {

          val subName =
            RTODatabase.masterDatabase(c)

          sb.append(
            "%-8s -> %s\n"
              .format(
                c,
                subName
              )
          )
        }

      } else {

        sb.append(
          "No child RTO records available.\n"
        )
      }

      sb.append(
        "\n=================================================================\n"
      )

      sb.append(
        "Vector map pin updated for the selected location."
      )

      searchResultArea.setText(
        sb.toString()
      )

      searchResultArea.setCaretPosition(0)

      mapCanvas.setSelectedLocation(
        query,
        locationName
      )

      val recType =
        if (isStateOnly)
          "State Authority"
        else
          "District RTO"

      historyTableModel.insertRow(
        0,
        Array[AnyRef](
          timestamp,
          query,
          locationName,
          recType
        )
      )

      statusLabel.setText(
        "Found: " +
        query +
        " -> " +
        locationName
      )

      statusLabel.setForeground(
        AppConfig.AccentGreen
      )

      resultCountBadge.setText(
        (childCodes.size + 1) +
        " records"
      )

    } else {

      val partialMatches =
        RTODatabase.masterDatabase.keys
          .filter(
            _.startsWith(query)
          )
          .toSeq
          .sorted

      if (partialMatches.nonEmpty) {

        val sb =
          new StringBuilder()

        sb.append(
          "=================================================================\n"
        )

        sb.append(
          "                       PARTIAL MATCHES\n"
        )

        sb.append(
          "=================================================================\n\n"
        )

        sb.append(
          "Matches for '" +
          query +
          "':\n\n"
        )

        for (m <- partialMatches) {

          val loc =
            RTODatabase.masterDatabase(m)

          sb.append(
            "%-8s -> %s\n"
              .format(
                m,
                loc
              )
          )
        }

        sb.append(
          "\nSelect one of the codes above and search again."
        )

        searchResultArea.setText(
          sb.toString()
        )

        searchResultArea.setCaretPosition(0)

        mapCanvas.clearSelection()

        historyTableModel.insertRow(
          0,
          Array[AnyRef](
            timestamp,
            query,
            partialMatches.size +
            " partial matches",
            "Partial Match"
          )
        )

        statusLabel.setText(
          partialMatches.size +
          " partial matches found."
        )

        statusLabel.setForeground(
          AppConfig.AccentBlue
        )

        resultCountBadge.setText(
          partialMatches.size +
          " matches"
        )

      } else {

        searchResultArea.setText(
          "'" +
          query +
          "' was not found.\n\n" +
          "Examples: GJ, KA, UP, DL, MP, RJ, TN, MH12"
        )

        searchResultArea.setCaretPosition(0)

        mapCanvas.clearSelection()

        historyTableModel.insertRow(
          0,
          Array[AnyRef](
            timestamp,
            query,
            "Not Found",
            "Invalid Code"
          )
        )

        statusLabel.setText(
          "'" +
          query +
          "' not found."
        )

        statusLabel.setForeground(
          AppConfig.DangerRed
        )

        resultCountBadge.setText(
          "0 records"
        )
      }
    }
  }

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

  private def clearAllFields(): Unit = {

    searchTextField.setText("")

    populateDefaultInstructions()

    mapCanvas.clearSelection()

    statusLabel.setText(
      "Search cleared."
    )

    statusLabel.setForeground(
      AppConfig.TextSecondary
    )

    resultCountBadge.setText(
      "0 results"
    )

    searchTextField.requestFocus()
  }

  // ===========================================================================
  // DEFAULT HELP
  // ===========================================================================

  private def populateDefaultInstructions(): Unit = {

    val defaultText =
      """=================================================================
        |                  INDIA RTO DIRECTORY
        |=================================================================
        |
        |HOW TO SEARCH
        |
        |1. Enter a State / UT code or RTO code.
        |2. Press SEARCH or Enter.
        |3. The matching location will appear on the map.
        |
        |STATE EXAMPLES
        |
        |GJ  = Gujarat
        |KA  = Karnataka
        |UP  = Uttar Pradesh
        |MP  = Madhya Pradesh
        |DL  = Delhi
        |RJ  = Rajasthan
        |MH  = Maharashtra
        |
        |MAHARASHTRA RTO EXAMPLES
        |
        |MH01 = Mumbai Central
        |MH12 = Pune City
        |MH14 = Pimpri-Chinchwad
        |MH15 = Nashik
        |MH31 = Nagpur Urban
        |
        |The map displays an approximate visual pin for the
        |selected state / RTO location.
        |
        |================================================================="""
        .stripMargin

    searchResultArea.setText(
      defaultText
    )

    searchResultArea.setCaretPosition(0)
  }
}

// =============================================================================
// 6. APPLICATION ENTRY POINT
// =============================================================================

val appInstance =
  new RTOMapApplication()

appInstance.start()