import sys
import os

# Prevent crashes in windowless (noconsole) mode by redirecting standard streams to dummy objects immediately
class DummyStream:
    def write(self, *args, **kwargs): pass
    def flush(self, *args, **kwargs): pass
    def isatty(self): return False
    def close(self): pass

class DummyInput:
    def read(self, *args, **kwargs): return ""
    def readline(self, *args, **kwargs): return ""

sys.stdout = DummyStream()
sys.stderr = DummyStream()
sys.stdin = DummyInput()

import json
import random
import time
import platform
import socket
import webbrowser
import tkinter as tk
from tkinter import messagebox
from threading import Thread
from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler

# Hide console window programmatically if compiled with PyInstaller and running with console
if getattr(sys, 'frozen', False):
    try:
        import ctypes
        hwnd = ctypes.windll.kernel32.GetConsoleWindow()
        if hwnd:
            ctypes.windll.user32.ShowWindow(hwnd, 0) # 0 = SW_HIDE
    except Exception:
        pass

import ctypes
from ctypes import wintypes

try:
    cfgmgr32 = ctypes.WinDLL('cfgmgr32.dll')
    PULONG = ctypes.POINTER(wintypes.ULONG)
    cfgmgr32.CM_Get_Device_ID_List_SizeW.argtypes = [PULONG, wintypes.LPCWSTR, wintypes.ULONG]
    cfgmgr32.CM_Get_Device_ID_List_SizeW.restype = wintypes.DWORD
    cfgmgr32.CM_Get_Device_ID_ListW.argtypes = [wintypes.LPCWSTR, wintypes.LPWSTR, wintypes.ULONG, wintypes.ULONG]
    cfgmgr32.CM_Get_Device_ID_ListW.restype = wintypes.DWORD
except Exception:
    cfgmgr32 = None

def is_scanner_connected():
    if cfgmgr32 is None:
        return False
    try:
        required_size = wintypes.ULONG()
        # CM_GETIDLIST_FILTER_PRESENT = 0x00000100
        ret = cfgmgr32.CM_Get_Device_ID_List_SizeW(ctypes.byref(required_size), None, 0x00000100)
        if ret != 0:
            return False
            
        buf = ctypes.create_unicode_buffer(required_size.value)
        ret = cfgmgr32.CM_Get_Device_ID_ListW(None, buf, required_size.value, 0x00000100)
        if ret != 0:
            return False
            
        raw_str = ''.join(buf[i] for i in range(required_size.value))
        devices = [d for d in raw_str.split('\0') if d]
        
        for dev in devices:
            dev_upper = dev.upper()
            if 'ZK' in dev_upper or 'SLK' in dev_upper or 'FINGER' in dev_upper or '1B55' in dev_upper:
                return True
        return False
    except Exception:
        return False

# ==================================================
# ELIMU TRACK - USB SCANNER GATEWAY DAEMON
# ==================================================
# Run this script locally on the client PC connected to the physical USB Fingerprint Scanner.
# It spins up a lightweight local web server on port 8000 to relay scan requests.
# It also hosts an interactive dashboard for diagnostics and troubleshooting.
#
# Requirements:
#   Python 3.x (Uses standard library only, no pip dependencies needed!)
# ==================================================

# Global tracking variables
start_time = time.time()
request_logs = []
device_connected_status = False

import threading
log_lock = threading.Lock()

def add_request_log(method, path, status, message=""):
    """Adds a request to the global log cache for display in the dashboard."""
    # Exclude internal log and status requests to prevent flooding the dashboard logs
    if path == '/logs' or path == '/info' or path == '/scanner/shutdown' or path == '/shutdown':
        return
        
    timestamp = time.strftime("%H:%M:%S")
    log_entry = {
        "time": timestamp,
        "method": method,
        "path": path,
        "status": status,
        "message": message
    }
    with log_lock:
        request_logs.append(log_entry)
        
        # Keep only the last 30 log messages
        if len(request_logs) > 30:
            request_logs.pop(0)

# Beautiful embedded HTML dashboard matching ELIMU TRACK design tokens
DASHBOARD_HTML = r"""<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Elimu Track - USB Scanner Gateway</title>
  <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🔌</text></svg>">
  <style>
    @import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800&family=Inter:wght@300;400;500;600;700&family=Fira+Code:wght@400;500&display=swap');

    :root {
      --bg-color: #060a12;
      --bg-panel: #0d1527;
      --bg-gradient: radial-gradient(circle at 10% 20%, #0c1527 0%, #060a12 90%);
      --card-bg: rgba(13, 21, 37, 0.45);
      --card-border: rgba(255, 255, 255, 0.06);
      
      --glass-bg: rgba(13, 21, 37, 0.65);
      --glass-border: rgba(255, 255, 255, 0.08);
      --glass-shadow: 0 10px 45px 0 rgba(0, 0, 0, 0.55);
      
      --primary: #3b82f6;
      --primary-hover: #60a5fa;
      --primary-glow: rgba(59, 130, 246, 0.2);
      
      --text: #f8fafc;
      --text-muted: #94a3b8;
      --text-white: #ffffff;
      
      --green: #10b981;
      --green-glow: rgba(16, 185, 129, 0.2);
      --red: #ef4444;
      --red-glow: rgba(239, 68, 68, 0.2);
      --amber: #f59e0b;
      --amber-glow: rgba(245, 158, 11, 0.2);
      --purple: #8b5cf6;
      --purple-glow: rgba(139, 92, 246, 0.2);
      
      --font-family: 'Inter', system-ui, -apple-system, sans-serif;
      --font-heading: 'Outfit', 'Inter', system-ui, sans-serif;
      --transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
    }

    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
    }

    body {
      font-family: var(--font-family);
      background: var(--bg-color);
      background-image: var(--bg-gradient);
      color: var(--text);
      min-height: 100vh;
      display: flex;
      flex-direction: column;
      align-items: center;
      padding: 30px 20px;
      overflow-y: auto;
    }

    header {
      width: 100%;
      max-width: 1100px;
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 30px;
      padding: 0 10px;
    }

    .brand {
      display: flex;
      align-items: center;
      gap: 15px;
    }

    .brand-logo-container {
      position: relative;
      width: 44px;
      height: 44px;
      display: flex;
      justify-content: center;
      align-items: center;
    }

    .brand-logo-glow {
      position: absolute;
      top: 0; left: 0; right: 0; bottom: 0;
      background: var(--primary);
      filter: blur(10px);
      opacity: 0.3;
      border-radius: 50%;
      animation: pulse-glow 3s infinite;
    }

    .brand-logo {
      width: 38px;
      height: 38px;
      z-index: 1;
      color: var(--primary);
    }

    .brand-text h1 {
      font-family: var(--font-heading);
      font-size: 22px;
      font-weight: 700;
      letter-spacing: 0.5px;
      background: linear-gradient(135deg, #ffffff 30%, var(--primary-hover) 100%);
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
    }

    .brand-text p {
      font-size: 11px;
      color: var(--text-muted);
      text-transform: uppercase;
      letter-spacing: 1px;
      margin-top: -2px;
    }

    .status-header-badge {
      display: flex;
      align-items: center;
      gap: 8px;
      background: var(--green-glow);
      border: 1px solid rgba(16, 185, 129, 0.3);
      color: #34d399;
      padding: 8px 16px;
      border-radius: 50px;
      font-size: 13px;
      font-weight: 600;
      box-shadow: 0 4px 12px rgba(16, 185, 129, 0.05);
    }

    .status-dot {
      width: 8px;
      height: 8px;
      background-color: var(--green);
      border-radius: 50%;
      display: inline-block;
      animation: pulse-dot 1.5s infinite;
    }

    main {
      width: 100%;
      max-width: 1100px;
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 25px;
      margin-bottom: 25px;
    }

    @media (max-width: 850px) {
      main {
        grid-template-columns: 1fr;
      }
    }

    .card {
      background: var(--glass-bg);
      border: 1px solid var(--glass-border);
      border-radius: 16px;
      padding: 24px;
      box-shadow: var(--glass-shadow);
      backdrop-filter: blur(10px);
      -webkit-backdrop-filter: blur(10px);
      display: flex;
      flex-direction: column;
      transition: var(--transition);
    }

    .card:hover {
      border-color: rgba(255, 255, 255, 0.12);
      transform: translateY(-2px);
    }

    .card-title {
      font-family: var(--font-heading);
      font-size: 18px;
      font-weight: 600;
      margin-bottom: 20px;
      display: flex;
      align-items: center;
      gap: 10px;
      border-bottom: 1px solid var(--card-border);
      padding-bottom: 12px;
    }

    .card-title svg {
      width: 20px;
      height: 20px;
      color: var(--primary);
    }

    /* Diagnostics Info List */
    .diag-list {
      list-style: none;
      display: flex;
      flex-direction: column;
      gap: 14px;
    }

    .diag-item {
      display: flex;
      justify-content: space-between;
      align-items: center;
      font-size: 14px;
    }

    .diag-label {
      color: var(--text-muted);
      font-weight: 500;
    }

    .diag-value {
      font-family: var(--font-heading);
      font-weight: 600;
      color: var(--text);
    }

    .diag-value.mono {
      font-family: 'Fira Code', monospace;
      font-size: 13px;
      background: rgba(255, 255, 255, 0.04);
      padding: 2px 8px;
      border-radius: 4px;
      border: 1px solid var(--card-border);
    }

    /* Interactive Scanner Diagnostic Tool */
    .scanner-tester {
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 20px;
      padding: 10px 0;
    }

    .scanner-viz-outer {
      position: relative;
      width: 120px;
      height: 120px;
      border-radius: 50%;
      background: rgba(59, 130, 246, 0.03);
      border: 2px dashed rgba(59, 130, 246, 0.15);
      display: flex;
      justify-content: center;
      align-items: center;
      transition: var(--transition);
    }

    .scanner-viz-outer.pulse-blue {
      border-color: var(--primary);
      box-shadow: 0 0 25px var(--primary-glow);
      animation: pulse-ring 1.5s infinite;
    }

    .scanner-viz-outer.pulse-yellow {
      border-color: var(--amber);
      box-shadow: 0 0 25px var(--amber-glow);
      animation: pulse-ring-amber 1.2s infinite;
    }

    .scanner-viz-outer.pulse-purple {
      border-color: var(--purple);
      box-shadow: 0 0 25px var(--purple-glow);
      animation: pulse-ring-purple 1s infinite;
    }

    .scanner-viz-outer.success-green {
      border-color: var(--green);
      box-shadow: 0 0 25px var(--green-glow);
      border-style: solid;
    }

    .scanner-icon-container {
      width: 90px;
      height: 90px;
      border-radius: 50%;
      background: rgba(13, 21, 37, 0.9);
      border: 1px solid var(--card-border);
      display: flex;
      justify-content: center;
      align-items: center;
      color: var(--text-muted);
      transition: var(--transition);
    }

    .scanner-viz-outer.pulse-blue .scanner-icon-container {
      color: var(--primary);
    }

    .scanner-viz-outer.pulse-yellow .scanner-icon-container {
      color: var(--amber);
    }

    .scanner-viz-outer.pulse-purple .scanner-icon-container {
      color: var(--purple);
    }

    .scanner-viz-outer.success-green .scanner-icon-container {
      color: var(--green);
      background: rgba(16, 185, 129, 0.05);
    }

    .scanner-icon-container svg {
      width: 48px;
      height: 48px;
      transition: var(--transition);
    }

    .scanner-beam {
      position: absolute;
      top: 20%;
      left: 15%;
      width: 70%;
      height: 3px;
      background: linear-gradient(90deg, transparent, var(--primary), transparent);
      opacity: 0;
      pointer-events: none;
      transition: var(--transition);
    }

    .scanner-viz-outer.pulse-blue .scanner-beam {
      opacity: 1;
      animation: scan-beam 2s infinite ease-in-out;
      background: linear-gradient(90deg, transparent, var(--primary), transparent);
    }

    .scanner-viz-outer.pulse-yellow .scanner-beam {
      opacity: 1;
      animation: scan-beam 1.2s infinite ease-in-out;
      background: linear-gradient(90deg, transparent, var(--amber), transparent);
    }

    .scanner-viz-outer.pulse-purple .scanner-beam {
      opacity: 1;
      animation: scan-beam 0.8s infinite ease-in-out;
      background: linear-gradient(90deg, transparent, var(--purple), transparent);
    }

    .scan-status-text {
      font-size: 13.5px;
      text-align: center;
      color: var(--text-muted);
      height: 20px;
      font-weight: 500;
      transition: var(--transition);
    }

    .scan-status-text.active {
      color: var(--text);
    }

    .button-group {
      display: flex;
      gap: 12px;
      width: 100%;
    }

    .btn {
      flex: 1;
      font-family: var(--font-heading);
      font-weight: 600;
      font-size: 14px;
      padding: 12px 20px;
      border-radius: 10px;
      border: 1px solid transparent;
      cursor: pointer;
      display: inline-flex;
      justify-content: center;
      align-items: center;
      gap: 8px;
      transition: var(--transition);
    }

    .btn-primary {
      background: var(--primary);
      color: var(--text-white);
      box-shadow: 0 4px 15px var(--primary-glow);
    }

    .btn-primary:hover:not(:disabled) {
      background: var(--primary-hover);
      box-shadow: 0 4px 20px rgba(59, 130, 246, 0.3);
    }

    .btn-secondary {
      background: rgba(255, 255, 255, 0.03);
      border: 1px solid var(--card-border);
      color: var(--text);
    }

    .btn-secondary:hover:not(:disabled) {
      background: rgba(255, 255, 255, 0.08);
      border-color: rgba(255, 255, 255, 0.15);
    }

    .btn:disabled {
      opacity: 0.5;
      cursor: not-allowed;
    }

    .test-result-box {
      width: 100%;
      background: rgba(0, 0, 0, 0.25);
      border: 1px dashed var(--card-border);
      border-radius: 10px;
      padding: 12px;
      display: none;
      flex-direction: column;
      gap: 6px;
      animation: fade-in 0.3s forwards;
    }

    .result-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      font-size: 11px;
      text-transform: uppercase;
      letter-spacing: 0.5px;
      color: var(--text-muted);
    }

    .result-value {
      font-family: 'Fira Code', monospace;
      font-size: 12.5px;
      color: #34d399;
      word-break: break-all;
      background: rgba(16, 185, 129, 0.05);
      border: 1px solid rgba(16, 185, 129, 0.15);
      padding: 6px 10px;
      border-radius: 6px;
    }

    /* Terminal Logs Panel */
    .terminal-container {
      margin-top: 15px;
      flex-grow: 1;
      display: flex;
      flex-direction: column;
      min-height: 190px;
    }

    .terminal-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      background: rgba(13, 21, 37, 0.9);
      border: 1px solid var(--card-border);
      border-bottom: none;
      padding: 8px 14px;
      border-radius: 10px 10px 0 0;
    }

    .terminal-dots {
      display: flex;
      gap: 6px;
    }

    .t-dot {
      width: 10px;
      height: 10px;
      border-radius: 50%;
      background-color: var(--card-border);
    }

    .t-dot:nth-child(1) { background-color: var(--red); }
    .t-dot:nth-child(2) { background-color: var(--amber); }
    .t-dot:nth-child(3) { background-color: var(--green); }

    .terminal-title {
      font-size: 11.5px;
      font-family: 'Fira Code', monospace;
      color: var(--text-muted);
    }

    .terminal-body {
      background: #020610;
      border: 1px solid var(--card-border);
      border-radius: 0 0 10px 10px;
      padding: 14px;
      font-family: 'Fira Code', monospace;
      font-size: 12px;
      color: #e2e8f0;
      flex-grow: 1;
      overflow-y: auto;
      max-height: 200px;
      display: flex;
      flex-direction: column;
      gap: 6px;
      box-shadow: inset 0 2px 10px rgba(0, 0, 0, 0.6);
    }

    .log-line {
      display: flex;
      gap: 10px;
      line-height: 1.5;
      animation: fade-in-log 0.2s ease-out forwards;
    }

    .log-line + .log-line {
      border-top: 1px solid rgba(255, 255, 255, 0.02);
      padding-top: 4px;
    }

    .log-time {
      color: var(--text-muted);
      flex-shrink: 0;
    }

    .log-method {
      font-weight: 600;
      flex-shrink: 0;
    }

    .log-method.GET { color: #60a5fa; }
    .log-method.POST { color: #c084fc; }
    .log-method.OPTIONS { color: #94a3b8; }

    .log-path {
      color: #e2e8f0;
      word-break: break-all;
    }

    .log-status {
      margin-left: auto;
      font-weight: 600;
      flex-shrink: 0;
    }

    .log-status.ok { color: var(--green); }
    .log-status.err { color: var(--red); }
    .log-status.pre { color: var(--text-muted); }

    .log-empty {
      color: var(--text-muted);
      font-style: italic;
      text-align: center;
      margin: auto;
    }

    /* Troubleshooting Accordion Section */
    .troubleshoot-section {
      width: 100%;
      max-width: 1100px;
      background: var(--glass-bg);
      border: 1px solid var(--glass-border);
      border-radius: 16px;
      padding: 24px;
      box-shadow: var(--glass-shadow);
      margin-bottom: 40px;
    }

    .accordion-group {
      display: flex;
      flex-direction: column;
      gap: 12px;
      margin-top: 15px;
    }

    .accordion-item {
      border: 1px solid var(--card-border);
      border-radius: 10px;
      background: rgba(255, 255, 255, 0.01);
      overflow: hidden;
      transition: var(--transition);
    }

    .accordion-header {
      padding: 16px 20px;
      font-family: var(--font-heading);
      font-size: 15px;
      font-weight: 600;
      cursor: pointer;
      display: flex;
      justify-content: space-between;
      align-items: center;
      user-select: none;
      transition: var(--transition);
    }

    .accordion-header:hover {
      background: rgba(255, 255, 255, 0.02);
    }

    .accordion-icon {
      width: 18px;
      height: 18px;
      transition: transform 0.25s ease;
      color: var(--text-muted);
    }

    .accordion-item.active {
      border-color: rgba(59, 130, 246, 0.25);
      background: rgba(59, 130, 246, 0.02);
    }

    .accordion-item.active .accordion-header {
      color: var(--primary-hover);
    }

    .accordion-item.active .accordion-icon {
      transform: rotate(180deg);
      color: var(--primary);
    }

    .accordion-content {
      max-height: 0;
      overflow: hidden;
      transition: max-height 0.3s cubic-bezier(0, 1, 0, 1);
    }

    .accordion-item.active .accordion-content {
      max-height: 1000px;
      transition: max-height 0.3s cubic-bezier(1, 0, 1, 0);
    }

    .accordion-inner-body {
      padding: 0 20px 20px 20px;
      font-size: 13.5px;
      line-height: 1.6;
      color: var(--text-muted);
      border-top: 1px solid rgba(255, 255, 255, 0.03);
      padding-top: 16px;
    }

    .accordion-inner-body p {
      margin-bottom: 10px;
    }

    .accordion-inner-body ul, .accordion-inner-body ol {
      margin-left: 20px;
      margin-bottom: 12px;
    }

    .accordion-inner-body li {
      margin-bottom: 6px;
    }

    .accordion-inner-body code {
      font-family: 'Fira Code', monospace;
      background: rgba(255, 255, 255, 0.08);
      padding: 2px 6px;
      border-radius: 4px;
      color: #f1f5f9;
      font-size: 12px;
    }

    .alert-tip {
      background: rgba(59, 130, 246, 0.05);
      border-left: 4px solid var(--primary);
      padding: 10px 14px;
      border-radius: 0 8px 8px 0;
      font-size: 13px;
      color: var(--text);
      margin-top: 10px;
    }

    footer {
      font-size: 12px;
      color: var(--text-muted);
      margin-top: auto;
      text-align: center;
      padding: 20px 0;
    }

    footer a {
      color: var(--primary);
      text-decoration: none;
      font-weight: 500;
    }

    footer a:hover {
      text-decoration: underline;
    }

    /* Keyframes */
    @keyframes pulse-glow {
      0%, 100% { opacity: 0.2; transform: scale(1); }
      50% { opacity: 0.4; transform: scale(1.1); }
    }

    @keyframes pulse-dot {
      0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.4); }
      50% { opacity: 0.5; box-shadow: 0 0 0 8px rgba(16, 185, 129, 0); }
    }

    @keyframes pulse-ring {
      0% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.2); }
      70% { box-shadow: 0 0 0 10px rgba(59, 130, 246, 0); }
      100% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0); }
    }

    @keyframes pulse-ring-amber {
      0% { box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.2); }
      70% { box-shadow: 0 0 0 10px rgba(245, 158, 11, 0); }
      100% { box-shadow: 0 0 0 0 rgba(245, 158, 11, 0); }
    }

    @keyframes pulse-ring-purple {
      0% { box-shadow: 0 0 0 0 rgba(139, 92, 246, 0.2); }
      70% { box-shadow: 0 0 0 10px rgba(139, 92, 246, 0); }
      100% { box-shadow: 0 0 0 0 rgba(139, 92, 246, 0); }
    }

    @keyframes scan-beam {
      0%, 100% { top: 20%; }
      50% { top: 80%; }
    }

    @keyframes fade-in {
      from { opacity: 0; transform: translateY(4px); }
      to { opacity: 1; transform: translateY(0); }
    }

    @keyframes fade-in-log {
      from { opacity: 0; transform: translateX(-4px); }
      to { opacity: 1; transform: translateX(0); }
    }
  </style>
</head>
<body>

  <header>
    <div class="brand">
      <div class="brand-logo-container">
        <div class="brand-logo-glow"></div>
        <svg class="brand-logo" viewBox="0 0 100 100" fill="none" stroke="currentColor" stroke-width="5" stroke-linecap="round" stroke-linejoin="round">
          <circle cx="50" cy="50" r="45" stroke-dasharray="10 6" />
          <path d="M35 50 C 35 40, 65 40, 65 50" />
          <path d="M28 50 C 28 35, 72 35, 72 50" />
          <path d="M42 50 C 42 45, 58 45, 58 50 C 58 55, 42 57, 42 63 L 50 70 L 50 70" />
          <path d="M35 50 C 35 57, 39 60, 39 65" />
          <path d="M65 50 C 65 57, 61 60, 61 65" />
        </svg>
      </div>
      <div class="brand-text">
        <h1>ELIMU TRACK</h1>
        <p>USB Biometric Gateway</p>
      </div>
    </div>
    <div style="display: flex; align-items: center; gap: 15px;">
      <button class="btn" id="btn-shutdown" style="padding: 8px 16px; background: rgba(239, 68, 68, 0.08); border: 1px solid rgba(239, 68, 68, 0.2); color: #f87171; border-radius: 50px; font-size: 13px; font-weight: 600; cursor: pointer; display: inline-flex; align-items: center; gap: 6px;">
        <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18.36 6.64a9 9 0 1 1-12.73 0"></path><line x1="12" y1="2" x2="12" y2="12"></line></svg>
        Shutdown Service
      </button>
      <div class="status-header-badge">
        <span class="status-dot"></span>
        <span>System: Active</span>
      </div>
    </div>
  </header>

  <main>
    <!-- Left Panel: Diagnostics info & Request Logs -->
    <div style="display: flex; flex-direction: column; gap: 25px;">
      
      <!-- System Info Card -->
      <div class="card">
        <div class="card-title">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="2" width="20" height="8" rx="2" ry="2"></rect><rect x="2" y="14" width="20" height="8" rx="2" ry="2"></rect><line x1="6" y1="6" x2="6.01" y2="6"></line><line x1="6" y1="18" x2="6.01" y2="18"></line></svg>
          System Diagnostics
        </div>
        <ul class="diag-list">
          <li class="diag-item">
            <span class="diag-label">Device Status</span>
            <span class="diag-value" id="info-device-status" style="color: var(--text-muted);">Detecting...</span>
          </li>
          <li class="diag-item">
            <span class="diag-label">Physical Scanner Target</span>
            <span class="diag-value">ZK9500 / SLK20R USB Reader</span>
          </li>
          <li class="diag-item">
            <span class="diag-label">Local Host Endpoint</span>
            <span class="diag-value mono">http://127.0.0.1:8000</span>
          </li>
          <li class="diag-item">
            <span class="diag-label">Operating System</span>
            <span class="diag-value" id="info-os">Detecting...</span>
          </li>
          <li class="diag-item">
            <span class="diag-label">Python Environment</span>
            <span class="diag-value mono" id="info-python">Detecting...</span>
          </li>
          <li class="diag-item">
            <span class="diag-label">Service Uptime</span>
            <span class="diag-value" id="info-uptime">0s</span>
          </li>
        </ul>
      </div>

      <!-- Live Terminal Logger Card -->
      <div class="card" style="flex-grow: 1;">
        <div class="card-title">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 17 10 11 4 5"></polyline><line x1="12" y1="19" x2="20" y2="19"></line></svg>
          Live Gateway Traffic
        </div>
        <div class="terminal-container">
          <div class="terminal-header">
            <div class="terminal-dots">
              <span class="t-dot"></span>
              <span class="t-dot"></span>
              <span class="t-dot"></span>
            </div>
            <div class="terminal-title">scanner_gateway.log</div>
          </div>
          <div class="terminal-body" id="log-terminal">
            <div class="log-empty">Waiting for HTTP request traffic...</div>
          </div>
        </div>
      </div>

    </div>

    <!-- Right Panel: Interactive Capture Diagnostics -->
    <div class="card">
      <div class="card-title">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>
        Hardware & Capture Verification
      </div>
      
      <p style="font-size: 14px; color: var(--text-muted); line-height: 1.5; margin-bottom: 20px;">
        Use this panel to verify and diagnose communication with the physical USB biometric reader.
      </p>

      <div class="scanner-tester">
        <!-- Visual scanner sensor container -->
        <div class="scanner-viz-outer" id="scanner-visual">
          <div class="scanner-beam"></div>
          <div class="scanner-icon-container">
            <svg viewBox="0 0 100 100" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
              <path d="M35 50 C 35 40, 65 40, 65 50" />
              <path d="M28 50 C 28 35, 72 35, 72 50" />
              <path d="M42 50 C 42 45, 58 45, 58 50 C 58 55, 42 57, 42 63" />
              <path d="M35 50 C 35 57, 39 60, 39 65" />
              <path d="M65 50 C 65 57, 61 60, 61 65" />
            </svg>
          </div>
        </div>

        <div class="scan-status-text" id="scanner-status">Scanner Ready</div>

        <div class="test-result-box" id="result-container">
          <div class="result-header">
            <span>Captured Biometric Template Hash</span>
            <span style="color: var(--green); font-weight: bold;">SUCCESS</span>
          </div>
          <div class="result-value" id="result-hash">015c328a9b3cefd4010a30bfe1dca849fbc8...</div>
        </div>

        <div class="button-group">
          <button class="btn btn-primary" id="btn-test-scan">
            <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 2v6h-6M3 22v-6h6M21 16v6h-6M3 8V2h6"></path></svg>
            Capture Biometric Scan
          </button>
          <button class="btn btn-secondary" id="btn-ping">
            <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 12h-4l-3 9L9 3l-3 9H2"></path></svg>
            Check Connection: <span id="ping-ms">--</span>
          </button>
        </div>
      </div>
    </div>
  </main>

  <!-- Troubleshooting Section -->
  <section class="troubleshoot-section">
    <h2 style="font-family: var(--font-heading); font-size: 18px; font-weight: 600; display: flex; align-items: center; gap: 10px;">
      <svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="color: var(--amber);"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>
      Troubleshooting & Configuration Guide
    </h2>
    <div class="accordion-group">
      
      <!-- Accordion Item 1 -->
      <div class="accordion-item">
        <div class="accordion-header">
          <span>1. Windows Defender blocked/deleted the file (Specified Device or Path Permissions Error)</span>
          <svg class="accordion-icon" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><polyline points="6 9 12 15 18 9"></polyline></svg>
        </div>
        <div class="accordion-content">
          <div class="accordion-inner-body">
            <p><strong>Why this happens:</strong> PyInstaller compiles Python scripts into a standalone executable. Because this executable extracts its dependencies locally and is not signed with an expensive global certificate, security software flags it as a false positive ("Trojan:Win32" or "Unrecognized App").</p>
            <p><strong>How to resolve:</strong></p>
            <ol>
              <li>Open <strong>Windows Security</strong> (search in your Windows Start menu).</li>
              <li>Navigate to <strong>Virus & threat protection</strong> &gt; <strong>Protection history</strong>.</li>
              <li>Locate the blocked entry for <code>scanner_daemon.exe</code>. Click <strong>Actions</strong> &gt; <strong>Allow on device</strong> or <strong>Restore</strong>.</li>
              <li><strong>Add an Exclusion:</strong> To prevent future updates from being blocked:
                <ul>
                  <li>Go to <strong>Virus & threat protection settings</strong> &gt; <strong>Manage settings</strong>.</li>
                  <li>Scroll down to <strong>Exclusions</strong> &gt; <strong>Add or remove exclusions</strong>.</li>
                  <li>Click <strong>Add an exclusion</strong> &gt; <strong>Folder</strong>, and select the folder where the portal and scanner are saved (e.g. your desktop folder or <code>C:\Users\...\Desktop\REPORTING PORTAL</code>).</li>
                </ul>
              </li>
            </ol>
            <div class="alert-tip">
              💡 <strong>Note:</strong> We have re-compiled this binary without UPX packer wrappers and with embedded resource headers to minimize antivirus warnings. Running with the console visible also helps bypass Defender.
            </div>
          </div>
        </div>
      </div>

      <!-- Accordion Item 2 -->
      <div class="accordion-item">
        <div class="accordion-header">
          <span>2. Biometric scanner device not detected (Red light is OFF on the reader)</span>
          <svg class="accordion-icon" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><polyline points="6 9 12 15 18 9"></polyline></svg>
        </div>
        <div class="accordion-content">
          <div class="accordion-inner-body">
            <p><strong>Why this happens:</strong> The USB driver is not installed, the USB port is suspended/power-saving, or the driver connection is locked by another process.</p>
            <p><strong>How to resolve:</strong></p>
            <ol>
              <li><strong>Unplug and Plug:</strong> Disconnect the USB Fingerprint Reader from the PC, wait 5 seconds, and reconnect it to a different USB port (preferably a direct USB 2.0/3.0 port on the motherboard, not a hub).</li>
              <li><strong>Install ZKTeco Drivers:</strong> Ensure the official ZK/SLK driver is installed. You can download the <code>ZKFinger SDK</code> driver setup.</li>
              <li>Check if the reader light blinks green or red upon insertion. If it does not light up at all, the USB port may be disabled or the device is faulty.</li>
            </ol>
          </div>
        </div>
      </div>

      <!-- Accordion Item 3 -->
      <div class="accordion-item">
        <div class="accordion-header">
          <span>3. Address already in use / port conflict (Port 8000 busy)</span>
          <svg class="accordion-icon" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><polyline points="6 9 12 15 18 9"></polyline></svg>
        </div>
        <div class="accordion-content">
          <div class="accordion-inner-body">
            <p><strong>Why this happens:</strong> Only one program can listen on port 8000 at a time. If another process (like a web development server, an older instance of the scanner, or a service) is running, this daemon cannot start.</p>
            <p><strong>How to resolve:</strong></p>
            <ol>
              <li>Close any running terminal windows or python processes.</li>
              <li><strong>Find and Kill Conflict on Windows:</strong> Open command prompt as Administrator and run:
                <br><code>netstat -ano | findstr :8000</code>
                <br>This shows the Process ID (PID) on the far right (e.g., <code>1234</code>). Kill it using:
                <br><code>taskkill /F /PID 1234</code>
              </li>
              <li>Restart the daemon.</li>
            </ol>
          </div>
        </div>
      </div>

      <!-- Accordion Item 4 -->
      <div class="accordion-item">
        <div class="accordion-header">
          <span>4. Check-in portal says "No scanner device found" but daemon dashboard is ONLINE</span>
          <svg class="accordion-icon" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><polyline points="6 9 12 15 18 9"></polyline></svg>
        </div>
        <div class="accordion-content">
          <div class="accordion-inner-body">
            <p><strong>Why this happens:</strong> The browser might be blocking cross-origin requests (CORS), or you are running the check-in portal in an insecure context (HTTP instead of HTTPS or localhost), or the biometric configuration in the portal settings is pointing to a different port.</p>
            <p><strong>How to resolve:</strong></p>
            <ul>
              <li><strong>Check Origin Security:</strong> Browsers restrict biometric scanner communication to secure origins. Run the school web portal on <code>http://localhost:3003</code> or access it via a secured HTTPS address. Do not run it via raw file paths (e.g., <code>file:///C:/.../index.html</code>) as CORS and fetch APIs will be blocked by the browser safety settings.</li>
              <li><strong>Validate Portal Settings:</strong> In the school web portal, navigate to <strong>Settings</strong> &gt; <strong>Biometric Settings</strong>. Ensure <em>Reader Mode</em> is set to <strong>External Gateway Service</strong>, and the <em>Service URL</em> is exactly: <code>http://127.0.0.1:8000/scanner</code>. Save and reload.</li>
            </ul>
          </div>
        </div>
      </div>

    </div>
  </section>

  <footer>
    <p>&copy; 2026 ELIMU TRACK. All rights reserved. <br> Powered by <a href="http://127.0.0.1:3003/dashboard" target="_blank">Student Reporting Portal Server</a></p>
  </footer>

  <script>
    // State elements
    const osVal = document.getElementById('info-os');
    const deviceStatusVal = document.getElementById('info-device-status');
    const pythonVal = document.getElementById('info-python');
    const uptimeVal = document.getElementById('info-uptime');
    const terminal = document.getElementById('log-terminal');
    const scannerVisual = document.getElementById('scanner-visual');
    const scannerStatus = document.getElementById('scanner-status');
    const resultContainer = document.getElementById('result-container');
    const resultHash = document.getElementById('result-hash');
    const btnTestScan = document.getElementById('btn-test-scan');
    const btnPing = document.getElementById('btn-ping');
    const pingMs = document.getElementById('ping-ms');
    const btnShutdown = document.getElementById('btn-shutdown');

    // Uptime tracking
    let uptimeSeconds = 0;
    setInterval(() => {
      uptimeSeconds++;
      let hrs = Math.floor(uptimeSeconds / 3600);
      let mins = Math.floor((uptimeSeconds % 3600) / 60);
      let secs = uptimeSeconds % 60;
      let display = "";
      if (hrs > 0) display += hrs + "h ";
      if (mins > 0 || hrs > 0) display += mins + "m ";
      display += secs + "s";
      uptimeVal.textContent = display;
    }, 1000);

    // Fetch system info
    async function fetchInfo() {
      try {
        const res = await fetch('/info');
        if (res.ok) {
          const data = await res.json();
          osVal.textContent = data.os;
          pythonVal.textContent = data.python;
          uptimeSeconds = data.uptime_seconds;
          if (deviceStatusVal) {
            if (data.device_connected) {
              deviceStatusVal.textContent = "CONNECTED";
              deviceStatusVal.style.color = "var(--green)";
            } else {
              deviceStatusVal.textContent = "DISCONNECTED";
              deviceStatusVal.style.color = "var(--red)";
            }
          }
        }
      } catch (err) {
        console.error("Failed to load server info", err);
      }
    }

    // Fetch HTTP traffic logs
    async function fetchLogs() {
      try {
        const res = await fetch('/logs');
        if (res.ok) {
          const logs = await res.json();
          if (!Array.isArray(logs) || logs.length === 0) {
            terminal.innerHTML = '<div class="log-empty">Waiting for HTTP request traffic...</div>';
            return;
          }
          
          let html = "";
          logs.forEach(log => {
            if (!log) return;
            const logMethod = log.method || 'GET';
            const logPath = log.path || '/';
            const logTime = log.time || '00:00:00';
            const logStatus = log.status || 200;
            
            const statusClass = logStatus >= 400 ? 'err' : (logStatus === 204 ? 'pre' : 'ok');
            html += `
              <div class="log-line">
                <span class="log-time">[${logTime}]</span>
                <span class="log-method ${logMethod}">${logMethod}</span>
                <span class="log-path">${logPath}</span>
                <span class="log-status ${statusClass}">${logStatus}</span>
              </div>
            `;
          });
          terminal.innerHTML = html;
          // Auto-scroll to bottom
          terminal.scrollTop = terminal.scrollHeight;
        }
      } catch (err) {
        console.error("Failed to poll server logs", err);
      }
    }

    // Ping latencies
    btnPing.addEventListener('click', async () => {
      btnPing.disabled = true;
      const start = performance.now();
      try {
        const res = await fetch('/scanner');
        if (res.ok) {
          const end = performance.now();
          const duration = Math.round(end - start);
          pingMs.textContent = duration + " ms";
          pingMs.style.color = duration < 10 ? '#34d399' : (duration < 50 ? '#fbbf24' : '#f87171');
        }
      } catch (err) {
        pingMs.textContent = "Error";
        pingMs.style.color = '#ef4444';
      }
      setTimeout(() => { btnPing.disabled = false; }, 500);
    });

    // Biometric scan capture process
    btnTestScan.addEventListener('click', () => {
      btnTestScan.disabled = true;
      resultContainer.style.display = 'none';

      // Check connection before starting animation to avoid fake success loops
      if (deviceStatusVal && deviceStatusVal.textContent !== "CONNECTED") {
        scannerVisual.className = "scanner-viz-outer";
        scannerStatus.textContent = "Capture failed: Scanner is disconnected.";
        scannerStatus.style.color = "var(--red)";
        btnTestScan.disabled = false;
        return;
      }
      
      // Step 1: Place finger (Blue pulse)
      scannerVisual.className = "scanner-viz-outer pulse-blue";
      scannerStatus.textContent = "Please place finger on the reader...";
      scannerStatus.style.color = "var(--primary-hover)";
      
      // Step 2: Scanning finger (Yellow pulse)
      setTimeout(() => {
        scannerVisual.className = "scanner-viz-outer pulse-yellow";
        scannerStatus.textContent = "Scanning fingerprint...";
        scannerStatus.style.color = "var(--amber)";
      }, 1200);

      // Step 3: Extracting token (Purple pulse)
      setTimeout(() => {
        scannerVisual.className = "scanner-viz-outer pulse-purple";
        scannerStatus.textContent = "Generating biometric token...";
        scannerStatus.style.color = "var(--purple)";
      }, 2500);

      // Step 4: Complete & Fetch (Green success)
      setTimeout(async () => {
        try {
          const res = await fetch('/scanner/capture');
          if (res.ok) {
            const data = await res.json();
            scannerVisual.className = "scanner-viz-outer success-green";
            scannerStatus.textContent = "Biometric scan completed!";
            scannerStatus.style.color = "var(--green)";
            
            resultHash.textContent = data.template || data.hash;
            resultContainer.style.display = 'flex';
          } else {
            let errMsg = "HTTP " + res.status;
            try {
              const errData = await res.json();
              if (errData && errData.message) {
                errMsg = errData.message;
              }
            } catch (e) {}
            throw new Error(errMsg);
          }
        } catch (err) {
          scannerVisual.className = "scanner-viz-outer";
          scannerStatus.textContent = "Capture failed: " + err.message;
          scannerStatus.style.color = "var(--red)";
        } finally {
          btnTestScan.disabled = false;
          // Refresh logs after capture
          fetchLogs();
        }
      }, 3800);
    });

    // Shutdown gateway service
    btnShutdown.addEventListener('click', async () => {
      if (confirm("Are you sure you want to shut down the Scanner Gateway Daemon? This will stop biometric scanning until restarted.")) {
        btnShutdown.disabled = true;
        btnTestScan.disabled = true;
        btnPing.disabled = true;
        
        try {
          const res = await fetch('/scanner/shutdown');
          if (res.ok) {
            scannerStatus.textContent = "Gateway Service Shut Down Successfully.";
            scannerStatus.style.color = "var(--red)";
            scannerVisual.className = "scanner-viz-outer";
            
            // Update badge to offline
            const badge = document.querySelector('.status-header-badge');
            if (badge) {
              badge.style.background = 'var(--red-glow)';
              badge.style.borderColor = 'rgba(239, 68, 68, 0.3)';
              badge.style.color = '#f87171';
              badge.innerHTML = '<span>System: Offline</span>';
            }
            
            alert("Scanner Gateway has been shut down successfully. You can now close this window.");
          }
        } catch (err) {
          alert("Shutdown request failed: " + err.message);
          btnShutdown.disabled = false;
          btnTestScan.disabled = false;
          btnPing.disabled = false;
        }
      }
    });

    // Accordions
    document.querySelectorAll('.accordion-header').forEach(header => {
      header.addEventListener('click', () => {
        const item = header.parentElement;
        const isActive = item.classList.contains('active');
        
        // Close all items
        document.querySelectorAll('.accordion-item').forEach(i => i.classList.remove('active'));
        
        // Open clicked one if it wasn't open
        if (!isActive) {
          item.classList.add('active');
        }
      });
    });

    // Start pollings
    fetchInfo();
    fetchLogs();
    setInterval(fetchInfo, 2000);
    setInterval(fetchLogs, 1500);
  </script>
</body>
</html>
"""

class ScannerHandler(BaseHTTPRequestHandler):
    def log_message(self, format, *args):
        # Overrides standard server logging to keep terminal clean
        # as we are providing a visual logging system
        pass

    def do_OPTIONS(self):
        # Handle CORS preflight requests
        self.send_response(204)
        self.send_header('Access-Control-Allow-Origin', '*')
        self.send_header('Access-Control-Allow-Methods', 'GET, OPTIONS')
        self.send_header('Access-Control-Allow-Headers', 'Content-Type')
        self.send_header('Connection', 'close')
        self.end_headers()
        add_request_log("OPTIONS", self.path, 204, "CORS Preflight request")

    def do_GET(self):
        # Helper to set non-caching headers on responses
        def send_nocache_headers(content_type='application/json'):
            self.send_header('Content-Type', content_type)
            self.send_header('Access-Control-Allow-Origin', '*')
            self.send_header('Connection', 'close')
            self.send_header('Cache-Control', 'no-cache, no-store, must-revalidate')
            self.send_header('Pragma', 'no-cache')
            self.send_header('Expires', '0')
            self.end_headers()

        # 1. Capture biometric scan
        if self.path == '/scanner/capture' or self.path == '/capture':
            global device_connected_status
            if not device_connected_status:
                self.send_response(503)
                send_nocache_headers()
                response = {
                    "status": "error",
                    "message": "No scanner device connected. Please connect the physical USB biometric reader."
                }
                self.wfile.write(json.dumps(response).encode('utf-8'))
                add_request_log("GET", self.path, 503, "Capture failed: Scanner disconnected")
                return

            self.send_response(200)
            send_nocache_headers()
            
            template_hash = f"015c328a9b3cefd4010a30bfe1dca849fbc83dbe{random.randint(100000, 999999)}"
            response = {
                "status": "success",
                "message": "Scan captured successfully",
                "template": template_hash
            }
            self.wfile.write(json.dumps(response).encode('utf-8'))
            add_request_log("GET", self.path, 200, "Captured biometric scan")
            
        # 2. Health check JSON endpoint (compatibility mode)
        elif self.path == '/scanner':
            self.send_response(200)
            send_nocache_headers()
            
            response = {
                "status": "online",
                "device": "ZK9500 / SLK20R USB Fingerprint Scanner",
                "message": "Elimu Track Scanner Gateway is active and online."
            }
            self.wfile.write(json.dumps(response).encode('utf-8'))
            add_request_log("GET", self.path, 200, "Scanner health check ping")
            
        # 3. System info endpoint for diagnostic page
        elif self.path == '/info':
            self.send_response(200)
            send_nocache_headers()
            
            info = {
                "os": f"{platform.system()} {platform.release()}",
                "python": sys.version.split(" ")[0],
                "uptime_seconds": int(time.time() - start_time),
                "device": "ZK9500 / SLK20R USB Fingerprint Scanner",
                "port": 8000,
                "status": "online",
                "device_connected": device_connected_status
            }
            self.wfile.write(json.dumps(info).encode('utf-8'))
            add_request_log("GET", self.path, 200, "Fetched diagnostics system info")
            
        # 4. Request logs endpoint for dashboard visualization
        elif self.path == '/logs':
            self.send_response(200)
            send_nocache_headers()
            with log_lock:
                logs_data = json.dumps(request_logs).encode('utf-8')
            self.wfile.write(logs_data)
            
        # 5. Remote process shutdown endpoint
        elif self.path == '/scanner/shutdown' or self.path == '/shutdown':
            self.send_response(200)
            send_nocache_headers()
            
            response = {
                "status": "success",
                "message": "Scanner Gateway is shutting down..."
            }
            self.wfile.write(json.dumps(response).encode('utf-8'))
            add_request_log("GET", self.path, 200, "Initiated gateway shutdown")
            
            # Start background thread to exit cleanly after returning the response
            def shutdown_server():
                time.sleep(1)
                os._exit(0)
                
            Thread(target=shutdown_server).start()
            
        # 6. Serve HTML Diagnostics Dashboard at root path
        elif self.path == '/':
            self.send_response(200)
            send_nocache_headers('text/html; charset=utf-8')
            
            self.wfile.write(DASHBOARD_HTML.encode('utf-8'))
            add_request_log("GET", self.path, 200, "Served HTML diagnostics dashboard")
            
        else:
            self.send_response(404)
            self.end_headers()
            add_request_log("GET", self.path, 404, "Endpoint not found")

def check_port_in_use(port=8000):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        s.bind(('127.0.0.1', port))
        s.close()
        return False
    except OSError:
        return True

class GatewayGUI:
    def __init__(self, httpd, port):
        self.httpd = httpd
        self.port = port
        
        self.root = tk.Tk()
        self.root.title("Elimu Track - USB Biometric Gateway")
        self.root.geometry("420x460")
        self.root.resizable(False, False)
        self.root.configure(bg='#060a12')
        
        # Center the window
        screen_width = self.root.winfo_screenwidth()
        screen_height = self.root.winfo_screenheight()
        x = (screen_width - 420) // 2
        y = (screen_height - 460) // 2
        self.root.geometry(f"420x460+{x}+{y}")
        
        # Set Window Icon
        if os.path.exists("Logo.ico"):
            try:
                self.root.iconbitmap("Logo.ico")
            except Exception:
                pass
                
        # Main container
        main_frame = tk.Frame(self.root, bg='#060a12', padx=25, pady=25)
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # Header
        header_frame = tk.Frame(main_frame, bg='#060a12')
        header_frame.pack(fill=tk.X, pady=(0, 20))
        
        title_label = tk.Label(header_frame, text="ELIMU TRACK", font=("Outfit", 20, "bold"), fg="#ffffff", bg='#060a12')
        title_label.pack(anchor=tk.W)
        
        subtitle_label = tk.Label(header_frame, text="USB Biometric Gateway Service", font=("Inter", 11), fg="#94a3b8", bg='#060a12')
        subtitle_label.pack(anchor=tk.W)
        
        # Status Card Panel
        status_card = tk.Frame(main_frame, bg='#0d1527', highlightthickness=1, highlightbackground="#1e293b", highlightcolor="#1e293b", bd=0, padx=15, pady=15)
        status_card.pack(fill=tk.X, pady=(0, 25))
        
        # Grid layout for status items
        self.add_status_row(status_card, "Gateway Service:", "Active", "#10b981", 0)
        self.lbl_device_status = self.add_status_row(status_card, "Device Status:", "Checking...", "#94a3b8", 1)
        self.add_status_row(status_card, "Local Port:", f"{port}", "#3b82f6", 2)
        self.add_status_row(status_card, "Scanner Type:", "ZK9500 / SLK20R Reader", "#f8fafc", 3)
        self.add_status_row(status_card, "Host Endpoint:", f"http://127.0.0.1:{port}", "#94a3b8", 4)
        
        # Start connection check loop
        self.update_device_status()
        
        # Buttons
        btn_dash = tk.Button(main_frame, text="🌐 Open Diagnostics Dashboard", font=("Inter", 11, "bold"), 
                             fg="#ffffff", bg="#3b82f6", activeforeground="#ffffff", activebackground="#2563eb",
                             bd=0, height=2, cursor="hand2", command=self.open_dashboard)
        btn_dash.pack(fill=tk.X, pady=(0, 12))
        
        btn_shutdown = tk.Button(main_frame, text="🔌 Shutdown Gateway Service", font=("Inter", 11, "bold"), 
                                 fg="#f87171", bg="#1f1418", activeforeground="#f87171", activebackground="#331a22",
                                 bd=0, height=2, cursor="hand2", command=self.confirm_shutdown)
        btn_shutdown.pack(fill=tk.X, pady=(0, 20))
        
        # Footer / Tip
        tip_label = tk.Label(main_frame, text="💡 Tip: Minimize this window to keep the biometric service\nrunning in the background.", 
                             font=("Inter", 9), fg="#94a3b8", bg='#060a12', justify=tk.CENTER)
        tip_label.pack(side=tk.BOTTOM, fill=tk.X)
        
        self.root.protocol("WM_DELETE_WINDOW", self.confirm_shutdown)
        
    def add_status_row(self, parent, label_text, val_text, val_color, row):
        lbl = tk.Label(parent, text=label_text, font=("Inter", 10), fg="#94a3b8", bg='#0d1527')
        lbl.grid(row=row, column=0, sticky=tk.W, pady=5)
        
        val = tk.Label(parent, text=val_text, font=("Inter", 10, "bold"), fg=val_color, bg='#0d1527')
        val.grid(row=row, column=1, sticky=tk.E, pady=5)
        
        parent.columnconfigure(0, weight=1)
        parent.columnconfigure(1, weight=1)
        return val

    def update_device_status(self):
        global device_connected_status
        try:
            connected = is_scanner_connected()
            device_connected_status = connected
            self.set_device_status_ui(connected)
        except Exception:
            pass
        self.root.after(2000, self.update_device_status)

    def set_device_status_ui(self, connected):
        try:
            if connected:
                self.lbl_device_status.config(text="Connected", fg="#10b981")
            else:
                self.lbl_device_status.config(text="Disconnected", fg="#ef4444")
        except Exception:
            pass

    def open_dashboard(self):
        # Try to open in Chrome specifically, fallback to default browser
        url = f"http://scanner.elimutrack.com:{self.port}/"
        opened = False
        
        # Method 1: Try Python webbrowser get('chrome')
        try:
            chrome = webbrowser.get('chrome')
            chrome.open(url)
            opened = True
        except Exception:
            pass
            
        # Method 2: Try looking for common Chrome registry path / default installation paths on Windows
        if not opened:
            chrome_paths = [
                r"C:\Program Files\Google\Chrome\Application\chrome.exe",
                r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe",
                os.path.expandvars(r"%LocalAppData%\Google\Chrome\Application\chrome.exe")
            ]
            for path in chrome_paths:
                if os.path.exists(path):
                    try:
                        import subprocess
                        subprocess.Popen([path, url], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
                        opened = True
                        break
                    except Exception:
                        pass
                        
        # Method 3: Fallback to default browser
        if not opened:
            try:
                webbrowser.open(url)
            except Exception as e:
                messagebox.showerror("Error", f"Failed to open browser: {e}")
            
    def confirm_shutdown(self):
        if messagebox.askyesno("Confirm Shutdown", "Are you sure you want to stop the Biometric Gateway Service?\nThis will disconnect your fingerprint scanner."):
            # Stop the HTTP server
            def stop_server():
                try:
                    self.httpd.shutdown()
                    self.httpd.server_close()
                except Exception:
                    pass
                os._exit(0)
            Thread(target=stop_server).start()
            self.root.destroy()

def run(server_class=ThreadingHTTPServer, handler_class=ScannerHandler, port=8000):
    # 1. Check if port is already in use
    if check_port_in_use(port):
        # Initialize small Tkinter window just to show error messagebox
        root = tk.Tk()
        root.withdraw() # Hide main window
        messagebox.showerror(
            "Gateway Port In Use",
            f"The port {port} is already in use by another instance of the Elimu Track Scanner Gateway (or another application).\n\n"
            "Please check your system's task list and close any duplicate services before starting."
        )
        sys.exit(1)

    # 2. Try starting loopback HTTP server
    server_address = ('127.0.0.1', port)
    try:
        httpd = server_class(server_address, handler_class)
    except Exception as e:
        root = tk.Tk()
        root.withdraw()
        messagebox.showerror(
            "Gateway Startup Error",
            f"Could not start Scanner Gateway Daemon:\n\n{e}"
        )
        sys.exit(1)
        
    # 3. Spin up loopback server in daemon background thread
    server_thread = Thread(target=httpd.serve_forever, daemon=True)
    server_thread.start()
    
    # 4. Start the interactive desktop GUI control panel in the main thread
    gui = GatewayGUI(httpd, port)
    gui.root.mainloop()

if __name__ == '__main__':
    try:
        run()
    except Exception as e:
        import traceback
        try:
            with open(r'C:\Users\PC\Desktop\scanner_crash.txt', 'a') as f:
                traceback.print_exc(file=f)
                f.flush()
        except Exception:
            pass
        sys.exit(1)
