# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Theorycrafter is a multiplatform application for EVE Online ship fitting and theorycrafting, built with Kotlin Multiplatform and Jetbrains Compose. The primary platform is Desktop (Windows/macOS/Linux), with Android support in development. It provides advanced fitting simulation, tournament composition management, and comprehensive ship statistics.

## Build Commands

The project uses Gradle with Kotlin Multiplatform. The desktop application uses JVM toolchain 21. Common commands:

```bash
# Build the entire project
./gradlew build

# Run the desktop application
./gradlew :DesktopApp:run

# Run all tests
./gradlew test

# Run tests for a specific module
./gradlew :FittingEngine:test
./gradlew :EveData:test
./gradlew :DesktopApp:test

# Create distributable packages (DMG for macOS, MSI for Windows, DEB/RPM for Linux)
./gradlew :DesktopApp:packageDistributionForCurrentOS

# Create release build with ProGuard obfuscation
./gradlew :DesktopApp:packageReleaseDistributionForCurrentOS

# Package source code into zip
./gradlew :DesktopApp:packageSource

# Update EVE Online static data (downloads SDE and regenerates eve_data.dat)
./gradlew :EveData:updateEveData

# Generate EVE data from existing SDE file in misc/sde.zip
./gradlew :EveData:generateEveData
```

## Module Architecture

The project uses Gradle's composite build system with a multiplatform architecture:

### Core Modules (Composite Builds - Multiplatform)

#### EveData
- **Purpose:** Provides EVE Online static game data (ships, modules, attributes, market groups)
- **Data Source:** EVE's Static Data Export (SDE) from CCP Games
- **Key Output:** Binary `eve_data.dat` file containing all game data
- **Tech:** Kotlin Multiplatform (JVM target), Compose runtime (for state management)
- **Source:** `EveData/src/commonMain/kotlin/`
- **Entry Point:** `EveData.loadStandard()` loads the compiled data

Contains type definitions for:
- `ShipType`, `ModuleType`, `ChargeType`, `DroneType`, `ImplantType`, `BoosterType`
- `Attribute<T>` - Typed attributes (e.g., shield HP, CPU usage)
- `Effect` - Attribute modifiers (bonuses, penalties)
- `TypeGroup`, `Category`, `MarketGroup` - Item taxonomy

#### FittingEngine
- **Purpose:** Core calculation engine for ship fitting simulation
- **Responsibilities:**
  - Module fitting into slots (high/med/low/rig/subsystem)
  - Attribute calculations with effect chains
  - Damage and defense modeling
  - Remote effects between fits (remote reps, command bursts)
  - Drone groups, implants, boosters
  - Skill sets and their bonuses
  - Environment effects (abyssal weather, wormholes)
- **Design:** State-based with modification scopes for atomic updates
- **Tech:** Kotlin Multiplatform (JVM target)
- **Source:** `FittingEngine/src/commonMain/kotlin/`
- **Dependencies:** EveData, Compose runtime
- **Key Classes:** `FittingEngine`, `Fit`, `Module`, `ModificationScope`

#### ESI
- **Purpose:** EVE Swagger Interface API client
- **Auto-generated:** Kotlin code from OpenAPI specs using `openapi-generator`
- **Functionality:** Character skill imports via EVE SSO
- **Tech:** Kotlin Multiplatform (JVM target)
- **HTTP Client:** OkHttp3
- **Regeneration:** When EVE API changes, regenerate from OpenAPI spec

#### ComposeWidgets
- **Purpose:** Reusable UI component library
- **Tech:** Kotlin Multiplatform, Jetbrains Compose Multiplatform
- **Source:** `ComposeWidgets/src/commonMain/kotlin/`
- **Contains:** Custom widgets used across the application (grids, inputs, dialogs)

### Application Modules

#### SharedApp
- **Purpose:** Shared multiplatform code for both Desktop and Android
- **Tech:** Kotlin Multiplatform (JVM + Android targets)
- **Source:** `SharedApp/src/commonMain/kotlin/`
- **Current State:** Foundation for future shared UI code (currently minimal)
- **Dependencies:** All core modules (EveData, FittingEngine, ESI, ComposeWidgets)

#### DesktopApp
- **Purpose:** Desktop application implementation (Windows/macOS/Linux)
- **Tech:** Kotlin JVM with Compose Desktop (JVM toolchain 21)
- **Entry Point:** `DesktopApp/src/main/kotlin/theorycrafter/Main.kt`
- **Dependencies:** SharedApp (and transitively all core modules)
- **Key Systems:**
  - Fit management (CRUD via `FitsContext`)
  - Skill set management (`SkillSetsContext`)
  - Tournament compositions (`TournamentContext`)
  - Import/export (EFT format, XML, Pyfa database)
  - Fit optimizer (simulated annealing)
  - EVE SSO authentication
  - Release notification system
  - Analytics (Google Analytics)
  - Native packaging and distribution

#### AndroidApp
- **Purpose:** Android application implementation
- **Tech:** Kotlin Android (API 26+, target SDK 36)
- **Entry Point:** `AndroidApp/src/main/kotlin/pro/theorycrafter/android/AppActivity.kt`
- **Dependencies:** SharedApp (and transitively all core modules)
- **Current State:** Early development with basic demo UI

## Multiplatform Structure

The project has been restructured to support Kotlin Multiplatform:

### Gradle Project Layout
```
Root project/
├── settings.gradle.kts          # Defines includeBuild() for composite builds
├── build.gradle.kts             # Plugin version coordination (no code)
├── SharedApp/                   # Multiplatform module (included in root)
├── DesktopApp/                  # Desktop-only module (included in root)
├── AndroidApp/                  # Android-only module (included in root)
├── EveData/                     # Composite build (multiplatform)
├── FittingEngine/               # Composite build (multiplatform)
├── ESI/                         # Composite build (multiplatform)
├── ComposeWidgets/              # Composite build (multiplatform)
├── misc/                        # Icons, resources (used by DesktopApp)
└── DesktopApp/libs/             # Platform-specific SQLite JDBC jars
```

### Current State (Transition Phase)
- **Core modules** (EveData, FittingEngine, ESI, ComposeWidgets) are now multiplatform with `commonMain` source sets
- **DesktopApp** contains all application logic and UI (what was previously in the root project)
- **SharedApp** is a multiplatform module set up for future shared code (currently minimal)
- **AndroidApp** is a basic Android application demonstrating EveData usage

### Future Direction
As development progresses, shared UI and business logic will be gradually moved from DesktopApp to SharedApp, allowing code reuse between Desktop and Android platforms.

### Source Set Organization
- `commonMain` - Shared code for all platforms
- `jvmMain` - JVM-specific code (Desktop and Android)
- `androidMain` - Android-specific code
- `commonTest` - Shared test code
- Desktop-only code remains in DesktopApp (not multiplatform)

## Architecture Patterns

### Context Pattern
Application state is managed through singleton contexts:
- `TheorycrafterContext` - Root context holding all application state
  - `FitsContext` - Fit CRUD operations and persistence
  - `SkillSetsContext` - Skill set management
  - `TournamentContext` - Tournament composition management
  - `AutoSuggestContext` - Type-ahead search for fitting items

Access via: `TheorycrafterContext.fits`, `TheorycrafterContext.eveData`, etc.

### Handle Pattern
Stable references to mutable resources:
```kotlin
interface FitHandle {
    val fitId: Int
    val name: String
    val storedFit: StoredFit?  // Null after deletion
}
```
Handles remain valid even after the underlying fit is deleted or unloaded from the engine.

### Modification Scope Pattern
All changes to fits must go through modification scopes for atomic updates:
```kotlin
fittingEngine.modify {
    // All changes here are batched
    val module = fit.fitModule(moduleType, slotIndex = 0)
    module.setState(Module.State.ACTIVE)
    module.setCharge(chargeType)
}
```

Contexts provide convenience methods:
```kotlin
fits.modifyAndSave(fitHandle) {
    // Changes are automatically persisted
}
```

### Temporary Fitting Scope
For theoretical calculations without persistence:
```kotlin
fits.withTemporaryFit(shipType) {
    tempFit.modify {
        // Test fitting scenarios
    }
    // Automatically cleaned up
}
```

### Composition Local Pattern
Dependencies injected through Compose's `CompositionLocalProvider`:
- `LocalTheorycrafterWindowManager`
- `LocalSnackbarHost`
- `LocalIsTest` (for testing)

## Key Directories

```
DesktopApp/src/main/kotlin/theorycrafter/
├── Main.kt                  # Desktop application entry point
├── TheorycrafterContext.kt  # Root application state
├── FitsContext.kt           # Fit management
├── SkillSetsContext.kt      # Skill set management
├── ui/                      # User interface
│   ├── MainWindows.kt       # Main window content
│   ├── FitList.kt           # Fit list sidebar
│   ├── fiteditor/           # Central fit editing panel
│   ├── fitstats/            # Stats display (firepower, defense, etc.)
│   ├── graphs/              # Advanced visualizations
│   ├── tournaments/         # Tournament composition editor
│   ├── settings/            # Settings UI
│   └── widgets/             # Custom UI components
├── storage/                 # Persistence layer
├── formats/                 # Import/export (EFT, XML, Pyfa)
├── optimizer/               # Fit optimizer
├── esi/                     # EVE SSO integration
└── utils/                   # Utilities

SharedApp/src/commonMain/kotlin/theorycrafter/
└── ui/                      # Shared UI code (future)

AndroidApp/src/main/kotlin/pro/theorycrafter/android/
├── AppActivity.kt           # Android app entry point
└── Icons.kt                 # Android-specific icon rendering

FittingEngine/src/commonMain/kotlin/theorycrafter/fitting/
├── FittingEngine.kt         # Core calculation engine
├── Fit.kt                   # Ship fit representation
├── Module.kt                # Fitted module
├── DroneGroup.kt            # Drone management
├── SkillSet.kt              # Skill set with levels
├── Environment.kt           # Environmental effects
└── stats/                   # Stat calculations

EveData/src/commonMain/kotlin/eve/data/
├── EveData.kt               # Main data container
├── Types.kt                 # Ship/module/etc. types
├── Attribute.kt             # Attribute definitions
├── Effect.kt                # Effect system
└── importer/                # SDE import logic

ComposeWidgets/src/commonMain/kotlin/compose/
└── widgets/                 # Reusable Compose widgets
```

## Testing

### Unit Tests (FittingEngine)
Located in `FittingEngine/src/commonTest/kotlin/theorycrafter/fitting/`

Key helper: `FittingEngineTest` class provides DSL for creating test fixtures:
```kotlin
runFittingTest {
    // 1. Create custom EveData items
    val attr = attribute()
    val module = moduleType(slotType = ModuleSlotType.MEDIUM) {
        attributeValue(attr, 100.0)
    }
    val ship = testShipType()

    // 2. Create and modify fits
    val (fit, mod) = fit(ship, module)

    // 3. Assert results
    fit.assertPropertyEquals(attr, 100.0, "Expected value")
}
```

Run with: `./gradlew :FittingEngine:test`

### UI Tests (Desktop Application)
Located in `DesktopApp/src/test/kotlin/theorycrafter/ui/`

Base class: `TheorycrafterTest` sets up full application context
```kotlin
class MyTest : TheorycrafterTest() {
    @Test
    fun testSomething() = runBlockingTest {
        rule.setApplicationContent {
            // UI content
        }

        val fit = newFit("Raven")
        // Test UI interactions
    }
}
```

Run with: `./gradlew :DesktopApp:test`

## Data Flow

### Editing a Fit
```
User Input (UI Event)
    ↓
FitEditor.kt composable
    ↓
TheorycrafterContext.fits.modifyAndSave(fitHandle)
    ↓
FittingEngine.modify { ... } (atomic scope)
    ↓
Fit/Module changes
    ↓
StoredFit serialization
    ↓
FittingRepository.updateFit()
    ↓
Binary write to fits.dat
    ↓
FitHandle.storedFit updated
    ↓
UI recomposes (reactive)
```

### Initialization Sequence
1. **Early Init** (`DesktopApp/src/main/kotlin/theorycrafter/Main.kt`):
   - Single instance check
   - Log redirection
   - EveData loading (parallel with UI)
   - Settings loading

2. **Compose Setup**:
   - Window creation
   - Theme initialization
   - `TheorycrafterContext.initialize()`:
     - Create `FittingEngine`
     - Load fits from repository
     - Build search index
     - Initialize tournament system

3. **Warmup Phase**:
   - Create temporary fits
   - Off-screen rendering for JIT compilation
   - Cache warming

4. **Ready State**:
   - EVE SSO token refresh (background)
   - Update checks (background)
   - Analytics reporting (background)

## Storage

### Fits Storage
- **Format:** Custom binary serialization to `fits.dat`
- **Class:** `FittingRepository` handles I/O
- **Data:** `StoredFit` - serializable fit snapshot
- **Backup:** Automatic backup before loading
- **Recovery:** Corrupted files trigger recovery from backup

### Settings
- **Format:** JSON via kotlinx.serialization
- **Class:** `TheorycrafterSettings`
- **Location:** Platform-specific user data directory

### Tournament Data
- **Format:** JSON files
- **Location:** Dedicated tournaments directory

## Kotlin Compiler Settings

The project uses specific Kotlin compiler flags:
- `-Xlambdas=indy` - Efficient lambda compilation (invokedynamic)
- `-Xcontext-receivers` - Context receiver feature for DSL-style APIs

JVM toolchain versions:
- **DesktopApp:** JVM 21
- **AndroidApp:** JVM 21 (compiles to Android API 26+)
- **Core modules:** Target JVM (compatible with both targets)

## Important Implementation Notes

### SQLite JDBC
The project uses **custom platform-specific SQLite JDBC jars** (not the standard all-in-one jar) to reduce distribution size. The jars in `DesktopApp/libs/` contain only the native library for one platform.

Selection logic in `DesktopApp/build.gradle.kts`:
```kotlin
fun sqliteJdbcJarName(): String {
    // Returns sqlite-jdbc-3.49.1.0-{platform}-{arch}.jar
}
```

### Native Distribution
Platform-specific packages are created with:
- **macOS:** DMG with `.icns` icon
- **Windows:** MSI with `.ico` icon and start menu shortcut
- **Linux:** DEB/RPM with `.png` icon

Icons located in `misc/icon/{platform}/`

### Versioning
Update in `DesktopApp/build.gradle.kts`:
- `version` - Semantic version string (e.g., "1.9.0-beta01")
- `versionCode` - Integer build number
- `packageVersion` - Version for native packages (no pre-release suffix)
- `releaseType` - "stable", "beta", or "dev"

Android versioning in `AndroidApp/build.gradle.kts`:
- `versionName` - Version string for Android
- `versionCode` - Integer build number for Android

## Common Development Tasks

### Adding a New Module Slot Type
1. Update `ModuleSlotType` enum in `FittingEngine/src/commonMain/kotlin/`
2. Add ship attributes for slot count in EveData
3. Update UI in `DesktopApp/src/main/kotlin/theorycrafter/ui/fiteditor/FitEditor.kt`
4. Add tests in `FittingEngine/src/commonTest/kotlin/`

### Adding a New Stat Display
1. Create stat calculation in `FittingEngine/src/commonMain/kotlin/theorycrafter/fitting/stats/`
2. Add UI component in `DesktopApp/src/main/kotlin/theorycrafter/ui/fitstats/`
3. Wire into `FitStats.kt`

### Updating EVE Data
When CCP releases new SDE:
1. Update `sdeUrl` in `EveData/build.gradle.kts`
2. Run `./gradlew :EveData:updateEveData`
3. Test that new items load correctly
4. Commit the new `eve_data.dat`

### Import/Export Format Support
Add new format in `DesktopApp/src/main/kotlin/theorycrafter/formats/`:
- Create parser/serializer
- Add UI option in import/export dialogs in `DesktopApp/src/main/kotlin/theorycrafter/ui/`
- Add tests in `DesktopApp/src/test/kotlin/` with sample files

### Adding Multiplatform Shared UI
To move UI code from DesktopApp to SharedApp for Android reuse:
1. Move composables to `SharedApp/src/commonMain/kotlin/theorycrafter/ui/`
2. Ensure no platform-specific dependencies (file I/O, desktop-specific APIs)
3. Extract platform-specific code to expect/actual declarations if needed
4. Update imports in DesktopApp
5. Test on both Desktop and Android
