# ARCHITECTURE

## 1. 기술 스택 (확정)
| 영역 | 선택 | 이유 |
|---|---|---|
| 런타임 | **.NET 8** | 최신 LTS, 네이티브 Windows |
| UI | **WPF + WPF-UI (lepoco)** | Fluent Design System / Mica·다크모드 / WinUI 감성. 텍스트 UI 아님 |
| 차트 | **LiveCharts2** | 애니메이션되는 아름다운 EQ 커브. (대안: ScottPlot=정적·경량) |
| MVVM | **CommunityToolkit.Mvvm** | 소스제너레이터 기반, 보일러플레이트 최소 |
| DB | **SQLite (Microsoft.Data.Sqlite)** | 로컬 프로파일 인덱스 |
| AI | **Anthropic REST (HttpClient)** 또는 `Anthropic.SDK` NuGet | 구조화 출력(tool use) |
| 패키징 | **MSIX 또는 velopack/single-file** | 배포. 라이선스 제외 옵션과 연동 |

> WPF-UI가 Mica 배경 + Fluent 컨트롤 + 다크테마를 제공해 다크·Fluent
> 룩앤필을 네이티브로 재현할 수 있다. 그래프는 LiveCharts2의 `CartesianChart`로 로그 X축(주파수)
> + 선형 Y축(dB), 영역 그라데이션 채우기.

## 2. 컴포넌트 구성
```
DansoriEQ.sln
├─ DansoriEQ.App        (WPF, WPF-UI)  ── Views/ViewModels, 그래프, 프롬프트 독
├─ DansoriEQ.Core       (class lib)
│   ├─ Profiles/         ProfileRepository, AutoEqParser, DbUpdater
│   ├─ Eq/               EqState, Filter(model), ApoRenderer, PreampCalculator
│   ├─ Ai/               ClaudeClient, EqPromptBuilder, EqDeltaSchema
│   └─ Apo/              ApoLocator, ApoConfigInstaller(승격 1회), IncludeWriter
└─ DansoriEQ.Setup      (elevated helper, 최초 1회 UAC — 아래 4장)
```

## 3. 데이터 흐름
```
프로파일 선택 → ProfileRepository.Load(id) → EqState(base)
    → ApoRenderer → IncludeWriter.Write(ai_eq.txt) → APO 라이브 리로드 → 그래프 갱신

프롬프트 입력 → EqPromptBuilder(현재 EqState + 헤드폰 메타 + 요청)
    → ClaudeClient(tool use, EqDeltaSchema) → EqDelta(JSON)
    → EqState.Apply(delta) → PreampCalculator → ApoRenderer → IncludeWriter → APO
    → AI 설명 텍스트를 응답 패널에 표시 / 히스토리 스택 push
```

## 4. APO 연동 & 저권한 전략 (중요)
문제: APO의 `config.txt`는 `C:\Program Files\EqualizerAPO\config\` 아래라 쓰기에 승격 필요.
매번 승격은 "저권한" 원칙 위반.

**채택 방식 — "1회 승격 설정, 이후 무승격":**
1. **최초 실행 설정 마법사**(DansoriEQ.Setup, UAC 1회):
   - APO 설치 경로 탐지: 레지스트리 `HKLM\SOFTWARE\EqualizerAPO` → `InstallPath`.
   - 사용자 쓰기 가능한 EQ 파일 준비. **권장:** `config` 폴더 내 `ai_eq.txt`를 만들고
     **그 파일(또는 폴더)에 현재 사용자 쓰기 ACL 부여**(icacls). 그리고 `config.txt`에
     `Include: ai_eq.txt` 한 줄 추가(중복 방지 검사).
   - 완료 플래그 저장(`%LOCALAPPDATA%\DansoriEQ\setup.json`).
2. **이후 실행**: 앱은 `ai_eq.txt`만 갱신 → **승격 불필요**. APO가 저장 즉시 리로드.

> ⚠️ **M1에서 검증할 스파이크:** APO `Include:`가 절대경로를 허용하는지 확인.
> - 허용 O → `ai_eq.txt`를 `%APPDATA%\DansoriEQ\`에 두고 절대경로 Include(ACL 불필요, 가장 깔끔).
> - 허용 X(상대경로만) → 위 4-1의 config 폴더 내 파일 + ACL 부여 방식으로.
> 두 경로 모두 "1회 승격, 이후 무승격"을 만족. 스파이크 결과로 하나 확정.

APO 필터 문법(렌더 타깃):
```
Preamp: -6.0 dB
Filter 1: ON PK  Fc 3000 Hz Gain 3.0 dB Q 1.20
Filter 2: ON LSC Fc 90 Hz   Gain 4.0 dB Q 0.70
Filter 3: ON HSC Fc 10000 Hz Gain -1.5 dB Q 0.70
```
- 타입 매핑: PK=peaking, LSC=low-shelf, HSC=high-shelf, (LP/HP 확장 가능).
- **Preamp = -(양의 게인 총합) 또는 최소 -(최대 양의 게인) − 여유 1dB.** 클리핑 방지 필수.

## 5. 프로파일 DB & 라이선스 분리
### 폴더 레이아웃
```
data/
├─ profiles/
│   ├─ open/         ← 재배포 허용(퍼미시브) 소스. 배포 빌드에 포함
│   └─ restricted/   ← 재배포 불가 소스. 배포 빌드에서 제외
├─ manifest.json     ← 소스별 {name, license, distributable, url, commit}
└─ dansorieq.db     ← SQLite 인덱스(open+restricted 모두 인덱싱)
```
### 원칙
- **수집(개발 중 1회):** AutoEQ repo를 zip/clone로 확보 → 파싱 → 소스 라이선스에 따라
  `open/` 또는 `restricted/`로 분류 저장 → SQLite에 upsert.
- **UI:** DB를 조회할 때 **라이선스 구분 없이 병합**해서 보여준다(IEM/헤드폰만 구분).
- **배포:** 패키징 시 `--exclude-restricted` 옵션 → `restricted/` 폴더와 해당 DB 행 제외.
  (개인 사용 로컬 빌드는 전체 포함.)
- DB 스키마:
```sql
CREATE TABLE headphone(
  id INTEGER PRIMARY KEY,
  name TEXT NOT NULL,
  brand TEXT,
  type TEXT CHECK(type IN ('iem','headphone')) NOT NULL,
  source TEXT,             -- 측정/소스명
  license TEXT,            -- 예: 'CC-BY', 'restricted'
  distributable INTEGER,   -- 0/1  (UI에선 필터하지 않음)
  target TEXT,             -- 예: 'Harman IE 2019'
  preamp_db REAL,
  filters_json TEXT,       -- [{type,fc,gain,q}]
  raw_text TEXT,           -- 원본 ParametricEQ.txt
  updated_at TEXT
);
CREATE INDEX ix_hp_type ON headphone(type);
CREATE INDEX ix_hp_name ON headphone(name);
```
### 앱 내 업데이트
- "DB 업데이트" → 최신 소스(zip) 재확보 → 파싱 → **upsert(변경분만)** → manifest commit/날짜 갱신.
- UI 하단에 `N profiles · 최신: YYYY-MM-DD` 표시.

## 6. AI 레이어 스키마 (구조화 출력)
`EqDelta` (Claude가 tool use로 반환):
```json
{
  "preamp_db": -6.0,
  "filters": [
    { "type": "PK",  "fc": 3000, "gain": 3.0, "q": 1.2, "enabled": true },
    { "type": "LSC", "fc": 90,   "gain": 4.0, "q": 0.7, "enabled": true }
  ],
  "explanation": "보컬 존재감을 위해 3kHz +3dB…",
  "replace": false            // false=현재 상태에 병합, true=전체 교체
}
```
시스템 프롬프트 요지:
- 역할: 파라메트릭 EQ 전문가. APO 제약(타입 PK/LSC/HSC, Fc 20–20000Hz, Gain ±12dB 권장,
  Q 0.3–6) 안에서만 출력.
- 입력으로 **현재 EqState + 헤드폰 모델/타입 + 사용자 요청** 제공 → 요청을 delta로 반영.
- 오디오 상식 힌트(보컬≈2–5kHz, 저음 단단히≈40–80Hz 무게+150–250Hz 뭉침 컷, 치찰음≈6–8kHz).
- **Preamp 안전값 계산 규칙 명시.**
- 런타임 모델: 기본 **Claude Sonnet**(요청당 2–5k 토큰), 옵션으로 Opus.

## 7. 오픈 이슈 → DEV_PLAN 참조
- APO Include 절대경로 허용 여부(스파이크).
- AutoEQ repo 구조/소스 라이선스 최신 상태 확인(수집 스크립트에서).
- 다중 출력 장치별 APO config 타깃팅(고급 옵션, MVP 이후).

## 8. EQ 프리셋 공유 포맷 (.tweq)
사용자가 만든 EQ를 다른 사용자와 공유하기 위한 파일. **JSON**, 확장자 `.tweq`.
```json
{
  "format": "dansorieq.eq",
  "version": 1,
  "app_version": "0.1.0",
  "created_utc": "2026-07-01T12:00:00Z",
  "author_note": "밝고 보컬 중심 튜닝",
  "target": {
    "name": "Truthear x Crinacle Zero",
    "brand": "Truthear",
    "type": "iem",
    "source": "AutoEQ",
    "target_curve": "Harman IE 2019"
  },
  "eq": {
    "preamp_db": -6.0,
    "filters": [
      { "type": "PK", "fc": 3000, "gain": 3.0, "q": 1.2, "enabled": true }
    ]
  },
  "history": [
    { "role": "user", "text": "보컬 가까이, 저음 단단히", "ts": "..." },
    { "role": "ai",   "text": "3kHz +3dB, 180Hz -2.5dB…",  "ts": "..." }
  ]
}
```
- **내보내기**: 현재 EqState + 대상 기기 메타 + 대화 히스토리(간략)를 직렬화해 저장.
- **가져오기**: 검증(format/version, 값 범위 클램프, 크기 상한) → **정보 패널(대상 기기 / 히스토리 / 작성자 노트) 표시** → EQ 적용.
- **라이선스 안전**: 파일엔 측정 데이터가 아니라 **기기 이름/메타만** 포함 → 재배포 문제 없음.
- **보안**: history 텍스트는 **표시 전용**(실행/링크 처리 안 함), 항목 수·길이 상한, API 키 미포함.

## 9. 설정 & Claude API 키 관리
- **별도 설정 화면**(WPF-UI NavigationView: Home/EQ ↔ Settings).
- 섹션: [AI · API 키] / [테마] / [APO] / [데이터베이스].
- **API 키**: 등록 / 삭제 / 교체, "연결 테스트", **DPAPI 암호화 저장**(`%LOCALAPPDATA%\DansoriEQ`).
- **모델 선택**: 기본 Claude Sonnet / 옵션 Opus.
- 키 미등록 시 프롬프트 독 비활성 + 등록 안내.
