#pragma once
#include <array>
#include <cstddef>
#include <cstdint>

namespace wearable {
enum class Home : uint8_t { Watch, Agent, Monitor };
enum class TextSize : uint8_t { Compact, Normal, Large };
enum class Theme : uint8_t { Dark, Dim };
enum class VoiceSendMode : uint8_t { Confirm, Direct };
struct DevicePreferences {
    VoiceSendMode voice_send_mode = VoiceSendMode::Confirm;
    uint8_t brightness = 80, volume = 80, vibration_enabled = 1, touch_enabled = 1;
    Home home_screen = Home::Watch;
    TextSize text_size = TextSize::Normal;
    Theme theme = Theme::Dark;
    uint8_t watchface = 0; // Agent Clock only. Volume reserved, no audio effect.
    int16_t timezone_offset_minutes = 0; // Last Gateway metadata; Demo display is fixed UTC+8.
};
inline bool valid(const DevicePreferences& p) {
    return p.brightness >= 10 && p.brightness <= 100 && p.volume <= 100 &&
        p.vibration_enabled <= 1 && p.touch_enabled <= 1 && uint8_t(p.home_screen) <= 2 &&
        uint8_t(p.text_size) <= 2 && uint8_t(p.theme) <= 1 && p.watchface == 0 &&
        uint8_t(p.voice_send_mode) <= 1 && p.timezone_offset_minutes >= -720 && p.timezone_offset_minutes <= 840;
}
inline uint8_t panel_value(uint8_t percent) {
    const uint8_t clamped=percent<10?10:percent>100?100:percent;
    return (clamped * 255U + 50U) / 100U;
}
using Blob = std::array<uint8_t, 25>;
constexpr size_t MAX_BLOB_SIZE = 269; // u8 payload length + envelope
inline uint32_t crc32(const uint8_t* p, size_t n) {
    uint32_t c = 0xffffffffU;
    for (size_t i=0; i<n; ++i) {
        c ^= p[i];
        for (int j=0; j<8; ++j) c = (c >> 1) ^ (0xedb88320U & (0U-(c & 1)));
    }
    return ~c;
}
inline void put32(uint8_t* p, uint32_t v) { for (unsigned i=0;i<4;++i) p[i]=v>>(i*8); }
inline uint32_t get32(const uint8_t* p) {
    uint32_t v=0; for (unsigned i=0;i<4;++i) v |= uint32_t(p[i])<<(i*8); return v;
}
inline bool encode(const DevicePreferences& p, uint32_t generation, Blob& b) {
    if (!valid(p)) return false;
    b = {'W','S','P','F',5,11};
    put32(b.data()+6, generation);
    b[10]=p.brightness; b[11]=p.volume; b[12]=p.vibration_enabled;
    b[13]=uint8_t(p.home_screen); b[14]=uint8_t(p.text_size);
    b[15]=uint8_t(p.theme); b[16]=p.watchface;
    b[17]=uint16_t(p.timezone_offset_minutes)&0xff;
    b[18]=uint16_t(p.timezone_offset_minutes)>>8;
    b[19]=uint8_t(p.voice_send_mode);
    b[20]=p.touch_enabled;
    put32(b.data()+21, crc32(b.data(),21)); return true;
}
enum class Decode { Invalid, Current, Migrated, Future };
inline Decode decode(const uint8_t* b, size_t n, DevicePreferences& p, uint32_t& generation) {
    if (!b || n<14 || n>MAX_BLOB_SIZE || b[0]!='W' || b[1]!='S' || b[2]!='P' || b[3]!='F' ||
        n!=size_t(b[5])+14 || get32(b+n-4)!=crc32(b,n-4)) return Decode::Invalid;
    generation=get32(b+6);
    if (b[4]>5) return Decode::Future;
    if (!((b[4]==1 && b[5]==5) || (b[4]==2 && b[5]==7) || (b[4]==3 && b[5]==9) || (b[4]==4 && b[5]==10) || (b[4]==5 && b[5]==11))) return Decode::Invalid;
    DevicePreferences v;
    v.brightness=b[10];v.volume=b[11];v.vibration_enabled=b[12];
    v.home_screen=Home(b[13]);v.text_size=TextSize(b[14]);
    if(b[4]>=2) { v.theme=Theme(b[15]);v.watchface=b[16]; }
    if(b[4]>=3) v.timezone_offset_minutes=int16_t(uint16_t(b[17]) | uint16_t(b[18])<<8);
    if(b[4]>=4) v.voice_send_mode=VoiceSendMode(b[19]);
    // v1-v4 predate the touch preference; Touch defaults On.
    if(b[4]>=5) v.touch_enabled=b[20];
    if(!valid(v)) return Decode::Invalid;
    p=v;return b[4]<5?Decode::Migrated:Decode::Current;
}
struct LoadedPreferences {
    DevicePreferences value;
    uint32_t generation=0;
    int slot=-1;
    bool read_only=false, migration_needed=false;
};
inline LoadedPreferences load(const uint8_t* a,size_t an,const uint8_t* b,size_t bn) {
    LoadedPreferences out;
    const uint8_t* data[2]={a,b};size_t sizes[2]={an,bn};
    for (int i=0;i<2;++i) {
        DevicePreferences p;uint32_t g=0;auto result=decode(data[i],sizes[i],p,g);
        if(result==Decode::Future) out.read_only=true;
        if((result==Decode::Current || result==Decode::Migrated) && (out.slot<0 || g>out.generation)) {
            out.value=p;out.generation=g;out.slot=i;out.migration_needed=result==Decode::Migrated;
        }
    }
    if(out.generation==0xffffffffU) out.read_only=true;
    return out;
}
} // namespace wearable
