# Godot Client Architecture

## Overview

The Godot client is a native frontend for Code of Conquest that connects to the Flask backend via REST API. It provides a consistent experience across desktop, mobile, and web platforms.

## Architecture Diagram

```
┌─────────────────────────────────────────────────────────────┐
│                     Godot Client                             │
│                                                               │
│  ┌───────────────────────────────────────────────────────┐  │
│  │                  UI Layer (Scenes)                     │  │
│  │  ┌────────┐  ┌────────┐  ┌────────┐  ┌────────┐      │  │
│  │  │  Auth  │  │  Char  │  │ Combat │  │ World  │      │  │
│  │  │ Screens│  │  Mgmt  │  │   UI   │  │   UI   │      │  │
│  │  └────────┘  └────────┘  └────────┘  └────────┘      │  │
│  └───────────────────────────────────────────────────────┘  │
│                          │                                   │
│  ┌───────────────────────────────────────────────────────┐  │
│  │              Component Layer                           │  │
│  │  ┌─────────┐  ┌──────┐  ┌──────────┐  ┌──────┐       │  │
│  │  │ Buttons │  │ Cards│  │FormFields│  │ ...  │       │  │
│  │  └─────────┘  └──────┘  └──────────┘  └──────┘       │  │
│  └───────────────────────────────────────────────────────┘  │
│                          │                                   │
│  ┌───────────────────────────────────────────────────────┐  │
│  │            Service Layer (Singletons)                  │  │
│  │  ┌──────────────┐           ┌────────────────┐        │  │
│  │  │ HTTPClient   │           │ StateManager   │        │  │
│  │  │ - API calls  │           │ - Session      │        │  │
│  │  │ - Auth token │           │ - Characters   │        │  │
│  │  │ - Error      │           │ - Wizard state │        │  │
│  │  │   handling   │           │ - Persistence  │        │  │
│  │  └──────────────┘           └────────────────┘        │  │
│  └───────────────────────────────────────────────────────┘  │
│                          │                                   │
│  ┌───────────────────────────────────────────────────────┐  │
│  │              Data Models (GDScript)                    │  │
│  │  Character, CharacterClass, Origin, Skill, etc.       │  │
│  └───────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘
                           │
                    HTTP/JSON REST API
                           │
┌─────────────────────────────────────────────────────────────┐
│                    Flask Backend                             │
│                  (Existing REST API)                         │
│                 /api/v1/auth/*, /api/v1/characters/*         │
└─────────────────────────────────────────────────────────────┘
```

## Layers

### 1. UI Layer (Scenes)

**Location**: `scenes/`

Individual screens and pages of the application.

**Responsibilities**:
- Render UI elements
- Handle user input
- Display data from StateManager
- Trigger API calls via HTTPClient
- Navigate between scenes

**Examples**:
- `scenes/auth/login.tscn` - Login screen
- `scenes/character/character_list.tscn` - Character list
- `scenes/character/create_wizard_step1.tscn` - Character creation step 1

**Communication**:
- Uses components from Component Layer
- Reads/writes to StateManager
- Calls HTTPClient for API requests
- Emits signals for navigation

### 2. Component Layer

**Location**: `scenes/components/`, `scripts/components/`

Reusable UI components shared across scenes.

**Responsibilities**:
- Provide consistent UI elements
- Encapsulate common patterns
- Handle component-specific logic
- Emit signals for parent scenes

**Examples**:
- `CustomButton` - Themed button with variants
- `Card` - Container with header/footer
- `FormField` - Input field with validation

**Communication**:
- Receives props from parent scenes
- Emits signals to parent scenes
- Uses ThemeColors for styling
- Independent of business logic

### 3. Service Layer (Singletons)

**Location**: `scripts/services/`

Global services accessible from anywhere in the app.

**Responsibilities**:
- Manage API communication
- Store global state
- Handle persistence
- Provide utilities

**Singletons**:

#### HTTPClient
- Base URL configuration
- Authentication token management
- JSON request/response handling
- Error handling and retries
- Timeout management

**API**:
```gdscript
HTTPClient.http_get(endpoint, callback, error_callback)
HTTPClient.http_post(endpoint, data, callback, error_callback)
HTTPClient.http_put(endpoint, data, callback, error_callback)
HTTPClient.http_delete(endpoint, callback, error_callback)
HTTPClient.http_patch(endpoint, data, callback, error_callback)
HTTPClient.set_auth_token(token)
```

#### StateManager
- User session state
- Character data cache
- Wizard state (character creation)
- Navigation history
- Settings and preferences
- Save/load to local storage

**API**:
```gdscript
StateManager.set_user_session(user_data, token)
StateManager.get_characters()
StateManager.set_wizard_origin(origin_data)
StateManager.save_state()
```

**Communication**:
- HTTPClient → Backend API (HTTP)
- StateManager → Local storage (FileAccess)
- Services → UI via signals

**Accessing Autoloads**:

Autoload singletons can be accessed in three ways:

1. **Direct access** (simple, works in `_ready()` and later):
```gdscript
func _ready():
    HTTPClient.http_get("/api/v1/characters", _on_success)
```

2. **Get node explicitly** (verbose but always works):
```gdscript
func _on_button_clicked():
    get_node("/root/HTTPClient").http_get("/api/v1/characters", _on_success)
```

3. **Cached reference with @onready** (best for multiple uses):
```gdscript
@onready var http_client = get_node("/root/HTTPClient")
@onready var state_manager = get_node("/root/StateManager")

func _ready():
    http_client.http_get("/api/v1/characters", _on_success)
```

**Important**: When one autoload needs to reference another (like StateManager calling HTTPClient), use `@onready` to avoid script parsing errors. Direct access (`HTTPClient.method()`) only works during runtime, not during script compilation.

### 4. Data Models

**Location**: `scripts/models/`

GDScript classes that mirror backend data structures.

**Responsibilities**:
- Define typed data structures
- Validate data
- Serialize/deserialize JSON
- Provide helper methods

**Examples**:
```gdscript
class_name Character

var id: String
var name: String
var origin_id: String
var class_id: String
var level: int
var hp: int
var max_hp: int
var gold: int
var skills: Array[Skill]

static func from_json(data: Dictionary) -> Character:
    # Parse JSON to Character object
    pass
```

**Communication**:
- Created from API responses
- Stored in StateManager
- Displayed in UI scenes

## Data Flow

### Example: User Login

```
1. User enters credentials in LoginScene
   ↓
2. LoginScene calls HTTPClient.http_post("/api/v1/auth/login", {...})
   ↓
3. HTTPClient sends HTTP request to Flask backend
   ↓
4. Backend validates and returns JWT token + user data
   ↓
5. HTTPClient receives response, creates APIResponse object
   ↓
6. LoginScene receives APIResponse via callback
   ↓
7. LoginScene calls StateManager.set_user_session(user_data, token)
   ↓
8. StateManager stores session and emits user_logged_in signal
   ↓
9. StateManager saves state to local storage
   ↓
10. LoginScene navigates to CharacterListScene
```

### Example: Load Characters

```
1. CharacterListScene calls HTTPClient.http_get("/api/v1/characters")
   ↓
2. HTTPClient includes auth token in headers
   ↓
3. Backend returns array of character data
   ↓
4. CharacterListScene receives response
   ↓
5. CharacterListScene calls StateManager.set_characters(characters)
   ↓
6. StateManager emits characters_updated signal
   ↓
7. CharacterListScene updates UI with character cards
```

### Example: Character Creation Wizard

```
1. User selects origin in WizardStep1Scene
   ↓
2. Scene calls StateManager.set_wizard_origin(origin_data)
   ↓
3. Scene navigates to WizardStep2Scene
   ↓
4. User selects class in WizardStep2Scene
   ↓
5. Scene calls StateManager.set_wizard_class(class_data)
   ↓
6. User continues through steps 3 and 4
   ↓
7. WizardStep4Scene (confirmation) calls HTTPClient.http_post(
      "/api/v1/characters",
      {
        "origin_id": StateManager.get_wizard_origin().id,
        "class_id": StateManager.get_wizard_class().id,
        "name": StateManager.get_wizard_name()
      }
   )
   ↓
8. Backend creates character and returns character data
   ↓
9. Scene calls StateManager.add_character(character_data)
   ↓
10. Scene calls StateManager.reset_wizard()
   ↓
11. Scene navigates back to CharacterListScene
```

## Navigation

### Scene Management

Godot's built-in scene tree is used for navigation:

```gdscript
# Navigate to another scene
get_tree().change_scene_to_file("res://scenes/character/character_list.tscn")

# Or use PackedScene for preloading
var next_scene = preload("res://scenes/auth/login.tscn")
get_tree().change_scene_to_packed(next_scene)
```

### Navigation Flow

```
App Start
  ↓
Main Scene (checks auth)
  ├─ Authenticated? → CharacterListScene
  └─ Not authenticated? → LoginScene
      ↓
      Login successful → CharacterListScene
          ↓
          Create Character → WizardStep1Scene
              ↓
              Step 2 → WizardStep2Scene
              ↓
              Step 3 → WizardStep3Scene
              ↓
              Step 4 → WizardStep4Scene
              ↓
              Complete → CharacterListScene
          ↓
          Select Character → CharacterDetailScene
              ↓
              Start Game → GameWorldScene
                  ↓
                  Enter Combat → CombatScene
```

### Navigation Helper

Consider creating a navigation manager:

```gdscript
# scripts/services/navigation_manager.gd
extends Node

const SCENE_LOGIN = "res://scenes/auth/login.tscn"
const SCENE_CHARACTER_LIST = "res://scenes/character/character_list.tscn"
const SCENE_WIZARD_STEP_1 = "res://scenes/character/create_wizard_step1.tscn"
# ... etc

func navigate_to(scene_path: String) -> void:
    StateManager.set_current_scene(scene_path)
    get_tree().change_scene_to_file(scene_path)

func navigate_to_login() -> void:
    navigate_to(SCENE_LOGIN)

func navigate_to_character_list() -> void:
    navigate_to(SCENE_CHARACTER_LIST)

# etc.
```

## Authentication Flow

### Initial Load

```gdscript
# main.gd (attached to main.tscn, first scene loaded)
extends Control

func _ready() -> void:
    # StateManager auto-loads from local storage in its _ready()
    # Check if user is authenticated
    if StateManager.is_authenticated():
        # Validate token by making a test API call
        HTTPClient.http_get("/api/v1/auth/validate", _on_token_validated, _on_token_invalid)
    else:
        # Go to login
        NavigationManager.navigate_to_login()

func _on_token_validated(response: HTTPClient.APIResponse) -> void:
    if response.is_success():
        # Token is valid, go to character list
        NavigationManager.navigate_to_character_list()
    else:
        # Token expired, clear and go to login
        StateManager.clear_user_session()
        NavigationManager.navigate_to_login()

func _on_token_invalid(response: HTTPClient.APIResponse) -> void:
    # Network error or token invalid
    StateManager.clear_user_session()
    NavigationManager.navigate_to_login()
```

### Login

```gdscript
# scenes/auth/login.gd
extends Control

@onready var email_field: FormField = $EmailField
@onready var password_field: FormField = $PasswordField
@onready var login_button: CustomButton = $LoginButton
@onready var error_label: Label = $ErrorLabel

func _on_login_button_clicked() -> void:
    # Validate fields
    if not email_field.validate() or not password_field.validate():
        return

    # Show loading state
    login_button.set_loading(true)
    error_label.visible = false

    # Make API call
    var credentials = {
        "email": email_field.get_value(),
        "password": password_field.get_value()
    }

    HTTPClient.http_post("/api/v1/auth/login", credentials, _on_login_success, _on_login_error)

func _on_login_success(response: APIResponse) -> void:
    login_button.set_loading(false)

    if response.is_success():
        # Extract user data and token from response
        var user_data = response.result.get("user", {})
        var token = response.result.get("token", "")

        # Store in StateManager
        StateManager.set_user_session(user_data, token)

        # Navigate to character list
        NavigationManager.navigate_to_character_list()
    else:
        # Show error
        error_label.text = response.get_error_message()
        error_label.visible = true

func _on_login_error(response: APIResponse) -> void:
    login_button.set_loading(false)
    error_label.text = "Failed to connect. Please try again."
    error_label.visible = true
```

### Logout

```gdscript
func _on_logout_button_clicked() -> void:
    # Call logout endpoint (optional, for server-side cleanup)
    HTTPClient.http_post("/api/v1/auth/logout", {}, _on_logout_complete)

func _on_logout_complete(response: APIResponse) -> void:
    # Clear local session regardless of API response
    StateManager.clear_user_session()

    # Navigate to login
    NavigationManager.navigate_to_login()
```

## State Persistence

### What Gets Saved

StateManager saves to `user://coc_state.save` (platform-specific location):

- User session data
- Auth token (if "remember me")
- Character list cache
- Selected character ID
- Character limits
- Settings/preferences
- Timestamp of last save

### Save Triggers

State is saved automatically:
- On login
- On logout (clears save)
- On character list update
- On character selection
- On settings change
- On app exit (if auto-save enabled)

Manual save:
```gdscript
StateManager.save_state()
```

### Load on Startup

StateManager automatically loads on `_ready()`:
- Restores user session if valid
- Restores character list
- Sets HTTPClient auth token

## Error Handling

### API Errors

```gdscript
HTTPClient.http_get("/api/v1/characters", _on_success, _on_error)

func _on_success(response: APIResponse) -> void:
    if response.is_success():
        # Handle success
        var characters = response.result
    else:
        # API returned error (4xx, 5xx)
        _show_error(response.get_error_message())

func _on_error(response: APIResponse) -> void:
    # Network error or JSON parse error
    _show_error("Failed to connect to server")
```

### Network Errors

HTTPClient handles:
- Connection timeout (30s default)
- DNS resolution failure
- TLS/SSL errors
- No response from server
- Invalid JSON responses

All errors return an `APIResponse` with:
- `status`: HTTP status code (or 500 for network errors)
- `error.message`: Human-readable error message
- `error.code`: Machine-readable error code

### User-Facing Errors

Show errors to users:

```gdscript
# Toast notification (create a notification system)
NotificationManager.show_error("Failed to load characters")

# Inline error label
error_label.text = response.get_error_message()
error_label.visible = true

# Error dialog
var dialog = AcceptDialog.new()
dialog.dialog_text = "An error occurred. Please try again."
dialog.popup_centered()
```

## Platform Considerations

### Mobile

**Touch Input**:
- Larger tap targets (minimum 44x44 dp)
- No hover states
- Swipe gestures for navigation

**Screen Sizes**:
- Responsive layouts
- Safe areas (notches, rounded corners)
- Portrait and landscape orientations

**Performance**:
- Limit draw calls
- Use compressed textures
- Reduce particle effects

**Networking**:
- Handle intermittent connectivity
- Show loading indicators
- Cache data locally

### Desktop

**Input**:
- Keyboard shortcuts
- Mouse hover effects
- Scroll wheel support

**Window Management**:
- Resizable windows
- Fullscreen mode
- Multiple monitor support

### Web

**Limitations**:
- No threading (slower)
- No file system access (use IndexedDB)
- Larger download size
- CORS restrictions

**Requirements**:
- HTTPS for SharedArrayBuffer
- Proper MIME types
- COOP/COEP headers

## Security Considerations

### Token Storage

**Desktop/Mobile**: Tokens stored in encrypted local storage
**Web**: Tokens stored in localStorage (less secure, consider sessionStorage)

### HTTPS Only

Always use HTTPS in production for API calls.

### Token Expiration

Handle expired tokens:
```gdscript
func _on_api_error(response: HTTPClient.APIResponse) -> void:
    if response.status == 401:  # Unauthorized
        # Token expired, logout
        StateManager.clear_user_session()
        NavigationManager.navigate_to_login()
```

### Input Validation

Always validate user input:
- FormField components have built-in validation
- Re-validate on backend (never trust client)
- Sanitize before displaying (prevent XSS if user-generated content)

## Testing Strategy

### Unit Tests

Test individual components:
- HTTPClient request/response handling
- StateManager save/load
- Data model serialization
- Component validation logic

### Integration Tests

Test scene flows:
- Login → Character List
- Character Creation wizard
- Character selection → Game

### Platform Tests

Test on each platform:
- Desktop: Windows, Mac, Linux
- Mobile: Android, iOS
- Web: Chrome, Firefox, Safari

### API Mocking

For offline testing, create mock responses:

```gdscript
# scripts/services/mock_http_client.gd
extends Node

# Same API as HTTPClient but returns mock data
func get(endpoint: String, callback: Callable, error_callback: Callable = Callable()) -> void:
    match endpoint:
        "/api/v1/characters":
            callback.call(_mock_character_list_response())
        _:
            error_callback.call(_mock_error_response())

func _mock_character_list_response() -> HTTPClient.APIResponse:
    var mock_data = {
        "app": "Code of Conquest",
        "version": "0.1.0",
        "status": 200,
        "timestamp": Time.get_datetime_string_from_system(),
        "result": [
            {"id": "1", "name": "Warrior", "level": 5, "hp": 100, "max_hp": 100},
            {"id": "2", "name": "Mage", "level": 3, "hp": 60, "max_hp": 60}
        ],
        "error": {}
    }
    return HTTPClient.APIResponse.new(mock_data)
```

## Performance Optimization

### Minimize API Calls

- Cache data in StateManager
- Only refetch when necessary
- Use pagination for large lists

### Optimize Rendering

- Use object pooling for lists
- Minimize StyleBox allocations
- Use TextureRect instead of Sprite when possible
- Reduce shadow/glow effects on mobile

### Lazy Loading

Load scenes only when needed:
```gdscript
# Preload for instant transition
const CharacterList = preload("res://scenes/character/character_list.tscn")

# Or load on-demand
var scene = load("res://scenes/character/character_list.tscn")
```

## Debugging

### Enable Logging

```gdscript
# Add to HTTPClient, StateManager, etc.
print("[HTTPClient] GET /api/v1/characters")
print("[StateManager] User logged in: ", user_data.get("email"))
```

### Remote Debugging

For mobile:
1. Enable remote debug in Godot
2. Connect device via USB
3. Run with "Remote Debug" option

### Network Inspector

Monitor network traffic:
- Desktop: Use browser dev tools for Godot Web export
- Mobile: Use Charles Proxy or similar
- Check request/response bodies, headers, timing

## Next Steps (Phase 2)

After Phase 1 (Foundation) is complete:

1. **Implement Auth Screens** (Week 2)
   - Login, Register, Password Reset
   - Email verification
   - Form validation

2. **Implement Character Management** (Weeks 3-4)
   - Character creation wizard
   - Character list
   - Character detail
   - Delete character

3. **Platform Optimization** (Week 5)
   - Mobile touch controls
   - Desktop keyboard shortcuts
   - Web export configuration

4. **Future Features** (Week 6+)
   - Combat UI
   - World exploration
   - Quest system
   - Multiplayer

## Resources

- [Godot Documentation](https://docs.godotengine.org/)
- [GDScript Style Guide](https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_styleguide.html)
- [Godot UI Tutorials](https://docs.godotengine.org/en/stable/tutorials/ui/index.html)
- [HTTP Request](https://docs.godotengine.org/en/stable/classes/class_httprequest.html)
- [FileAccess](https://docs.godotengine.org/en/stable/classes/class_fileaccess.html)
