# 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
# Run the desktop application
./gradlew :SharedApp:run

# Run desktop UI tests
./gradlew :SharedApp:test

# Run FittingEngine unit tests
./gradlew :FittingEngine:test

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

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

# Package source code into zip
./gradlew 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

# Regenerate ESI API client from OpenAPI spec
./gradlew :Esi:updateEsiApi

# Build Android app
./gradlew :AndroidApp:build
```

## Module Architecture

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

### Gradle Project Layout
```
Root project/
├── settings.gradle.kts          # Defines includeBuild() for composite builds
├── build.gradle.kts             # Plugin coordination + packageSource task
├── SharedApp/                   # Main multiplatform module (Desktop + Android)
│   ├── desktopLibs/             # Platform-specific SQLite JDBC jars
│   └── proguard_rules/          # ProGuard rules for release builds
├── AndroidApp/                  # Android application (minimal, uses SharedApp)
├── EveData/                     # Composite build (multiplatform)
├── FittingEngine/               # Composite build (multiplatform)
├── Esi/                         # Composite build (JVM, auto-generated ESI client)
├── ComposeWidgets/              # Composite build (JVM, Compose UI widgets)
└── misc/                        # Icons, SDE cache, resources
```

### Core Modules (Composite Builds)

#### 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 + Android targets), Compose runtime (for state management)
- **Source:** `EveData/src/commonMain/kotlin/eve/data/`
- **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)
- `TypeGroups`, `TypeCategories`, `MarketGroups` - Item taxonomy
- `typeid/` - Extension functions for identifying module/ship types (e.g., `isMutadaptiveRemoteArmorRepairer()`)

#### 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/theorycrafter/fitting/`
- **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, JSON: Moshi
- **Regeneration:** `./gradlew :Esi:updateEsiApi` when EVE API changes

#### ComposeWidgets
- **Purpose:** Reusable UI component library
- **Tech:** Kotlin Multiplatform (JVM target), Jetbrains Compose
- **Source:** `ComposeWidgets/src/commonMain/kotlin/compose/`
- **Contains:** Custom widgets (`widgets/`) and Compose utilities (`utils/`, `input/`)

### Application Modules

#### SharedApp
- **Purpose:** The main application module — contains all shared and desktop-specific code
- **Tech:** Kotlin Multiplatform with `jvm("desktop")` and `android` targets
- **Dependencies:** EveData, FittingEngine, Esi, ComposeWidgets
- **Desktop entry point:** `SharedApp/src/desktopMain/kotlin/theorycrafter/Main.kt`
- **Desktop distribution:** Configured directly in `SharedApp/build.gradle.kts`

Source set hierarchy:
```
commonMain      → shared across all platforms
  jvmMain       → shared JVM code (ESI/SSO, I/O, network utilities)
    desktopMain → all desktop app code (UI, storage, tournament rules, etc.)
    androidMain → Android-specific code (minimal)
desktopTest     → desktop UI tests
```

#### AndroidApp
- **Purpose:** Android application shell
- **Tech:** Kotlin Android (API 26+, target SDK 36, JVM 21)
- **Entry Point:** `AndroidApp/src/main/kotlin/pro/theorycrafter/android/AppActivity.kt`
- **Dependencies:** SharedApp (and transitively all core modules)
- **Current State:** Early development; delegates almost entirely to SharedApp

## Key Directories

```
SharedApp/src/desktopMain/kotlin/theorycrafter/
├── Main.kt                  # Desktop application entry point
├── TheorycrafterContext.kt  # Root application state
├── TheorycrafterSettings.kt # Settings persistence
├── TheorycrafterWindowManager.kt
├── SkillSetsContext.kt      # Skill set management
├── Analytics.kt             # Google Analytics
├── Releases.kt              # Release notification system
├── Prices.kt                # Market prices
├── fits/
│   └── FitsContext.kt       # Fit CRUD operations
├── storage/                 # Persistence layer (FittingRepository, StoredFit, etc.)
├── formats/                 # Import/export (EFT, XML, Pyfa)
├── optimizer/               # Fit optimizer (simulated annealing)
├── tournaments/             # Tournament rules and context
│   └── impl/                # Specific tournament rule implementations
│       ├── ag/              # Anger Games variants (AngerGames6, AngerGames7, ...)
│       └── at/              # Alliance Tournament variants
├── ui/
│   ├── MainWindows.kt       # Main window content
│   ├── FitList.kt           # Fit list sidebar
│   ├── fiteditor/           # Central fit editing panel (20+ files)
│   ├── fitstats/            # Stats display (firepower, defense, etc.)
│   ├── graphs/              # Advanced visualizations
│   ├── market/              # Market browser
│   ├── tournaments/         # Tournament composition editor
│   ├── settings/            # Settings UI
│   └── widgets/             # Custom UI components
└── utils/                   # Desktop utilities (fonts, dialogs, etc.)

SharedApp/src/jvmMain/kotlin/theorycrafter/
├── esi/                     # EVE SSO integration (Esi.kt, EveSso.kt)
└── utils/                   # JVM I/O, network, OkHttp utilities

SharedApp/src/commonMain/kotlin/theorycrafter/
├── fits/                    # FitDefaults, Mutaplasmids
├── ui/                      # EveAutoSuggest, ShortNames
└── utils/                   # Platform-agnostic utilities

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

EveData/src/commonMain/kotlin/eve/data/
├── EveData.kt               # Main data container
├── ShipType.kt, ModuleType.kt, ChargeType.kt, ...  # Item type definitions
├── Attributes.kt            # Attribute definitions
├── Effects.kt               # Effect system
├── TypeGroups.kt, MarketGroups.kt, ...             # Taxonomy
└── typeid/                  # Type-identification extension functions

ComposeWidgets/src/commonMain/kotlin/compose/
├── widgets/                 # Reusable Compose widgets
├── utils/                   # Compose utilities
└── input/                   # Keyboard/mouse input handling
```

## 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
  - `EveAutoSuggest` - 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)

## 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)
Located in `SharedApp/src/desktopTest/kotlin/theorycrafter/`

Base class: `TheorycrafterTestUtils` sets up full application context.

Run with: `./gradlew :SharedApp: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** (`SharedApp/src/desktopMain/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
- `-Xexpect-actual-classes` - Allow expect/actual class declarations

JVM toolchain: 21 (SharedApp, AndroidApp, and all core modules)

## 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 `SharedApp/desktopLibs/` contain only the native library for one platform.

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

### Native Distribution
Platform-specific packages are configured in `SharedApp/build.gradle.kts`:
- **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/{mac,win,linux}/`

### Versioning
Update in `SharedApp/build.gradle.kts`:
- `version` - Semantic version string (e.g., "1.9.1-ag7")
- `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 `EveData/src/commonMain/kotlin/eve/data/Misc.kt`
2. Add ship attributes for slot count in EveData
3. Update UI in `SharedApp/src/desktopMain/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/`
2. Add UI component in `SharedApp/src/desktopMain/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`

### Adding an Import/Export Format
Add new format in `SharedApp/src/desktopMain/kotlin/theorycrafter/formats/`:
- Create parser/serializer
- Add UI option in import/export dialogs in `SharedApp/src/desktopMain/kotlin/theorycrafter/ui/`
- Add tests in `SharedApp/src/desktopTest/kotlin/`

### Adding Tournament Rules
Add a new file in `SharedApp/src/desktopMain/kotlin/theorycrafter/tournaments/impl/` and register the `TournamentDescriptor` in `TournamentContext`.

## Coding Style

- In `if` conditions with multiple clauses, wrap each clause in parentheses, unless it's a simple negation. Example: `if ((a == b) && (c == d) && !e)`
