; Mosis Designer Test Utilities
; AutoHotkey v2

#Include "..\config.ahk"

; Find the designer window and return its handle
FindDesignerWindow() {
    return WinExist(WINDOW_TITLE)
}

; Wait for the designer window to appear
; Returns window handle or 0 if timeout
WaitForDesignerWindow(timeout := 0) {
    if (timeout = 0)
        timeout := STARTUP_TIMEOUT

    startTime := A_TickCount
    while (A_TickCount - startTime < timeout) {
        hwnd := FindDesignerWindow()
        if (hwnd) {
            ; Give window time to fully initialize
            Sleep(500)
            return hwnd
        }
        Sleep(100)
    }
    return 0
}

; Get the client area position of the designer window
; Returns {x, y, w, h} or 0 if window not found
GetClientArea(hwnd := 0) {
    if (hwnd = 0)
        hwnd := FindDesignerWindow()
    if (!hwnd)
        return 0

    ; Get window position
    WinGetPos(&winX, &winY, &winW, &winH, hwnd)

    ; Get client area - for GLFW windows, typically minimal border
    ; Client area starts after title bar
    ; We'll estimate based on typical Windows decorations
    ; Title bar is usually ~30-40 pixels, borders ~8 pixels each side

    ; For a more accurate approach, we could use DllCall to GetClientRect
    ; But for GLFW windows, the client area often matches the requested size

    ; Calculate client area assuming standard decorations
    borderWidth := 8
    titleHeight := 31

    return {
        x: winX + borderWidth,
        y: winY + titleHeight,
        w: PHONE_WIDTH,
        h: PHONE_HEIGHT
    }
}

; Convert phone coordinates to screen coordinates
PhoneToScreen(phoneX, phoneY, hwnd := 0) {
    client := GetClientArea(hwnd)
    if (!client)
        return 0

    return {
        x: client.x + phoneX,
        y: client.y + phoneY
    }
}

; Click at phone coordinates
ClickPhone(phoneX, phoneY, hwnd := 0) {
    if (hwnd = 0)
        hwnd := FindDesignerWindow()
    if (!hwnd) {
        LogMessage("ERROR: Designer window not found")
        return false
    }

    ; Activate window first
    WinActivate(hwnd)
    Sleep(50)

    screen := PhoneToScreen(phoneX, phoneY, hwnd)
    if (!screen) {
        LogMessage("ERROR: Could not convert coordinates")
        return false
    }

    ; Move and click
    Click(screen.x, screen.y)
    Sleep(CLICK_DELAY)

    LogMessage("Clicked at phone(" . phoneX . "," . phoneY . ") -> screen(" . screen.x . "," . screen.y . ")")
    return true
}

; Click on a named app icon
ClickApp(appName, hwnd := 0) {
    if (!APP_POSITIONS.Has(appName)) {
        LogMessage("ERROR: Unknown app: " . appName)
        return false
    }

    pos := APP_POSITIONS[appName]
    LogMessage("Clicking app: " . appName)
    return ClickPhone(pos.x, pos.y, hwnd)
}

; Click on a dock item
ClickDock(appName, hwnd := 0) {
    if (!DOCK_POSITIONS.Has(appName)) {
        LogMessage("ERROR: Unknown dock item: " . appName)
        return false
    }

    pos := DOCK_POSITIONS[appName]
    LogMessage("Clicking dock: " . appName)
    return ClickPhone(pos.x, pos.y, hwnd)
}

; Send keyboard input to the designer
SendToDesigner(keys, hwnd := 0) {
    if (hwnd = 0)
        hwnd := FindDesignerWindow()
    if (!hwnd)
        return false

    WinActivate(hwnd)
    Sleep(50)
    Send(keys)
    return true
}

; Read the log file and check for a pattern
; Returns the matching line or empty string
CheckLogFor(pattern, logFile := 0) {
    if (logFile = 0)
        logFile := LOG_FILE

    if (!FileExist(logFile))
        return ""

    content := FileRead(logFile)
    lines := StrSplit(content, "`n")

    ; Search from end (most recent)
    loop lines.Length {
        idx := lines.Length - A_Index + 1
        line := lines[idx]
        if (InStr(line, pattern))
            return line
    }

    return ""
}

; Wait for a specific pattern to appear in the log
WaitForLog(pattern, timeout := 0, logFile := 0) {
    if (timeout = 0)
        timeout := NAVIGATION_TIMEOUT
    if (logFile = 0)
        logFile := LOG_FILE

    startTime := A_TickCount
    while (A_TickCount - startTime < timeout) {
        result := CheckLogFor(pattern, logFile)
        if (result)
            return result
        Sleep(100)
    }
    return ""
}

; Wait for navigation to a specific screen
WaitForNavigation(screenName, timeout := 0) {
    pattern := "Navigated to: " . screenName
    return WaitForLog(pattern, timeout)
}

; Wait for back navigation
WaitForBack(timeout := 0) {
    return WaitForLog("Back to:", timeout)
}

; Take a screenshot of the designer window
CaptureScreenshot(filename, hwnd := 0) {
    if (hwnd = 0)
        hwnd := FindDesignerWindow()
    if (!hwnd)
        return false

    ; Ensure screenshot directory exists
    if (!DirExist(SCREENSHOT_DIR))
        DirCreate(SCREENSHOT_DIR)

    fullPath := SCREENSHOT_DIR . "\" . filename

    ; Use built-in screenshot capability
    ; Note: This captures the entire window including decorations
    try {
        ; Activate and bring to front
        WinActivate(hwnd)
        Sleep(100)

        ; Get window position
        WinGetPos(&x, &y, &w, &h, hwnd)

        ; Use GDI+ or Windows API for screenshot
        ; For simplicity, we'll use the Snipping approach
        ; In production, you'd use a proper GDI+ screenshot

        LogMessage("Screenshot requested: " . fullPath . " (not implemented)")
        return false
    } catch as e {
        LogMessage("Screenshot error: " . e.Message)
        return false
    }
}

; Log a message with timestamp
LogMessage(msg) {
    timestamp := FormatTime(, "yyyy-MM-dd HH:mm:ss")
    line := "[" . timestamp . "] " . msg . "`n"

    ; Write to console
    OutputDebug(line)

    ; Also append to a test log file
    testLog := A_ScriptDir . "\test_run.log"
    FileAppend(line, testLog)
}

; Clear test logs
ClearLogs() {
    testLog := A_ScriptDir . "\test_run.log"
    if (FileExist(testLog))
        FileDelete(testLog)
    if (FileExist(LOG_FILE))
        FileDelete(LOG_FILE)
}

; Close the designer window
CloseDesigner(hwnd := 0) {
    if (hwnd = 0)
        hwnd := FindDesignerWindow()
    if (hwnd) {
        WinClose(hwnd)
        Sleep(500)
    }
}

; Kill any running designer processes
KillDesigner() {
    try {
        Run('taskkill /F /IM mosis-designer.exe', , "Hide")
    }
    Sleep(500)
}

; Start the designer process with log redirection
; Returns the process ID or 0 on failure
StartDesigner(rmlFile := 0) {
    if (rmlFile = 0)
        rmlFile := HOME_RML

    ; Build command with output redirection
    cmd := '"' . DESIGNER_EXE . '" "' . rmlFile . '"'

    LogMessage("Starting designer: " . cmd)

    ; Start process and capture output
    ; We'll redirect to a log file
    fullCmd := A_ComSpec . ' /c "' . cmd . '" > "' . LOG_FILE . '" 2>&1'

    Run(fullCmd, A_ScriptDir, "Hide", &pid)

    if (pid) {
        LogMessage("Designer started with PID: " . pid)
        return pid
    }

    LogMessage("ERROR: Failed to start designer")
    return 0
}

; Assert helper - logs pass/fail and returns result
Assert(condition, testName) {
    if (condition) {
        LogMessage("PASS: " . testName)
        return true
    } else {
        LogMessage("FAIL: " . testName)
        return false
    }
}
