Code Sketch


bixu
By: Mhalsakant School
Category: Programming
import javax.swing.JFrame
import javax.swing.JPanel
import javax.swing.JLabel
import javax.swing.JButton
import javax.swing.JPasswordField
import javax.swing.JTextField
import javax.swing.JTextArea
import javax.swing.JScrollPane
import javax.swing.JList
import javax.swing.DefaultListModel
import javax.swing.JOptionPane
import javax.swing.BorderFactory
import javax.swing.WindowConstants
import javax.swing.SwingConstants
import javax.swing.ListSelectionModel
import javax.swing.BoxLayout
import javax.swing.Box
import javax.swing.SwingUtilities
import javax.swing.JTabbedPane

import java.awt.BorderLayout
import java.awt.Color
import java.awt.Component
import java.awt.Cursor
import java.awt.Desktop
import java.awt.Dimension
import java.awt.FlowLayout
import java.awt.Font
import java.awt.GridLayout
import java.awt.GridBagLayout
import java.awt.GridBagConstraints
import java.awt.Insets

import java.awt.event.ActionEvent
import java.awt.event.ActionListener

import java.net.URI

import java.security.MessageDigest
import java.text.SimpleDateFormat
import java.util.Date

// ============================================================
// BHIKSHU'S CAR & BIKES LIVE
// Kojo / Scala / Swing
//
// LOGIN PASSWORD:
// 238u549o7i4t5u278849346573535462454i8375623547652
//
// CardLayout ???????? ????.
// ============================================================


// ============================================================
// CONFIG
// ============================================================

object BhikshuAppConfig {

  val Title = "BHIKSHU'S CAR & BIKES LIVE"

  val Width = 1250
  val Height = 800

  // SHA-256("bhavesh2013")
  private val PasswordHash =
    "064adaded7ad21776cf2bef2a3b06c024a3191ca89e0074b4411898d5385eaa6"

  def sha256(value: String): String = {

    val md =
      MessageDigest.getInstance("SHA-256")

    md.digest(
      value.getBytes("UTF-8")
    ).map(
      "%02x".format(_)
    ).mkString
  }

  def checkPassword(value: String): Boolean = {

    sha256(value.trim) == PasswordHash
  }


  // ---------- COLORS ----------

  val DarkBlue =
    new Color(21, 45, 78)

  val Blue =
    new Color(41, 128, 185)

  val Green =
    new Color(39, 174, 96)

  val Orange =
    new Color(230, 126, 34)

  val Red =
    new Color(192, 57, 43)

  val Purple =
    new Color(111, 78, 170)

  val Background =
    new Color(244, 247, 251)

  val Card =
    new Color(255, 255, 255)

  val Text =
    new Color(35, 35, 35)

  val Secondary =
    new Color(105, 105, 105)

  val Border =
    new Color(215, 222, 232)

  val LightBlue =
    new Color(232, 241, 250)

  val White =
    Color.WHITE
}


// ============================================================
// VEHICLE
// ============================================================

case class Vehicle(
  category: String,
  brand: String,
  model: String,
  fuel: String,
  transmission: String,
  price: Long,
  priceText: String,
  priceNote: String,
  sourceName: String,
  sourceUrl: String
)


// ============================================================
// DATABASE
// ============================================================

object VehicleDatabase {

  val vehicles: Seq[Vehicle] = Seq(

    // ========================================================
    // CARS
    // ========================================================

    Vehicle(
      "CAR",
      "Hyundai",
      "Creta",
      "Petrol / Diesel",
      "Manual / Automatic / DCT",
      1090700L,
      "?10,90,700 onwards",
      "Reference ex-showroom starting price. Exact price varies by variant and city.",
      "Hyundai India",
      "https://www.hyundai.com/in/en/find-a-car/creta/price"
    ),

    Vehicle(
      "CAR",
      "Hyundai",
      "Venue",
      "Petrol / Diesel",
      "Manual / Automatic / DCT",
      799900L,
      "?7,99,900 onwards",
      "Reference starting price. Verify exact variant and city price.",
      "Hyundai India",
      "https://www.hyundai.com/in/en/find-a-car"
    ),

    Vehicle(
      "CAR",
      "Hyundai",
      "Aura",
      "Petrol / CNG",
      "Manual / Automatic",
      599990L,
      "?5,99,990 onwards",
      "Reference starting price. Verify exact variant and city price.",
      "Hyundai India",
      "https://www.hyundai.com/in/en/find-a-car"
    ),

    Vehicle(
      "CAR",
      "Maruti Suzuki",
      "Swift",
      "Petrol",
      "Manual / AMT",
      0L,
      "Check official site",
      "Price varies by variant and city.",
      "Maruti Suzuki",
      "https://www.marutisuzuki.com/"
    ),

    Vehicle(
      "CAR",
      "Maruti Suzuki",
      "Baleno",
      "Petrol / CNG",
      "Manual / AMT",
      0L,
      "Check official site",
      "Price varies by variant and city.",
      "Maruti Suzuki",
      "https://www.marutisuzuki.com/"
    ),

    Vehicle(
      "CAR",
      "Tata",
      "Nexon",
      "Petrol / Diesel / CNG / EV",
      "Manual / Automatic",
      0L,
      "Check official site",
      "Price varies by variant and city.",
      "Tata Motors",
      "https://www.tatamotors.com/"
    ),

    Vehicle(
      "CAR",
      "Tata",
      "Punch",
      "Petrol / CNG / EV",
      "Manual / AMT / Automatic",
      0L,
      "Check official site",
      "Price varies by variant and city.",
      "Tata Motors",
      "https://www.tatamotors.com/"
    ),

    Vehicle(
      "CAR",
      "Mahindra",
      "XUV 3XO",
      "Petrol / Diesel",
      "Manual / Automatic",
      0L,
      "Check official site",
      "Price varies by variant and city.",
      "Mahindra",
      "https://auto.mahindra.com/"
    ),

    Vehicle(
      "CAR",
      "Mahindra",
      "Scorpio-N",
      "Petrol / Diesel",
      "Manual / Automatic",
      0L,
      "Check official site",
      "Price varies by variant and city.",
      "Mahindra",
      "https://auto.mahindra.com/"
    ),

    Vehicle(
      "CAR",
      "Toyota",
      "Urban Cruiser Hyryder",
      "Petrol / Hybrid / CNG",
      "Manual / Automatic",
      0L,
      "Check official site",
      "Price varies by variant and city.",
      "Toyota India",
      "https://www.toyotabharat.com/"
    ),

    Vehicle(
      "CAR",
      "Honda Cars",
      "City",
      "Petrol",
      "Manual / CVT",
      0L,
      "Check official site",
      "Price varies by variant and city.",
      "Honda Cars India",
      "https://www.hondacarindia.com/"
    ),

    Vehicle(
      "CAR",
      "Kia",
      "Seltos",
      "Petrol / Diesel",
      "Manual / Automatic / DCT / iMT",
      0L,
      "Check official site",
      "Price varies by variant and city.",
      "Kia India",
      "https://www.kia.com/in/"
    ),

    Vehicle(
      "CAR",
      "MG",
      "Windsor EV",
      "Electric",
      "Automatic",
      0L,
      "Check official site",
      "Price varies by variant and city.",
      "MG Motor India",
      "https://www.mgmotor.co.in/"
    ),

    Vehicle(
      "CAR",
      "Skoda",
      "Kushaq",
      "Petrol",
      "Manual / Automatic",
      0L,
      "Check official site",
      "Price varies by variant and city.",
      "Skoda India",
      "https://www.skoda-auto.co.in/"
    ),


    // ========================================================
    // BIKES
    // ========================================================

    Vehicle(
      "BIKE",
      "Hero MotoCorp",
      "Splendor Plus",
      "Petrol",
      "Manual",
      77823L,
      "?77,823 onwards (reference)",
      "Location-specific reference price. Check exact city and variant.",
      "Hero MotoCorp",
      "https://www.heromotocorp.com/"
    ),

    Vehicle(
      "BIKE",
      "Bajaj",
      "Pulsar NS200",
      "Petrol",
      "Manual",
      138302L,
      "?1,38,302",
      "Reference price. Verify current variant and city price.",
      "Bajaj Auto",
      "https://www.bajajauto.com/"
    ),

    Vehicle(
      "BIKE",
      "Bajaj",
      "Pulsar 125",
      "Petrol",
      "Manual",
      104318L,
      "?1,04,318",
      "Reference price. Verify current variant and city price.",
      "Bajaj Auto",
      "https://www.bajajauto.com/"
    ),

    Vehicle(
      "BIKE",
      "Bajaj",
      "Pulsar 150",
      "Petrol",
      "Manual",
      128650L,
      "?1,28,650",
      "Reference price. Verify current variant and city price.",
      "Bajaj Auto",
      "https://www.bajajauto.com/"
    ),

    Vehicle(
      "BIKE",
      "Royal Enfield",
      "Classic 350",
      "Petrol",
      "Manual",
      0L,
      "Check official site",
      "Price varies by variant and city.",
      "Royal Enfield",
      "https://www.royalenfield.com/in/en/home/"
    ),

    Vehicle(
      "BIKE",
      "TVS",
      "Apache RTR 160",
      "Petrol",
      "Manual",
      0L,
      "Check official site",
      "Price varies by variant and city.",
      "TVS Motor",
      "https://www.tvsmotor.com/"
    ),

    Vehicle(
      "BIKE",
      "Honda",
      "SP 125",
      "Petrol",
      "Manual",
      0L,
      "Check official site",
      "Price varies by variant and city.",
      "Honda 2Wheelers",
      "https://www.honda2wheelersindia.com/"
    ),

    Vehicle(
      "BIKE",
      "Yamaha",
      "MT-15",
      "Petrol",
      "Manual",
      0L,
      "Check official site",
      "Price varies by variant and city.",
      "Yamaha India",
      "https://www.yamaha-motor-india.com/"
    ),


    // ========================================================
    // SCOOTERS
    // ========================================================

    Vehicle(
      "SCOOTER",
      "Honda",
      "Activa",
      "Petrol",
      "Automatic",
      0L,
      "Check official site",
      "Price varies by variant and city.",
      "Honda 2Wheelers",
      "https://www.honda2wheelersindia.com/"
    ),

    Vehicle(
      "SCOOTER",
      "TVS",
      "Jupiter",
      "Petrol",
      "Automatic",
      0L,
      "Check official site",
      "Price varies by variant and city.",
      "TVS Motor",
      "https://www.tvsmotor.com/"
    ),

    Vehicle(
      "SCOOTER",
      "Suzuki",
      "Access",
      "Petrol",
      "Automatic",
      0L,
      "Check official site",
      "Price varies by variant and city.",
      "Suzuki Motorcycle India",
      "https://www.suzukimotorcycle.co.in/"
    ),

    Vehicle(
      "SCOOTER",
      "Ola Electric",
      "S1",
      "Electric",
      "Automatic",
      0L,
      "Check official site",
      "Price varies by variant and city.",
      "Ola Electric",
      "https://www.olaelectric.com/"
    ),

    Vehicle(
      "SCOOTER",
      "Ather",
      "Rizta",
      "Electric",
      "Automatic",
      0L,
      "Check official site",
      "Price varies by variant and city.",
      "Ather Energy",
      "https://www.atherenergy.com/"
    )
  )


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

  def search(
      query: String,
      category: String
  ): Seq[Vehicle] = {

    val q =
      query.trim.toLowerCase

    val categoryFiltered =
      if (category == "ALL") {
        vehicles
      } else {
        vehicles.filter(
          _.category == category
        )
      }

    if (q.isEmpty) {

      categoryFiltered

    } else {

      categoryFiltered.filter { v =>

        v.brand.toLowerCase.contains(q) ||
        v.model.toLowerCase.contains(q) ||
        v.category.toLowerCase.contains(q) ||
        v.fuel.toLowerCase.contains(q)
      }
    }
  }
}


// ============================================================
// MAIN APP
// ============================================================

class BhikshuCarBikeApp {

  // ----------------------------------------------------------
  // FRAME
  // ----------------------------------------------------------

  private val frame =
    new JFrame(
      BhikshuAppConfig.Title
    )


  // ----------------------------------------------------------
  // MAIN PANEL
  // ----------------------------------------------------------

  private val mainPanel =
    new JPanel(
      new BorderLayout()
    )


  // ----------------------------------------------------------
  // TAB PANEL
  // ----------------------------------------------------------

  private val tabs =
    new JTabbedPane()


  // ----------------------------------------------------------
  // SEARCH
  // ----------------------------------------------------------

  private val searchField =
    new JTextField(20)


  // ----------------------------------------------------------
  // VEHICLE LIST
  // ----------------------------------------------------------

  private val listModel =
    new DefaultListModel[String]()

  private val vehicleList =
    new JList[String](listModel)


  // ----------------------------------------------------------
  // DATA
  // ----------------------------------------------------------

  private var currentCategory =
    "ALL"

  private var currentVehicles =
    VehicleDatabase.vehicles

  private var selectedVehicle
    : Option[Vehicle] =
      None

  private var lastRefresh =
    new Date()


  // ----------------------------------------------------------
  // STATUS
  // ----------------------------------------------------------

  private val status =
    new JLabel("Ready")


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

  def start(): Unit = {

    frame.setSize(
      BhikshuAppConfig.Width,
      BhikshuAppConfig.Height
    )

    frame.setMinimumSize(
      new Dimension(
        950,
        650
      )
    )

    frame.setDefaultCloseOperation(
      WindowConstants.EXIT_ON_CLOSE
    )

    frame.setLocationRelativeTo(null)

    frame.setResizable(true)

    showLogin()

    frame.setVisible(true)

    frame.toFront()
    frame.requestFocus()
  }


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

  private def showLogin(): Unit = {

    val outer =
      new JPanel(
        new GridBagLayout()
      )

    outer.setBackground(
      BhikshuAppConfig.Background
    )


    // --------------------------------------------------------
    // LOGIN CARD
    // --------------------------------------------------------

    val loginCard =
      new JPanel()

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

    loginCard.setBackground(
      BhikshuAppConfig.Card
    )

    loginCard.setBorder(
      BorderFactory.createCompoundBorder(
        BorderFactory.createLineBorder(
          BhikshuAppConfig.Border,
          1
        ),
        BorderFactory.createEmptyBorder(
          40,
          55,
          40,
          55
        )
      )
    )


    // --------------------------------------------------------
    // TITLE
    // --------------------------------------------------------

    val title =
      new JLabel(
        "<html><center>" +
        "BHIKSHU'S CAR &amp; BIKES LIVE" +
        "<br/>" +
        "<font size='5'>SECURE LOGIN</font>" +
        "</center></html>",
        SwingConstants.CENTER
      )

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

    title.setAlignmentX(
      Component.CENTER_ALIGNMENT
    )

    title.setForeground(
      BhikshuAppConfig.DarkBlue
    )


    // --------------------------------------------------------
    // INFO
    // --------------------------------------------------------

    val info =
      new JLabel(
        "Enter password to open the application",
        SwingConstants.CENTER
      )

    info.setFont(
      new Font(
        "Arial",
        Font.PLAIN,
        14
      )
    )

    info.setForeground(
      BhikshuAppConfig.Secondary
    )

    info.setAlignmentX(
      Component.CENTER_ALIGNMENT
    )


    // --------------------------------------------------------
    // PASSWORD
    // --------------------------------------------------------

    val password =
      new JPasswordField()

    password.setPreferredSize(
      new Dimension(
        330,
        42
      )
    )

    password.setMaximumSize(
      new Dimension(
        330,
        42
      )
    )

    password.setHorizontalAlignment(
      SwingConstants.CENTER
    )

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


    // --------------------------------------------------------
    // LOGIN BUTTON
    // --------------------------------------------------------

    val login =
      new JButton("LOGIN")

    styleButton(
      login,
      BhikshuAppConfig.Blue
    )

    login.setAlignmentX(
      Component.CENTER_ALIGNMENT
    )

    login.setMaximumSize(
      new Dimension(
        330,
        48
      )
    )


    // --------------------------------------------------------
    // HINT
    // --------------------------------------------------------

    val hint =
      new JLabel(
        "<html><center>" +
        "Password authentication uses SHA-256." +
        "<br/>" +
        "The password is not displayed in the UI." +
        "</center></html>",
        SwingConstants.CENTER
      )

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

    hint.setForeground(
      BhikshuAppConfig.Secondary
    )

    hint.setAlignmentX(
      Component.CENTER_ALIGNMENT
    )


    // --------------------------------------------------------
    // ADD
    // --------------------------------------------------------

    loginCard.add(title)
    loginCard.add(
      Box.createVerticalStrut(18)
    )

    loginCard.add(info)
    loginCard.add(
      Box.createVerticalStrut(24)
    )

    loginCard.add(password)
    loginCard.add(
      Box.createVerticalStrut(18)
    )

    loginCard.add(login)
    loginCard.add(
      Box.createVerticalStrut(20)
    )

    loginCard.add(hint)


    // --------------------------------------------------------
    // CENTER CARD
    // --------------------------------------------------------

    val gbc =
      new GridBagConstraints()

    gbc.gridx = 0
    gbc.gridy = 0
    gbc.anchor =
      GridBagConstraints.CENTER

    gbc.insets =
      new Insets(
        20,
        20,
        20,
        20
      )

    outer.add(
      loginCard,
      gbc
    )


    // --------------------------------------------------------
    // LOGIN FUNCTION
    // --------------------------------------------------------

    def authenticate(): Unit = {

      val entered =
        new String(
          password.getPassword
        )

      if (
        BhikshuAppConfig
          .checkPassword(entered)
      ) {

        password.setText("")

        showMain()

      } else {

        password.setText("")

        JOptionPane.showMessageDialog(
          frame,
          "Wrong password!",
          "Login Error",
          JOptionPane.ERROR_MESSAGE
        )

        password.requestFocus()
      }
    }


    // --------------------------------------------------------
    // EVENTS
    // --------------------------------------------------------

    login.addActionListener(
      new ActionListener {

        def actionPerformed(
            e: ActionEvent
        ): Unit = {

          authenticate()
        }
      }
    )


    password.addActionListener(
      new ActionListener {

        def actionPerformed(
            e: ActionEvent
        ): Unit = {

          authenticate()
        }
      }
    )


    // --------------------------------------------------------
    // SHOW
    // --------------------------------------------------------

    frame.setContentPane(
      outer
    )

    frame.revalidate()
    frame.repaint()

    password.requestFocus()
  }


  // ==========================================================
  // MAIN
  // ==========================================================

  private def showMain(): Unit = {

    mainPanel.removeAll()

    tabs.removeAll()

    tabs.addTab(
      "VEHICLES",
      createVehiclePage()
    )

    tabs.addTab(
      "INFORMATION",
      createInformationPage()
    )

    mainPanel.add(
      createHeader(),
      BorderLayout.NORTH
    )

    mainPanel.add(
      tabs,
      BorderLayout.CENTER
    )

    mainPanel.add(
      createStatusBar(),
      BorderLayout.SOUTH
    )

    frame.setContentPane(
      mainPanel
    )

    frame.revalidate()
    frame.repaint()

    refreshVehicleList()
  }


  // ==========================================================
  // HEADER
  // ==========================================================

  private def createHeader(): JPanel = {

    val header =
      new JPanel(
        new BorderLayout()
      )

    header.setBackground(
      BhikshuAppConfig.DarkBlue
    )

    header.setBorder(
      BorderFactory.createEmptyBorder(
        12,
        18,
        12,
        18
      )
    )


    // --------------------------------------------------------
    // BRAND
    // --------------------------------------------------------

    val brand =
      new JPanel(
        new GridLayout(
          2,
          1
        )
      )

    brand.setOpaque(false)


    val title =
      new JLabel(
        "BHIKSHU'S CAR & BIKES LIVE"
      )

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

    title.setForeground(
      Color.WHITE
    )


    val subtitle =
      new JLabel(
        "Cars ? Bikes ? Scooters ? Price Reference"
      )

    subtitle.setForeground(
      new Color(
        210,
        220,
        230
      )
    )


    brand.add(title)
    brand.add(subtitle)


    header.add(
      brand,
      BorderLayout.WEST
    )


    // --------------------------------------------------------
    // BUTTONS
    // --------------------------------------------------------

    val buttons =
      new JPanel(
        new FlowLayout(
          FlowLayout.RIGHT,
          8,
          3
        )
      )

    buttons.setOpaque(false)


    val home =
      new JButton("HOME")

    val logout =
      new JButton("LOGOUT")


    styleButton(
      home,
      BhikshuAppConfig.Green
    )

    styleButton(
      logout,
      BhikshuAppConfig.Red
    )


    buttons.add(home)
    buttons.add(logout)


    header.add(
      buttons,
      BorderLayout.EAST
    )


    // --------------------------------------------------------
    // HOME
    // --------------------------------------------------------

    home.addActionListener(
      new ActionListener {

        def actionPerformed(
            e: ActionEvent
        ): Unit = {

          tabs.setSelectedIndex(0)

          status.setText(
            "Home / Vehicles"
          )
        }
      }
    )


    // --------------------------------------------------------
    // LOGOUT
    // --------------------------------------------------------

    logout.addActionListener(
      new ActionListener {

        def actionPerformed(
            e: ActionEvent
        ): Unit = {

          selectedVehicle = None

          showLogin()
        }
      }
    )


    header
  }


  // ==========================================================
  // VEHICLE PAGE
  // ==========================================================

  private def createVehiclePage(): JPanel = {

    val page =
      new JPanel(
        new BorderLayout(
          10,
          10
        )
      )

    page.setBackground(
      BhikshuAppConfig.Background
    )

    page.setBorder(
      BorderFactory.createEmptyBorder(
        12,
        12,
        12,
        12
      )
    )


    // --------------------------------------------------------
    // TOP SEARCH AREA
    // --------------------------------------------------------

    val top =
      new JPanel(
        new BorderLayout(
          10,
          8
        )
      )

    top.setBackground(
      BhikshuAppConfig.Card
    )

    top.setBorder(
      BorderFactory.createEmptyBorder(
        10,
        12,
        10,
        12
      )
    )


    // --------------------------------------------------------
    // CATEGORY BUTTONS
    // --------------------------------------------------------

    val categoryButtons =
      new JPanel(
        new FlowLayout(
          FlowLayout.LEFT,
          6,
          0
        )
      )

    categoryButtons.setOpaque(false)


    val allButton =
      new JButton("ALL")

    val carButton =
      new JButton("CARS")

    val bikeButton =
      new JButton("BIKES")

    val scooterButton =
      new JButton("SCOOTERS")


    styleButton(
      allButton,
      BhikshuAppConfig.Blue
    )

    styleButton(
      carButton,
      BhikshuAppConfig.Orange
    )

    styleButton(
      bikeButton,
      BhikshuAppConfig.Green
    )

    styleButton(
      scooterButton,
      BhikshuAppConfig.Purple
    )


    categoryButtons.add(allButton)
    categoryButtons.add(carButton)
    categoryButtons.add(bikeButton)
    categoryButtons.add(scooterButton)


    top.add(
      categoryButtons,
      BorderLayout.WEST
    )


    // --------------------------------------------------------
    // SEARCH
    // --------------------------------------------------------

    val searchPanel =
      new JPanel(
        new FlowLayout(
          FlowLayout.RIGHT,
          6,
          0
        )
      )

    searchPanel.setOpaque(false)


    val searchLabel =
      new JLabel("Search:")

    val searchButton =
      new JButton("SEARCH")

    val refreshButton =
      new JButton("REFRESH")


    styleButton(
      searchButton,
      BhikshuAppConfig.Blue
    )

    styleButton(
      refreshButton,
      BhikshuAppConfig.Green
    )


    searchField.setPreferredSize(
      new Dimension(
        210,
        34
      )
    )

    searchField.setFont(
      new Font(
        "Arial",
        Font.PLAIN,
        14
      )
    )


    searchPanel.add(
      searchLabel
    )

    searchPanel.add(
      searchField
    )

    searchPanel.add(
      searchButton
    )

    searchPanel.add(
      refreshButton
    )


    top.add(
      searchPanel,
      BorderLayout.EAST
    )


    page.add(
      top,
      BorderLayout.NORTH
    )


    // --------------------------------------------------------
    // LEFT LIST
    // --------------------------------------------------------

    vehicleList.setSelectionMode(
      ListSelectionModel.SINGLE_SELECTION
    )

    vehicleList.setFont(
      new Font(
        "Arial",
        Font.PLAIN,
        14
      )
    )

    vehicleList.setFixedCellHeight(
      40
    )

    vehicleList.setBackground(
      Color.WHITE
    )


    val listScroll =
      new JScrollPane(
        vehicleList
      )

    listScroll.setBorder(
      BorderFactory.createTitledBorder(
        "VEHICLES"
      )
    )


    // --------------------------------------------------------
    // DETAILS
    // --------------------------------------------------------

    val detailsArea =
      new JTextArea()

    detailsArea.setEditable(false)
    detailsArea.setLineWrap(true)
    detailsArea.setWrapStyleWord(true)

    detailsArea.setFont(
      new Font(
        "Arial",
        Font.PLAIN,
        15
      )
    )

    detailsArea.setForeground(
      BhikshuAppConfig.Text
    )

    detailsArea.setBackground(
      Color.WHITE
    )


    val detailsScroll =
      new JScrollPane(
        detailsArea
      )

    detailsScroll.setBorder(
      BorderFactory.createTitledBorder(
        "VEHICLE DETAILS"
      )
    )


    // --------------------------------------------------------
    // BUTTONS BELOW DETAILS
    // --------------------------------------------------------

    val actionPanel =
      new JPanel(
        new FlowLayout(
          FlowLayout.LEFT,
          8,
          6
        )
      )

    actionPanel.setOpaque(false)


    val profileButton =
      new JButton(
        "VIEW FULL PROFILE"
      )

    val officialButton =
      new JButton(
        "OFFICIAL WEBSITE"
      )


    styleButton(
      profileButton,
      BhikshuAppConfig.Purple
    )

    styleButton(
      officialButton,
      BhikshuAppConfig.Green
    )


    actionPanel.add(
      profileButton
    )

    actionPanel.add(
      officialButton
    )


    // --------------------------------------------------------
    // RIGHT AREA
    // --------------------------------------------------------

    val right =
      new JPanel(
        new BorderLayout()
      )

    right.setOpaque(false)

    right.add(
      detailsScroll,
      BorderLayout.CENTER
    )

    right.add(
      actionPanel,
      BorderLayout.SOUTH
    )


    // --------------------------------------------------------
    // CENTER SPLIT
    // --------------------------------------------------------

    val center =
      new JPanel(
        new GridLayout(
          1,
          2,
          10,
          10
        )
      )

    center.setOpaque(false)

    center.add(listScroll)
    center.add(right)


    page.add(
      center,
      BorderLayout.CENTER
    )


    // --------------------------------------------------------
    // CATEGORY EVENTS
    // --------------------------------------------------------

    allButton.addActionListener(
      new ActionListener {

        def actionPerformed(
            e: ActionEvent
        ): Unit = {

          currentCategory = "ALL"

          refreshVehicleList()
        }
      }
    )


    carButton.addActionListener(
      new ActionListener {

        def actionPerformed(
            e: ActionEvent
        ): Unit = {

          currentCategory = "CAR"

          refreshVehicleList()
        }
      }
    )


    bikeButton.addActionListener(
      new ActionListener {

        def actionPerformed(
            e: ActionEvent
        ): Unit = {

          currentCategory = "BIKE"

          refreshVehicleList()
        }
      }
    )


    scooterButton.addActionListener(
      new ActionListener {

        def actionPerformed(
            e: ActionEvent
        ): Unit = {

          currentCategory = "SCOOTER"

          refreshVehicleList()
        }
      }
    )


    // --------------------------------------------------------
    // SEARCH EVENTS
    // --------------------------------------------------------

    searchButton.addActionListener(
      new ActionListener {

        def actionPerformed(
            e: ActionEvent
        ): Unit = {

          refreshVehicleList()
        }
      }
    )


    searchField.addActionListener(
      new ActionListener {

        def actionPerformed(
            e: ActionEvent
        ): Unit = {

          refreshVehicleList()
        }
      }
    )


    refreshButton.addActionListener(
      new ActionListener {

        def actionPerformed(
            e: ActionEvent
        ): Unit = {

          searchField.setText("")

          refreshVehicleList()
        }
      }
    )


    // --------------------------------------------------------
    // LIST EVENT
    // --------------------------------------------------------

    vehicleList.addListSelectionListener(
      new javax.swing.event.ListSelectionListener {

        def valueChanged(
            e: javax.swing.event.ListSelectionEvent
        ): Unit = {

          if (!e.getValueIsAdjusting) {

            val index =
              vehicleList.getSelectedIndex

            if (
              index >= 0 &&
              index < currentVehicles.size
            ) {

              selectedVehicle =
                Some(
                  currentVehicles(index)
                )

              showPreview(
                currentVehicles(index),
                detailsArea
              )
            }
          }
        }
      }
    )


    // --------------------------------------------------------
    // PROFILE BUTTON
    // --------------------------------------------------------

    profileButton.addActionListener(
      new ActionListener {

        def actionPerformed(
            e: ActionEvent
        ): Unit = {

          selectedVehicle match {

            case Some(v) =>
              showVehicleProfile(v)

            case None =>
              JOptionPane.showMessageDialog(
                frame,
                "First select a vehicle.",
                "Vehicle Selection",
                JOptionPane.WARNING_MESSAGE
              )
          }
        }
      }
    )


    // --------------------------------------------------------
    // OFFICIAL BUTTON
    // --------------------------------------------------------

    officialButton.addActionListener(
      new ActionListener {

        def actionPerformed(
            e: ActionEvent
        ): Unit = {

          openOfficialSite()
        }
      }
    )


    // --------------------------------------------------------
    // DEFAULT MESSAGE
    // --------------------------------------------------------

    detailsArea.setText(
      "Welcome to BHIKSHU'S CAR & BIKES LIVE\n\n" +
      "Select a vehicle from the left side.\n\n" +
      "You can:\n" +
      "? Search by brand or model\n" +
      "? Select Cars / Bikes / Scooters\n" +
      "? View vehicle details\n" +
      "? Open the official manufacturer website\n\n" +
      "Price information is reference data and exact " +
      "price can vary by city, variant, taxes and offers."
    )


    page
  }


  // ==========================================================
  // INFORMATION PAGE
  // ==========================================================

  private def createInformationPage(): JPanel = {

    val page =
      new JPanel(
        new BorderLayout()
      )

    page.setBackground(
      BhikshuAppConfig.Background
    )

    page.setBorder(
      BorderFactory.createEmptyBorder(
        25,
        25,
        25,
        25
      )
    )


    val heading =
      new JLabel(
        "ABOUT BHIKSHU'S CAR & BIKES LIVE"
      )

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

    heading.setForeground(
      BhikshuAppConfig.DarkBlue
    )


    val area =
      new JTextArea()

    area.setEditable(false)
    area.setLineWrap(true)
    area.setWrapStyleWord(true)

    area.setFont(
      new Font(
        "Arial",
        Font.PLAIN,
        15
      )
    )

    area.setText(
      "BHIKSHU'S CAR & BIKES LIVE\n" +
      "=============================================\n\n" +

      "This application is an offline vehicle reference " +
      "dashboard built using Scala and Java Swing.\n\n" +

      "FEATURES\n" +
      "---------------------------------------------\n" +
      "1. Secure password login\n" +
      "2. SHA-256 password verification\n" +
      "3. Cars database\n" +
      "4. Bikes database\n" +
      "5. Scooters database\n" +
      "6. Search by brand/model/fuel\n" +
      "7. Vehicle profile display\n" +
      "8. Official manufacturer website button\n" +
      "9. Dashboard refresh time\n\n" +

      "IMPORTANT PRICE NOTE\n" +
      "---------------------------------------------\n" +
      "The application does not automatically download live " +
      "market prices. Stored values are reference values. " +
      "For the latest exact price, use the official website " +
      "button in the vehicle profile.\n\n" +

      "SECURITY\n" +
      "---------------------------------------------\n" +
      "The login comparison uses the SHA-256 hash of the " +
      "password instead of storing the plain password in " +
      "the authentication comparison.\n\n" +

      "APPLICATION STATUS\n" +
      "---------------------------------------------\n" +
      "Application started successfully."
    )


    page.add(
      heading,
      BorderLayout.NORTH
    )

    page.add(
      new JScrollPane(area),
      BorderLayout.CENTER
    )


    page
  }


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

  private def createStatusBar(): JPanel = {

    val bar =
      new JPanel(
        new BorderLayout()
      )

    bar.setBackground(
      new Color(
        235,
        238,
        243
      )
    )

    bar.setBorder(
      BorderFactory.createEmptyBorder(
        7,
        12,
        7,
        12
      )
    )

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

    status.setForeground(
      BhikshuAppConfig.DarkBlue
    )

    bar.add(
      status,
      BorderLayout.WEST
    )

    bar
  }


  // ==========================================================
  // REFRESH VEHICLE LIST
  // ==========================================================

  private def refreshVehicleList(): Unit = {

    currentVehicles =
      VehicleDatabase.search(
        searchField.getText,
        currentCategory
      )

    listModel.clear()

    currentVehicles.foreach { v =>

      val price =
        if (v.priceText.nonEmpty) {
          v.priceText
        } else {
          "Check official site"
        }

      listModel.addElement(
        v.category +
        "  |  " +
        v.brand +
        " " +
        v.model +
        "  |  " +
        price
      )
    }


    selectedVehicle = None

    lastRefresh =
      new Date()


    val categoryName =
      currentCategory match {

        case "CAR" =>
          "CARS"

        case "BIKE" =>
          "BIKES"

        case "SCOOTER" =>
          "SCOOTERS"

        case _ =>
          "ALL VEHICLES"
      }


    status.setText(
      categoryName +
      " ? " +
      currentVehicles.size +
      " records ? Refreshed: " +
      formatDate(lastRefresh)
    )
  }


  // ==========================================================
  // SHOW PREVIEW
  // ==========================================================

  private def showPreview(
      v: Vehicle,
      area: JTextArea
  ): Unit = {

    val priceLine =

      if (v.price > 0L) {

        "REFERENCE PRICE : " +
        v.priceText

      } else {

        "REFERENCE PRICE : Check official manufacturer site"
      }


    area.setText(

      "====================================================\n" +
      "                 VEHICLE PROFILE\n" +
      "====================================================\n\n" +

      "CATEGORY        : " +
      v.category +
      "\n" +

      "BRAND           : " +
      v.brand +
      "\n" +

      "MODEL           : " +
      v.model +
      "\n" +

      "FUEL            : " +
      v.fuel +
      "\n" +

      "TRANSMISSION    : " +
      v.transmission +
      "\n\n" +

      priceLine +
      "\n\n" +

      "PRICE NOTE      : " +
      v.priceNote +
      "\n\n" +

      "SOURCE          : " +
      v.sourceName +
      "\n\n" +

      "LAST REFRESH    : " +
      formatDate(lastRefresh) +
      "\n\n" +

      "PRICE PREDICTION: Not generated.\n" +
      "Future prices are not invented by this application.\n\n" +

      "Click VIEW FULL PROFILE for complete information."
    )
  }


  // ==========================================================
  // FULL PROFILE
  // ==========================================================

  private def showVehicleProfile(
      v: Vehicle
  ): Unit = {

    val dialog =
      new JFrame(
        v.brand +
        " " +
        v.model +
        " - Full Profile"
      )

    dialog.setSize(
      850,
      650
    )

    dialog.setLocationRelativeTo(frame)

    dialog.setDefaultCloseOperation(
      WindowConstants.DISPOSE_ON_CLOSE
    )


    val root =
      new JPanel(
        new BorderLayout(
          10,
          10
        )
      )

    root.setBackground(
      BhikshuAppConfig.Background
    )

    root.setBorder(
      BorderFactory.createEmptyBorder(
        15,
        15,
        15,
        15
      )
    )


    val heading =
      new JLabel(
        v.brand +
        " " +
        v.model
      )

    heading.setFont(
      new Font(
        "Arial",
        Font.BOLD,
        25
      )
    )

    heading.setForeground(
      BhikshuAppConfig.DarkBlue
    )


    val area =
      new JTextArea()

    area.setEditable(false)
    area.setLineWrap(true)
    area.setWrapStyleWord(true)

    area.setFont(
      new Font(
        "Arial",
        Font.PLAIN,
        16
      )
    )

    area.setText(

      "VEHICLE DETAILS\n" +
      "=============================================\n\n" +

      "Category       : " +
      v.category +
      "\n\n" +

      "Brand          : " +
      v.brand +
      "\n\n" +

      "Model          : " +
      v.model +
      "\n\n" +

      "Fuel           : " +
      v.fuel +
      "\n\n" +

      "Transmission   : " +
      v.transmission +
      "\n\n" +

      "Reference Price: " +
      v.priceText +
      "\n\n" +

      "Price Note     : " +
      v.priceNote +
      "\n\n" +

      "Official Source: " +
      v.sourceName +
      "\n\n" +

      "Website URL    : " +
      v.sourceUrl +
      "\n\n" +

      "Last Dashboard Refresh:\n" +
      formatDate(lastRefresh) +
      "\n\n" +

      "NOTE:\n" +
      "Exact purchase price can vary according to city, " +
      "variant, registration, insurance, taxes and offers."
    )


    val bottom =
      new JPanel(
        new FlowLayout(
          FlowLayout.LEFT,
          8,
          5
        )
      )

    bottom.setOpaque(false)


    val official =
      new JButton(
        "OPEN OFFICIAL WEBSITE"
      )

    val close =
      new JButton(
        "CLOSE"
      )


    styleButton(
      official,
      BhikshuAppConfig.Green
    )

    styleButton(
      close,
      BhikshuAppConfig.Red
    )


    bottom.add(official)
    bottom.add(close)


    official.addActionListener(
      new ActionListener {

        def actionPerformed(
            e: ActionEvent
        ): Unit = {

          openURL(
            v.sourceUrl
          )
        }
      }
    )


    close.addActionListener(
      new ActionListener {

        def actionPerformed(
            e: ActionEvent
        ): Unit = {

          dialog.dispose()
        }
      }
    )


    root.add(
      heading,
      BorderLayout.NORTH
    )

    root.add(
      new JScrollPane(area),
      BorderLayout.CENTER
    )

    root.add(
      bottom,
      BorderLayout.SOUTH
    )


    dialog.setContentPane(root)

    dialog.setVisible(true)
  }


  // ==========================================================
  // OFFICIAL SITE
  // ==========================================================

  private def openOfficialSite(): Unit = {

    selectedVehicle match {

      case Some(v) =>

        openURL(
          v.sourceUrl
        )

        status.setText(
          "Opened official website: " +
          v.sourceName
        )


      case None =>

        JOptionPane.showMessageDialog(
          frame,
          "First select a vehicle from the list.",
          "No Vehicle Selected",
          JOptionPane.WARNING_MESSAGE
        )
    }
  }


  // ==========================================================
  // URL
  // ==========================================================

  private def openURL(
      url: String
  ): Unit = {

    try {

      if (
        Desktop.isDesktopSupported &&
        Desktop
          .getDesktop
          .isSupported(
            Desktop.Action.BROWSE
          )
      ) {

        Desktop
          .getDesktop
          .browse(
            new URI(url)
          )

      } else {

        JOptionPane.showMessageDialog(
          frame,
          url,
          "Official Website",
          JOptionPane.INFORMATION_MESSAGE
        )
      }

    } catch {

      case _: Exception =>

        JOptionPane.showMessageDialog(
          frame,
          url,
          "Official Website",
          JOptionPane.INFORMATION_MESSAGE
        )
    }
  }


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

  private def styleButton(
      button: JButton,
      background: Color
  ): Unit = {

    button.setBackground(
      background
    )

    button.setForeground(
      Color.WHITE
    )

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

    button.setFocusPainted(false)

    button.setBorder(
      BorderFactory.createEmptyBorder(
        8,
        14,
        8,
        14
      )
    )

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


  // ==========================================================
  // DATE
  // ==========================================================

  private def formatDate(
      date: Date
  ): String = {

    new SimpleDateFormat(
      "dd MMM yyyy, HH:mm:ss"
    ).format(date)
  }
}


// ============================================================
// SAFE START FOR KOJO
// ============================================================

SwingUtilities.invokeLater(
  new Runnable {

    def run(): Unit = {

      val app =
        new BhikshuCarBikeApp()

      app.start()
    }
  }
)