Code Sketch


graphic_7325
By: Mhalsakant School
Category: Art
// =============================================================================
// PROGRAM NAME : Self-Contained Vector India & Maharashtra RTO Map Application
// ENVIRONMENT  : Kojo 5 / Scala Swing Desktop GUI
// PURPOSE      : Built-in Graphic Map, Secure Login, Full RTO Directory
// =============================================================================

import javax.swing._
import javax.swing.border._
import javax.swing.table.DefaultTableModel
import java.awt._
import java.awt.event._
import java.awt.image.BufferedImage
import java.awt.geom._
import java.security.MessageDigest
import java.text.SimpleDateFormat
import java.util.Date

// =============================================================================
// 1. CONFIGURATION & THEME
// =============================================================================

object AppConfig {
  val AppTitle: String = "?? INDIA RTO MAP APPLICATION - KOJO 5"
  val AppWidth: Int = 1200
  val AppHeight: Int = 780
  
  // Password hash for 'aa7313'
  val AuthPasswordHash: String = "767623b9dc3820dcaca125ad24862a09"
  
  val PrimaryDark: Color   = new Color(24, 43, 73)
  val AccentBlue: Color    = new Color(41, 128, 185)
  val AccentGreen: Color   = new Color(39, 174, 96)
  val DangerRed: Color     = new Color(192, 57, 43)
  val CanvasBg: Color      = new Color(245, 248, 252)
  val TextPrimary: Color   = new Color(33, 33, 33)
  val TextSecondary: Color = new Color(110, 110, 110)
}

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

// =============================================================================
// 2. MASTER DATABASE (STATES, UTS & ALL MAHARASHTRA RTOS)
// =============================================================================

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",
    "CH" -> "Chandigarh", "DH" -> "Dadra, Nagar Haveli & 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 (Aurangabad)",
    "MH21" -> "Jalna",
    "MH22" -> "Parbhani",
    "MH23" -> "Beed",
    "MH24" -> "Latur",
    "MH25" -> "Dharashiv (Osmanabad)",
    "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 (Nashik Rural)",
    "MH42" -> "Baramati (Pune Rural)",
    "MH43" -> "Navi Mumbai (Vashi)",
    "MH44" -> "Ambejogai (Beed Rural)",
    "MH45" -> "Akluj (Solapur Rural)",
    "MH46" -> "Panvel (Navi Mumbai South)",
    "MH47" -> "Borivali (Mumbai Suburb)",
    "MH48" -> "Vasai-Virar (Palghar)",
    "MH49" -> "Nagpur East",
    "MH50" -> "Karad (Satara Rural)"
  )

  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 masterDatabase: Map[String, String] =
    statesAndUTs ++ maharashtraRTO ++ delhiRTO ++ karnatakaRTO ++ gujaratRTO

  // ????????? ???? ???????? ????? (X%, Y%)
  val coordinateMap: Map[String, MapCoordinate] = Map(
    "MH"   -> MapCoordinate(0.38, 0.58), "DL"   -> MapCoordinate(0.37, 0.30),
    "KA"   -> MapCoordinate(0.36, 0.76), "GJ"   -> MapCoordinate(0.26, 0.49),
    "MP"   -> MapCoordinate(0.44, 0.49), "UP"   -> MapCoordinate(0.48, 0.38),
    "RJ"   -> MapCoordinate(0.31, 0.38), "TN"   -> MapCoordinate(0.42, 0.85),
    "KL"   -> MapCoordinate(0.37, 0.86), "AP"   -> MapCoordinate(0.45, 0.72),
    "TS"   -> MapCoordinate(0.44, 0.64), "WB"   -> MapCoordinate(0.68, 0.48),
    "PB"   -> MapCoordinate(0.34, 0.25), "HR"   -> MapCoordinate(0.36, 0.28),

    // Maharashtra Pins
    "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), "KA01" -> MapCoordinate(0.37, 0.77),
    "GJ01" -> MapCoordinate(0.27, 0.49)
  )
}

// =============================================================================
// 3. SECURITY UTILITY (MD5 PASSWORD CHECK)
// =============================================================================

object CryptoUtil {
  def md5Hex(text: String): String = {
    try {
      val md = MessageDigest.getInstance("MD5")
      val digestBytes = md.digest(text.getBytes("UTF-8"))
      digestBytes.map(b => "%02x".format(b & 255)).mkString
    } catch {
      case _: Exception => ""
    }
  }

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

// =============================================================================
// 4. BUILT-IN VECTOR MAP PANEL (???? ?????? ??? ????)
// =============================================================================

class EnhancedIndiaMapPanel extends JPanel {
  private var activeCode: String = ""
  private var activeTitle: String = ""

  setDoubleBuffered(true)
  setBackground(AppConfig.CanvasBg)

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

  def clearSelection(): Unit = {
    this.activeCode = ""
    this.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)
    g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON)

    val w = getWidth
    val h = getHeight

    // ?. ??????????? ????? (Grid Background)
    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 }

    // ?. ???? ?????
    g2.setColor(AppConfig.PrimaryDark)
    g2.setFont(new Font("Arial", Font.BOLD, 18))
    g2.drawString("GEOGRAPHIC VISUALIZATION ENGINE", 25, 30)

    g2.setColor(AppConfig.TextSecondary)
    g2.setFont(new Font("Arial", Font.PLAIN, 12))
    g2.drawString("Live territorial pinpointing & 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

    // ?. ??????? ????? ???? (Built-in India Vector Map)
    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()

    // ????? (Drop Shadow)
    g2.setColor(new Color(0, 0, 0, 25))
    val shadowTransform = AffineTransform.getTranslateInstance(4, 4)
    g2.fill(shadowTransform.createTransformedShape(indiaShape))

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

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

    // ????????????? ?????? ??????? (Highlight Maharashtra Region)
    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)

    // ?. ?????????? RTO ?? ??? ?????? (Pin Marker)
    if (activeCode.nonEmpty && RTODatabase.coordinateMap.contains(activeCode)) {
      val coord = RTODatabase.coordinateMap(activeCode)
      val pinPixelX = startX + (drawW * coord.xRatio).toInt
      val pinPixelY = startY + (drawH * coord.yRatio).toInt

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

      // ????? ??? ?????
      g2.setColor(new Color(192, 57, 43))
      g2.fillOval(pinPixelX - 7, pinPixelY - 7, 14, 14)
      g2.setColor(Color.WHITE)
      g2.fillOval(pinPixelX - 3, pinPixelY - 3, 6, 6)

      // ???? ??? (Name Badge)
      val badgeText = s"? $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 Box)
    val lx = w - 220
    val ly = h - 65
    g2.setColor(new Color(255, 255, 255, 220))
    g2.fillRoundRect(lx, ly, 205, 45, 8, 8)
    g2.setColor(new Color(200, 205, 215))
    g2.drawRoundRect(lx, ly, 205, 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("???????? ??.??.?. ?????", lx + 30, ly + 27)
  }
}

// =============================================================================
// 5. APPLICATION CONTROLLER & GUI VIEW
// =============================================================================

class RTOMapApplication {

  val frame: JFrame = new JFrame(AppConfig.AppTitle)
  frame.setSize(AppConfig.AppWidth, AppConfig.AppHeight)
  frame.setLocationRelativeTo(null)
  frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)

  private val mapCanvas: EnhancedIndiaMapPanel = new EnhancedIndiaMapPanel()
  private val searchTextField: JTextField = new JTextField(10)
  private val searchResultArea: JTextArea = new JTextArea()
  private val historyTableModel: DefaultTableModel = new DefaultTableModel()
  private val historyTable: JTable = new JTable(historyTableModel)
  private val statusLabel: JLabel = new JLabel("???? (Ready)")
  private val resultCountBadge: JLabel = new JLabel("0 ????? ????????")

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

  // -----------------------------------------------------------
  // 5.1 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("?")
    lockSymbol.setFont(new Font("Segoe UI Emoji", Font.PLAIN, 46))
    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("?????? ?????????? ??????? ???? ???:")
    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))
    // SwingConstants.CENTER ?????????? ????? ??? ??? ????
    passwordField.setHorizontalAlignment(SwingConstants.CENTER)
    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("? ?????? ???????! ????? ????? ??????? ????.")
        passwordField.setText("")
        passwordField.requestFocus()
      }
    }

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

    frame.setContentPane(rootPanel)
    frame.revalidate()
    frame.repaint()
    passwordField.requestFocus()
  }

  // -----------------------------------------------------------
  // 5.2 MAIN DASHBOARD
  // -----------------------------------------------------------
  def displayMainApplicationView(): Unit = {
    frame.getContentPane.removeAll()

    val rootPanel = new JPanel(new BorderLayout(0, 0))
    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()
  }

  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("?? INDIA RTO INFORMATION & MAP SYSTEM")
    brandTitle.setFont(new Font("Arial", Font.BOLD, 20))
    brandTitle.setForeground(Color.WHITE)

    val brandSubtitle = new JLabel("????? ???, ????? ??? ?????? ??? ??????? | Kojo Platform")
    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("RTO / State 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))
    // SwingConstants.CENTER ?????????? ??????? ??? ????
    searchTextField.setHorizontalAlignment(SwingConstants.CENTER)
    searchTextField.setToolTipText("???. MH12, MH, DL, KA")

    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
  }

  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))
  }

  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
  }

  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 Table
    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
  }

  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
  }

  // -----------------------------------------------------------
  // 5.3 BUSINESS LOGIC (SEARCH & LOCATION PINNING)
  // -----------------------------------------------------------
  def executeLookup(): Unit = {
    val rawInput = searchTextField.getText
    val query = rawInput.trim.toUpperCase.replaceAll("\\s+", "")

    if (query.isEmpty) {
      statusLabel.setText("?? ????? ???? ?????????? ??.??.?. ??? ????.")
      statusLabel.setForeground(AppConfig.DangerRed)
      searchResultArea.setText("?????:\n????? ?? ???????? ?????????? ????? ??? ???? ???.\n???: MH12, MH14, MH, DL, ???????.")
      mapCanvas.clearSelection()
      resultCountBadge.setText("0 ????? ????????")
      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 ?????? ??? ?????                          \n")
      sb.append("=================================================================\n\n")
      sb.append(s" ? ?????? ??? (Code)      : $query\n")
      sb.append(s" ? ????????????? (Location) : $locationName\n")
      sb.append(s" ? ?????? (Category)        : " + (if (isStateOnly) "????? / ??????????? ??????" else "?????? ??.??.?. ????????") + "\n")
      sb.append(s" ? ??? ??? (Query Time)    : $timestamp\n\n")

      if (childCodes.nonEmpty) {
        sb.append("-----------------------------------------------------------------\n")
        sb.append(s" ?? ??????? ?????? ???? ??-??? (${childCodes.size} ?????????):\n")
        sb.append("-----------------------------------------------------------------\n")
        for (c <- childCodes) {
          val subName = RTODatabase.masterDatabase(c)
          sb.append(s"  %-8s ->  %s\n".format(c, subName))
        }
      } else {
        sb.append("???: ?? ??? ????????? ???? ?????? ???????? (RTO Office) ???.\n")
      }

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

      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(s"? ?????? ???: $query -> $locationName")
      statusLabel.setForeground(AppConfig.AccentGreen)
      resultCountBadge.setText(s"${childCodes.size + 1} ????? ??????")

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

      if (partialMatches.nonEmpty) {
        val sb = new StringBuilder()
        sb.append("=================================================================\n")
        sb.append("              ??????? ??.??.?. ??????? ????                     \n")
        sb.append("=================================================================\n\n")
        sb.append(s"??????? '${query}' ?? ????????? ????? ????? ?????? ????:\n\n")

        for (m <- partialMatches) {
          val loc = RTODatabase.masterDatabase(m)
          sb.append(s"  %-8s ->  %s\n".format(m, loc))
        }

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

        searchResultArea.setText(sb.toString())
        searchResultArea.setCaretPosition(0)
        mapCanvas.clearSelection()

        historyTableModel.insertRow(0, Array[AnyRef](timestamp, query, s"${partialMatches.size} ??????? ?????", "Partial Match"))

        statusLabel.setText(s"?? ??????? ${partialMatches.size} ????? ??????.")
        statusLabel.setForeground(AppConfig.AccentBlue)
        resultCountBadge.setText(s"${partialMatches.size} ?????? ??????")

      } else {
        searchResultArea.setText(
          s"? '$query' ?? ??? ???????????? ?????? ????.\n\n????? MH12, MH14, MH, DL, KA ??? ??? ??? ????? ??????? ???."
        )
        searchResultArea.setCaretPosition(0)
        mapCanvas.clearSelection()

        historyTableModel.insertRow(0, Array[AnyRef](timestamp, query, "Not Found", "Invalid Code"))
        statusLabel.setText(s"? '$query' ?? ??? ?????? ????!")
        statusLabel.setForeground(AppConfig.DangerRed)
        resultCountBadge.setText("0 ?????")
      }
    }
  }

  private def clearAllFields(): Unit = {
    searchTextField.setText("")
    populateDefaultInstructions()
    mapCanvas.clearSelection()
    statusLabel.setText("??? ??? ???? (Search Cleared)")
    statusLabel.setForeground(AppConfig.TextSecondary)
    resultCountBadge.setText("????")
    searchTextField.requestFocus()
  }

  private def populateDefaultInstructions(): Unit = {
    val defaultText =
      """=================================================================
        |              ?? ???? ??? ?????????? RTO ??? ??????
        |=================================================================
        |
        |??? ??????:
        | 1. ???? ???? ?????????? ??????? ????? ????? ????? ??? ???? ???.
        | 2. 'SEARCH' ??? ???? ????? ??????????? Enter ?? ????.
        | 3. ??????? ????? ??? ???????? ????? ?????? ?????? ?????.
        |
        |???? ?????????? ?????:
        | ? MH12 -> ???? ??? (Pune)
        | ? MH14 -> ??????-?????? (Pimpri-Chinchwad)
        | ? MH01 -> ????? (Tardeo)
        | ? MH31 -> ?????? (Nagpur)
        | ? MH20 -> ??????? ????????? (Aurangabad)
        | ? MH   -> ??????? ????????????? ??????? RTO ????
        | ? DL   -> ?????? ??????????? ??????
        | ? KA   -> ??????? ?????
        |
        |??????? ??????????: ?????? ??? ???????.
        |=================================================================""".stripMargin

    searchResultArea.setText(defaultText)
    searchResultArea.setCaretPosition(0)
  }
}

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

val appInstance = new RTOMapApplication()
appInstance.start()