# QualifierEvent_LeaderboardSort Enum

This enum defines all supported leaderboard sorting strategies for qualifier events in the TournamentAssistant Client.

Each sort type allows leaderboard data to be ranked according to specific gameplay metrics, with different sorting modes available.

## Sorting Modes

- **Default (Descending)**  
  Sorts scores from highest to lowest. This is the default for most metrics where higher numbers are better (e.g., ModifiedScore, MaxCombo).

- **Ascending**  
  Sorts scores from lowest to highest. Ideal for metrics where fewer is better (e.g., NotesMissed, BadCuts).

- **Target**  
  Sorts based on how close a score is to a predefined target value. The absolute difference is used for ranking (i.e., `|target - playerValue|`), with the smallest difference ranked highest.

---

## Enum Values

### ModifiedScore
```protobuf
ModifiedScore = 0;
````

Sorts by Modified Score, descending (highest first).

### ModifiedScoreAscending

```protobuf
ModifiedScoreAscending = 1;
```

Sorts by Modified Score, ascending (lowest first).

### ModifiedScoreTarget

```protobuf
ModifiedScoreTarget = 2;
```

Sorts by proximity to a target Modified Score.
Example with target = 107000:

```
Player A: 107114 → 114
Player B: 107310 → 310
Player C: 103851 → 3149
```

---

### NotesMissed

```protobuf
NotesMissed = 3;
```

Sorts by number of missed notes, descending.

### NotesMissedAscending

```protobuf
NotesMissedAscending = 4;
```

Sorts by number of missed notes, ascending.

### NotesMissedTarget

```protobuf
NotesMissedTarget = 5;
```

Sorts by proximity to a target missed notes value.

---

### BadCuts

```protobuf
BadCuts = 6;
```

Sorts by number of bad cuts, descending.

### BadCutsAscending

```protobuf
BadCutsAscending = 7;
```

Sorts by number of bad cuts, ascending.
Example:

```
28  
39  
43
```

### BadCutsTarget

```protobuf
BadCutsTarget = 8;
```

Sorts by proximity to a target bad cuts value.
Example with target = 40:

```
Player A: 39 → 1
Player B: 43 → 3
Player C: 28 → 12
```

---

### MaxCombo

```protobuf
MaxCombo = 9;
```

Sorts by max combo achieved, descending.

### MaxComboAscending

```protobuf
MaxComboAscending = 10;
```

Sorts by max combo, ascending.

### MaxComboTarget

```protobuf
MaxComboTarget = 11;
```

Sorts by proximity to a target max combo value.

---

### GoodCuts

```protobuf
GoodCuts = 12;
```

Sorts by number of good cuts, descending.

### GoodCutsAscending

```protobuf
GoodCutsAscending = 13;
```

Sorts by number of good cuts, ascending.

### GoodCutsTarget

```protobuf
GoodCutsTarget = 14;
```

Sorts by proximity to a target good cuts value.

---

## Summary Table

| Enum Value             | Bit Value | Sort Type  | Description                         |
| ---------------------- | --------- | ---------- | ----------------------------------- |
| ModifiedScore          | 0         | Descending | Standard leaderboard score          |
| ModifiedScoreAscending | 1         | Ascending  | Inverse score order                 |
| ModifiedScoreTarget    | 2         | Target     | Closeness to target score           |
| NotesMissed            | 3         | Descending | More missed notes = worse           |
| NotesMissedAscending   | 4         | Ascending  | Fewer missed notes = better         |
| NotesMissedTarget      | 5         | Target     | Closeness to target missed notes    |
| BadCuts                | 6         | Descending | More bad cuts = worse               |
| BadCutsAscending       | 7         | Ascending  | Fewer bad cuts = better             |
| BadCutsTarget          | 8         | Target     | Closeness to target bad cuts        |
| MaxCombo               | 9         | Descending | Highest combo achieved              |
| MaxComboAscending      | 10        | Ascending  | Lowest combo achieved               |
| MaxComboTarget         | 11        | Target     | Closeness to target combo count     |
| GoodCuts               | 12        | Descending | Most good cuts = better             |
| GoodCutsAscending      | 13        | Ascending  | Fewest good cuts = better           |
| GoodCutsTarget         | 14        | Target     | Closeness to target good cuts count |


# New DB fileserver idea:

4 columns:
- uploader (discordid)
- file_name (guid identifier)
- file (Unit8Array)
- created_at (time)

PostgreSQL:
```sql
CREATE TABLE files (
    id SERIAL PRIMARY KEY,
    uploader TEXT NOT NULL,
    file_name TEXT NOT NULL UNIQUE,
    file BYTEA NOT NULL, -- BYTEA stores binary data
    created_at TIMESTAMP DEFAULT NOW()
);
```

MySQL:
```sql
CREATE TABLE files (
    id INT AUTO_INCREMENT PRIMARY KEY,
    uploader VARCHAR(64) NOT NULL,
    file_name VARCHAR(255) NOT NULL UNIQUE,
    file LONGBLOB NOT NULL, -- LONGBLOB supports up to 4GB of binary data
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```

## Implementation example (using my beloved Drizzle ORM and a postgres database):

```ts
export const files = pgTable("files", {
  id: serial("id").primaryKey(),
  uploader: text("uploader").notNull(),
  fileName: text("file_name").notNull().unique(),
  file: blob("file").notNull(), // Drizzle handles BYTEA for pg as blob
  createdAt: timestamp("created_at").defaultNow(),
});
```

```ts
import { db } from "./db"; // drizzle db setup
import { files } from "./schema"; // import the table

const uploadFile = async (uploader: string, fileName: string, binaryData: Uint8Array) => {
  await db.insert(files).values({
    uploader,
    fileName,
    file: Buffer.from(binaryData), // Convert Uint8Array to Buffer for insertion
  });
};
```

```ts
async function getFile(fileName: string) {
  const result = await db.select().from(files).where(eq(files.fileName, fileName));
  if (!result.length) return null;

  const fileRecord = result[0];
  const uint8Array = new Uint8Array(fileRecord.file); // convert Buffer to Uint8Array
  return uint8Array;
};

app.get("/files/:fileName", async (req, res) => {
  const fileName = req.params.fileName;
  const file = await getFile(fileName);
  if (!file) return res.status(404).send("File not found");

  res.setHeader("Content-Type", "application/octet-stream");
  res.send(Buffer.from(file));
});

```