#pragma once
#include "M5GFX.h"
#include "cjk_subset_font.h"
#include "transcript_font.h"
#include "typography.h"
#include <algorithm>
#include <atomic>

namespace wearable {
// Mixed-script native-size font. Latin is the antialiased M5GFX Montserrat
// resource; Simplified Chinese is the native CJK resource for the role;
// missing SC/Traditional glyphs retain the proven CN16/TW16 fallback.
class HybridFont final : public lgfx::IFont {
public:
    HybridFont(const lgfx::IFont& latin,const lgfx::IFont& primary)
        : latin_(latin),primary_(primary) {}
    font_type_t getType() const override {return ft_unknown;}
    void getDefaultMetric(lgfx::FontMetrics* metric) const override {
        primary_.getDefaultMetric(metric);
        lgfx::FontMetrics latin{};latin_.getDefaultMetric(&latin);
        metric->height=std::max(metric->height,latin.height);
        metric->y_advance=std::max(metric->y_advance,latin.y_advance);
        metric->baseline=std::max(metric->baseline,latin.baseline);
        metric->y_offset=-metric->baseline;
    }
    bool updateFontMetric(lgfx::FontMetrics* metric,uint16_t code) const override {
        const lgfx::IFont* selected=select(code,metric);
        if(selected) return true;
        ++missing_;
        return latin_.updateFontMetric(metric,'?');
    }
    size_t drawChar(lgfx::LGFXBase* gfx,int32_t x,int32_t y,uint16_t code,
                    const lgfx::TextStyle* style,lgfx::FontMetrics* metric,int32_t& filled_x) const override {
        lgfx::FontMetrics probe=*metric;
        const auto* selected=select(code,&probe);
        if(!selected) {code='?';selected=&latin_;latin_.updateFontMetric(&probe,code);}
        *metric=probe;
        return selected->drawChar(gfx,x,y,code,style,metric,filled_x);
    }
    uint32_t missing() const {return missing_.load();}
private:
    const lgfx::IFont* select(uint16_t code,lgfx::FontMetrics* metric) const {
        if(latin_.updateFontMetric(metric,code)) return &latin_;
        if(primary_.updateFontMetric(metric,code)) return &primary_;
        if(lgfx::fonts::efontCN_16.updateFontMetric(metric,code)) return &lgfx::fonts::efontCN_16;
        if(lgfx::fonts::efontTW_16.updateFontMetric(metric,code)) return &lgfx::fonts::efontTW_16;
        return nullptr;
    }
    const lgfx::IFont& latin_;const lgfx::IFont& primary_;
    mutable std::atomic<uint32_t> missing_{0};
};

class FontBook {
public:
    FontBook()
        : compact_(lgfx::fonts::lv_font_montserrat_16,lgfx::fonts::efontCN_16),
          normal_(lgfx::fonts::lv_font_montserrat_24,fonts::CjkSc24),
          large_(lgfx::fonts::lv_font_montserrat_28,fonts::CjkSc28) {}
    const lgfx::IFont& font(TypeRole role,TextSize body) const {
        switch(role) {
        case TypeRole::WatchTime:return fonts::WatchTime88;
        case TypeRole::WatchDate:return large_;
        case TypeRole::PageTitle:return lgfx::fonts::lv_font_montserrat_24;
        case TypeRole::StatusPrimary:return lgfx::fonts::lv_font_montserrat_24;
        case TypeRole::StatusSecondary:return lgfx::fonts::lv_font_montserrat_18;
        case TypeRole::AgentBody:return body_font(body);
        case TypeRole::AgentBodyLarge:return body==TextSize::Compact?static_cast<const lgfx::IFont&>(lgfx::fonts::lv_font_montserrat_18):body_font(body);
        case TypeRole::Meta:return compact_;
        case TypeRole::Footer:return lgfx::fonts::lv_font_montserrat_14;
        }
        return compact_;
    }
    template<class Gfx> void apply(Gfx& gfx,TypeRole role,TextSize body,uint16_t color,uint16_t background=0) const {
        gfx.setFont(&font(role,body));gfx.setTextSize(1);gfx.setTextColor(color,background);gfx.setTextDatum(0);
    }
    uint32_t missing() const {return compact_.missing()+normal_.missing()+large_.missing();}
private:
    const lgfx::IFont& body_font(TextSize size) const {
        return size==TextSize::Compact?static_cast<const lgfx::IFont&>(compact_):
            size==TextSize::Normal?static_cast<const lgfx::IFont&>(normal_):static_cast<const lgfx::IFont&>(large_);
    }
    HybridFont compact_,normal_,large_;
};
} // namespace wearable
