#!/usr/bin/env python3
"""
# Script ob-colors
## Metadata
**Kind**: #obsidian/ob-script
**Language**: #python
**Parent**:: [[Obsidian Chore Scripts]], [[Executable Markdown File]]
## Synopsis
Dump palettes saved in macOS color picker.
Example:: [[Palette - Obsidian]]
## References
- JXA Cookbook Authors. (2017). _[Using Objective C (ObjC) with JXA](https://github.com/JXA-Cookbook/JXA-Cookbook/wiki/Using-Objective-C-(ObjC)-with-JXA)_. GitHub.
- Apple Developer Authors. (2019). _[NSColorList](https://developer.apple.com/documentation/appkit/nscolorlist?language=objc)_. Apple Developer Documentation.
- [[Wikipedia Authors - Luma (Video) (Highlights)]]
## Script
```python
# """
import filecmp
import json
import os
import re
import subprocess
import tempfile
from pathlib import Path
JXA_SCRIPT = '''
ObjC.import("AppKit");
function hex(nsfloat) {
  return Math.round(ObjC.unwrap(nsfloat) * 255).toString(16).toUpperCase().padStart(2, '0');
}
function rgba(nscolor) {
  const r = hex(nscolor.redComponent);
  const g = hex(nscolor.greenComponent);
  const b = hex(nscolor.blueComponent);
  const a = hex(nscolor.alphaComponent);
  return `${r}${g}${b}${a == 'FF' ? '' : a}`;
}
let palettesJSON = {};
for (const palette of ObjC.unwrap($.NSColorList.availableColorLists)) {
  const name = ObjC.unwrap(palette.name);
  const keys = ObjC.unwrap(palette.allKeys);
  let paletteJSON = {};
  for (const key of keys) {
    const color = palette.colorWithKey(key).colorUsingColorSpace($.NSColorSpace.genericRGBColorSpace);
    paletteJSON[ObjC.unwrap(key).replace('\\x00', '')] = rgba(color);
  }
  palettesJSON[name] = paletteJSON;
}
JSON.stringify(palettesJSON, null, 2)
'''
INVALID_CHARS_RE = re.compile(r'[\[\]\'?*#:||,\\/:" ~]+')
ESCAPE_MD_RE = re.compile(r'[#]')
def safe_palette_name(name):
    return INVALID_CHARS_RE.sub(' ', name).strip()
def safe_color_name(name):
    return ESCAPE_MD_RE.sub(r'\\\g<0>', name)
def auto_color(rgba):
    r = int(rgba[0:2], 16)
    g = int(rgba[2:4], 16)
    b = int(rgba[4:6], 16)
    brightness = 0.2126 * r + 0.7152 * g + 0.0722 * b
    if len(rgba) == 8:
        brightness = brightness * int(rgba[6:8], 16) / 255
    if brightness > 144:
        return 'black'
    return 'white'
palettes = json.loads(
    subprocess.check_output(
        ['osascript', '-l', 'JavaScript', '-e', JXA_SCRIPT],
        stderr=subprocess.DEVNULL,
        encoding='utf-8'
    )
)
export_dir = Path.home() / 'Dropbox' / 'Brain' / 'robot' / 'Color Palettes'
for name, palette in palettes.items():
    name = safe_palette_name(name)
    tmp_md_fd, tmp_md_path = tempfile.mkstemp(suffix='.md', text=True)
    with os.fdopen(tmp_md_fd, 'w') as md_io:
        print(f'# Palette - {name}\n\n', file=md_io)
        print('**Kind**:: #color-palette', file=md_io)
        print('**Source**:: #from/macos-colors', file=md_io)
        print('**Generated by**:: [[ob-colors]]', file=md_io)
        print('\n| Name | Color |\n| --- | --- |', file=md_io)
        for color_name, color in palette.items():
            colorized_rgb = f'<code style="color:{auto_color(color)}; background:#{color}"> {color} </code>'
            print(
                f'| {safe_color_name(color_name)} | {colorized_rgb} |', file=md_io)
    md_file = export_dir / f'Palette - {name}.md'
    tmp_md_path = Path(tmp_md_path)
    if not md_file.exists() or not filecmp.cmp(tmp_md_path, md_file, shallow=False):
        if name != "System" or not md_file.exists():
            print('New: {}'.format(md_file.name))
            os.replace(tmp_md_path, md_file)
    else:
        # print('Same: {}'.format(md_file.name))
        tmp_md_path.unlink()
"""
# vim: ft=python
```
"""