#include "gpssystem.hh"
#include "d578uv_codeplug.hh"
#include "config.hh"
#include "utils.hh"
#include "channel.hh"
#include "config.h"
#include "intermediaterepresentation.hh"

#include <QTimeZone>
#include <QtEndian>


/* ******************************************************************************************** *
 * Implementation of D578UVCodeplug::ChannelElement
 * ******************************************************************************************** */
D578UVCodeplug::ChannelElement::ChannelElement(uint8_t *ptr, unsigned size)
  : D878UVCodeplug::ChannelElement(ptr, size)
{
  // pass...
}

D578UVCodeplug::ChannelElement::ChannelElement(uint8_t *ptr)
  : D878UVCodeplug::ChannelElement(ptr)
{
  // pass...
}


bool
D578UVCodeplug::ChannelElement::bluetoothEnabled() const {
  return getBit(Offset::bluetooth());
}
void
D578UVCodeplug::ChannelElement::enableBluetooth(bool enable) {
  setBit(Offset::bluetooth(), enable);
}


bool
D578UVCodeplug::ChannelElement::roamingEnabled() const {
  // inverted!
  return !getBit(Offset::roaming());
}

void
D578UVCodeplug::ChannelElement::enableRoaming(bool enable) {
  // inverted!
  setBit(Offset::roaming(), !enable);
}


D578UVCodeplug::ChannelElement::InterruptPriority
D578UVCodeplug::ChannelElement::interruptPriority() const {
  return (InterruptPriority)getUInt2(Offset::interruptPriority());
}

void
D578UVCodeplug::ChannelElement::setInterruptPriority(InterruptPriority pri) {
  setUInt2(Offset::interruptPriority(), (unsigned) pri);
}


bool
D578UVCodeplug::ChannelElement::noiseReductionEnabled() const {
  return getBit(Offset::noiseReduction());
}

void
D578UVCodeplug::ChannelElement::enableNoiseReduction(bool enable) {
  setBit(Offset::noiseReduction(), enable);
}


bool
D578UVCodeplug::ChannelElement::multipleKeyEncryption() const {
  return getBit(Offset::multipleKeyEncryption());
}

void
D578UVCodeplug::ChannelElement::enableMultipleKeyEncryption(bool enable) {
  setBit(Offset::multipleKeyEncryption(), enable);
}


bool
D578UVCodeplug::ChannelElement::randomKey() const {
  return getBit(Offset::randomKey());
}

void
D578UVCodeplug::ChannelElement::enableRandomKey(bool enable) {
  setBit(Offset::randomKey(), enable);
}


bool
D578UVCodeplug::ChannelElement::sms() const {
  return !getBit(Offset::sms());
}

void
D578UVCodeplug::ChannelElement::enableSMS(bool enable) {
  setBit(Offset::sms(), !enable);
}


bool
D578UVCodeplug::ChannelElement::dataACK() const {
  // inverted!
  return !getBit(Offset::dataACK());
}

void
D578UVCodeplug::ChannelElement::enableDataACK(bool enable) {
  // inverted!
  setBit(Offset::dataACK(), !enable);
}


bool
D578UVCodeplug::ChannelElement::autoScan() const {
  return getBit(Offset::autoScan());
}

void
D578UVCodeplug::ChannelElement::enableAutoScan(bool enable) {
  setBit(Offset::autoScan(), enable);
}

bool
D578UVCodeplug::ChannelElement::sendTalkerAlias() const {
  return getBit(Offset::talkerAlias());
}

void
D578UVCodeplug::ChannelElement::enableSendTalkerAlias(bool enable) {
  setBit(Offset::talkerAlias(), enable);
}


D878UVCodeplug::ChannelElement::AdvancedEncryptionType
D578UVCodeplug::ChannelElement::advancedEncryptionType() const {
  return getBit(Offset::dmrEncryptionType()) ? AdvancedEncryptionType::ARC4 : AdvancedEncryptionType::AES;
}

void
D578UVCodeplug::ChannelElement::setEncryptionType(AdvancedEncryptionType type) {
  setBit(Offset::dmrEncryptionType(), AdvancedEncryptionType::ARC4 == type);
}


bool
D578UVCodeplug::ChannelElement::analogScamblerEnabled() const {
  return FMScramblerFrequency::Off != (FMScramblerFrequency)getUInt8(Offset::fmScrambler());
}

Frequency
D578UVCodeplug::ChannelElement::analogScramblerFrequency() const {
  switch ((FMScramblerFrequency)getUInt8(Offset::fmScrambler())) {
  case FMScramblerFrequency::Off: return Frequency();
  case FMScramblerFrequency::Hz3300: return Frequency::fromHz(3300);
  case FMScramblerFrequency::Hz3200: return Frequency::fromHz(3200);
  case FMScramblerFrequency::Hz3100: return Frequency::fromHz(3100);
  case FMScramblerFrequency::Hz3000: return Frequency::fromHz(3000);
  case FMScramblerFrequency::Hz2900: return Frequency::fromHz(2900);
  case FMScramblerFrequency::Hz2800: return Frequency::fromHz(2800);
  case FMScramblerFrequency::Hz2700: return Frequency::fromHz(2700);
  case FMScramblerFrequency::Hz2600: return Frequency::fromHz(2600);
  case FMScramblerFrequency::Hz2500: return Frequency::fromHz(2500);
  case FMScramblerFrequency::Hz4095: return Frequency::fromHz(4095);
  case FMScramblerFrequency::Hz3458: return Frequency::fromHz(3458);
  case FMScramblerFrequency::Custom:
    return Frequency::fromHz(100*getUInt8(Offset::customScrambler())+1500);
  }
  return Frequency();
}

void
D578UVCodeplug::ChannelElement::setAnalogScamberFrequency(Frequency f) {
  if (4095 == f.inHz()) {
    setUInt8(Offset::fmScrambler(), (unsigned)FMScramblerFrequency::Hz4095);
    setUInt8(Offset::customScrambler(), 0);
  } else if (3458 == f.inHz()) {
    setUInt8(Offset::fmScrambler(), (unsigned)FMScramblerFrequency::Hz3458);
    setUInt8(Offset::customScrambler(), 0);
  } else if ((f.inHz() >= 1500) && (f.inHz() <= 4100)) {
    setUInt8(Offset::fmScrambler(), (unsigned)FMScramblerFrequency::Custom);
    setUInt8(Offset::customScrambler(), (f.inHz()-1500)/100);
  } else {
    clearAnalogScambler();
  }
}

void
D578UVCodeplug::ChannelElement::clearAnalogScambler() {
  setUInt8(Offset::fmScrambler(), (unsigned)FMScramblerFrequency::Off);
  setUInt8(Offset::customScrambler(), 0);
}


unsigned int
D578UVCodeplug::ChannelElement::fmAPRSFrequencyIndex() const {
  return getUInt8(Offset::fmAPRSFrequencyIndex());
}

void
D578UVCodeplug::ChannelElement::setFMAPRSFrequencyIndex(unsigned int idx) {
  setUInt8(Offset::fmAPRSFrequencyIndex(), std::min(7U, idx));
}


bool
D578UVCodeplug::ChannelElement::hasARC4EncryptionKeyIndex() const {
  return 0 != getUInt8(Offset::arc4KeyIndex());
}

unsigned
D578UVCodeplug::ChannelElement::arc4EncryptionKeyIndex() const {
  return getUInt8(Offset::arc4KeyIndex()) - 1;
}

void
D578UVCodeplug::ChannelElement::setARC4EncryptionKeyIndex(unsigned idx) {
  setUInt8(Offset::arc4KeyIndex(), idx+1);
}

void
D578UVCodeplug::ChannelElement::clearARC4EncryptionKeyIndex() {
  setUInt8(Offset::arc4KeyIndex(), 0);
}


Channel *
D578UVCodeplug::ChannelElement::toChannelObj(Context &ctx) const {
  Channel *ch = D878UVCodeplug::ChannelElement::toChannelObj(ctx);
  if (nullptr == ch)
    return nullptr;

  // Apply extensions
  if (FMChannel *fch = ch->as<FMChannel>()) {
    if (AnytoneFMChannelExtension *ext = fch->anytoneChannelExtension()) {
      // Common settings
      ext->enableHandsFree(bluetoothEnabled());
      // FM specific settings
      ext->setScramblerFrequency(analogScamblerEnabled() ? analogScramblerFrequency() : Frequency());
    }
  } else if (DMRChannel *dch = ch->as<DMRChannel>()) {
    if (AnytoneDMRChannelExtension *ext = dch->anytoneChannelExtension()) {
      // Common settings
      ext->enableHandsFree(bluetoothEnabled());
      // DMR specific extensions
    }
  }

  // Done.
  return ch;
}


bool
D578UVCodeplug::ChannelElement::fromChannelObj(const Channel *ch, Context &ctx) {
  if (! D878UVCodeplug::ChannelElement::fromChannelObj(ch, ctx))
    return false;

  // Apply extensions
  if (const FMChannel *fch = ch->as<FMChannel>()) {
    if (AnytoneFMChannelExtension *ext = fch->anytoneChannelExtension()) {
      // Common settings
      enableBluetooth(ext->handsFree());
      // FM specific settings
      if (ext->scramblerFrequency().isZero())
        clearAnalogScambler();
      else
        setAnalogScamberFrequency(ext->scramblerFrequency());
    }
  } else if (const DMRChannel *dch = ch->as<DMRChannel>()) {
    if (AnytoneDMRChannelExtension *ext = dch->anytoneChannelExtension()) {
      // Common settings
      enableBluetooth(ext->handsFree());
      // DMR specific extensions
    }
  }

  return true;
}



/* ******************************************************************************************** *
 * Implementation of D578UVCodeplug::ChannelExtensionElement
 * ******************************************************************************************** */
D578UVCodeplug::ChannelExtensionElement::ChannelExtensionElement(uint8_t *data, unsigned size)
  : Element(data, size)
{
  // pass...
}

D578UVCodeplug::ChannelExtensionElement::ChannelExtensionElement(uint8_t *data)
  : Element(data, size())
{
  // pass...
}


void
D578UVCodeplug::ChannelExtensionElement::clear() {
  Element::clear();
  std::memset(_data, 0, size());
}


unsigned int
D578UVCodeplug::ChannelExtensionElement::fiveToneIdIndexBOT() const {
  return getUInt8(Offset::fiveToneIdIndexBOT());
}

void
D578UVCodeplug::ChannelExtensionElement::setFiveToneIdIndexBOT(unsigned int idx) {
  setUInt8(Offset::fiveToneIdIndexBOT(), idx);
}


unsigned int
D578UVCodeplug::ChannelExtensionElement::fiveToneIdIndexEOT() const {
  return getUInt8(Offset::fiveToneIdIndexEOT());
}

void
D578UVCodeplug::ChannelExtensionElement::setFiveToneIdIndexEOT(unsigned int idx) {
  setUInt8(Offset::fiveToneIdIndexEOT(), idx);
}



/* ******************************************************************************************** *
 * Implementation of D578UVCodeplug::GeneralSettingsElement::KeyFunction
 * ******************************************************************************************** */
uint8_t
D578UVCodeplug::KeyFunction::encode(AnytoneKeySettingsExtension::KeyFunction func) {
  switch (func) {
  case AnytoneKeySettingsExtension::KeyFunction::Off:               return (uint8_t)KeyFunction::Off;
  case AnytoneKeySettingsExtension::KeyFunction::Voltage:           return (uint8_t)KeyFunction::Voltage;
  case AnytoneKeySettingsExtension::KeyFunction::Power:             return (uint8_t)KeyFunction::Power;
  case AnytoneKeySettingsExtension::KeyFunction::Repeater:          return (uint8_t)KeyFunction::Repeater;
  case AnytoneKeySettingsExtension::KeyFunction::Reverse:           return (uint8_t)KeyFunction::Reverse;
  case AnytoneKeySettingsExtension::KeyFunction::Encryption:        return (uint8_t)KeyFunction::Encryption;
  case AnytoneKeySettingsExtension::KeyFunction::Call:              return (uint8_t)KeyFunction::Call;
  case AnytoneKeySettingsExtension::KeyFunction::ToggleVFO:         return (uint8_t)KeyFunction::ToggleVFO;
  case AnytoneKeySettingsExtension::KeyFunction::Scan:              return (uint8_t)KeyFunction::Scan;
  case AnytoneKeySettingsExtension::KeyFunction::WFM:               return (uint8_t)KeyFunction::WFM;
  case AnytoneKeySettingsExtension::KeyFunction::Alarm:             return (uint8_t)KeyFunction::Alarm;
  case AnytoneKeySettingsExtension::KeyFunction::RecordSwitch:      return (uint8_t)KeyFunction::RecordSwitch;
  case AnytoneKeySettingsExtension::KeyFunction::Record:            return (uint8_t)KeyFunction::Record;
  case AnytoneKeySettingsExtension::KeyFunction::SMS:               return (uint8_t)KeyFunction::SMS;
  case AnytoneKeySettingsExtension::KeyFunction::Dial:              return (uint8_t)KeyFunction::Dial;
  case AnytoneKeySettingsExtension::KeyFunction::GPSInformation:    return (uint8_t)KeyFunction::GPSInformation;
  case AnytoneKeySettingsExtension::KeyFunction::Monitor:           return (uint8_t)KeyFunction::Monitor;
  case AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel: return (uint8_t)KeyFunction::ToggleMainChannel;
  case AnytoneKeySettingsExtension::KeyFunction::HotKey1:           return (uint8_t)KeyFunction::HotKey1;
  case AnytoneKeySettingsExtension::KeyFunction::HotKey2:           return (uint8_t)KeyFunction::HotKey2;
  case AnytoneKeySettingsExtension::KeyFunction::HotKey3:           return (uint8_t)KeyFunction::HotKey3;
  case AnytoneKeySettingsExtension::KeyFunction::HotKey4:           return (uint8_t)KeyFunction::HotKey4;
  case AnytoneKeySettingsExtension::KeyFunction::HotKey5:           return (uint8_t)KeyFunction::HotKey5;
  case AnytoneKeySettingsExtension::KeyFunction::HotKey6:           return (uint8_t)KeyFunction::HotKey6;
  case AnytoneKeySettingsExtension::KeyFunction::WorkAlone:         return (uint8_t)KeyFunction::WorkAlone;
  case AnytoneKeySettingsExtension::KeyFunction::SkipChannel:       return (uint8_t)KeyFunction::SkipChannel;
  case AnytoneKeySettingsExtension::KeyFunction::DMRMonitor:        return (uint8_t)KeyFunction::DMRMonitor;
  case AnytoneKeySettingsExtension::KeyFunction::SubChannel:        return (uint8_t)KeyFunction::SubChannel;
  case AnytoneKeySettingsExtension::KeyFunction::PriorityZone:      return (uint8_t)KeyFunction::PriorityZone;
  case AnytoneKeySettingsExtension::KeyFunction::VFOScan:           return (uint8_t)KeyFunction::VFOScan;
  case AnytoneKeySettingsExtension::KeyFunction::MICSoundQuality:   return (uint8_t)KeyFunction::MICSoundQuality;
  case AnytoneKeySettingsExtension::KeyFunction::LastCallReply:     return (uint8_t)KeyFunction::LastCallReply;
  case AnytoneKeySettingsExtension::KeyFunction::ChannelType:       return (uint8_t)KeyFunction::ChannelType;
  case AnytoneKeySettingsExtension::KeyFunction::Ranging:           return (uint8_t)KeyFunction::Ranging;
  case AnytoneKeySettingsExtension::KeyFunction::Roaming:           return (uint8_t)KeyFunction::Roaming;
  case AnytoneKeySettingsExtension::KeyFunction::ChannelRanging:    return (uint8_t)KeyFunction::ChannelRanging;
  case AnytoneKeySettingsExtension::KeyFunction::MaxVolume:         return (uint8_t)KeyFunction::MaxVolume;
  case AnytoneKeySettingsExtension::KeyFunction::Slot:              return (uint8_t)KeyFunction::Slot;
  case AnytoneKeySettingsExtension::KeyFunction::APRSTypeSwitch:    return (uint8_t)KeyFunction::APRSTypeSwitch;
  case AnytoneKeySettingsExtension::KeyFunction::Zone:              return (uint8_t)KeyFunction::Zone;
  case AnytoneKeySettingsExtension::KeyFunction::MuteA:             return (uint8_t)KeyFunction::MuteA;
  case AnytoneKeySettingsExtension::KeyFunction::MuteB:             return (uint8_t)KeyFunction::MuteB;
  case AnytoneKeySettingsExtension::KeyFunction::RoamingSet:        return (uint8_t)KeyFunction::RoamingSet;
  case AnytoneKeySettingsExtension::KeyFunction::APRSSet:           return (uint8_t)KeyFunction::APRSSet;
  case AnytoneKeySettingsExtension::KeyFunction::ZoneUp:            return (uint8_t)KeyFunction::ZoneUp;
  case AnytoneKeySettingsExtension::KeyFunction::ZoneDown:          return (uint8_t)KeyFunction::ZoneDown;
  case AnytoneKeySettingsExtension::KeyFunction::Exit:              return (uint8_t)KeyFunction::Exit;
  case AnytoneKeySettingsExtension::KeyFunction::Menu:              return (uint8_t)KeyFunction::Menu;
  case AnytoneKeySettingsExtension::KeyFunction::XBandRepeater:     return (uint8_t)KeyFunction::XBandRepeater;
  case AnytoneKeySettingsExtension::KeyFunction::Speaker:           return (uint8_t)KeyFunction::Speaker;
  case AnytoneKeySettingsExtension::KeyFunction::ChannelName:       return (uint8_t)KeyFunction::ChannelName;
  case AnytoneKeySettingsExtension::KeyFunction::Bluetooth:         return (uint8_t)KeyFunction::Bluetooth;
  case AnytoneKeySettingsExtension::KeyFunction::GPS:               return (uint8_t)KeyFunction::GPS;
  case AnytoneKeySettingsExtension::KeyFunction::CDTScan:           return (uint8_t)KeyFunction::CDTScan;
  case AnytoneKeySettingsExtension::KeyFunction::TBSTSend:          return (uint8_t)KeyFunction::TBSTSend;
  case AnytoneKeySettingsExtension::KeyFunction::APRSSend:          return (uint8_t)KeyFunction::APRSSend;
  case AnytoneKeySettingsExtension::KeyFunction::APRSInfo:          return (uint8_t)KeyFunction::APRSInfo;
  case AnytoneKeySettingsExtension::KeyFunction::GPSRoaming:        return (uint8_t)KeyFunction::GPSRoaming;
  case AnytoneKeySettingsExtension::KeyFunction::Squelch:           return (uint8_t)KeyFunction::Squelch;
  case AnytoneKeySettingsExtension::KeyFunction::NoiseReductionTX:  return (uint8_t)KeyFunction::NoiseReductionTX;
  default:                                                          return (uint8_t)KeyFunction::Off;
  }
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::KeyFunction::decode(uint8_t code) {
  switch ((KeyFunctionCode)code) {
  case KeyFunction::Off:               return AnytoneKeySettingsExtension::KeyFunction::Off;
  case KeyFunction::Voltage:           return AnytoneKeySettingsExtension::KeyFunction::Voltage;
  case KeyFunction::Power:             return AnytoneKeySettingsExtension::KeyFunction::Power;
  case KeyFunction::Repeater:          return AnytoneKeySettingsExtension::KeyFunction::Repeater;
  case KeyFunction::Reverse:           return AnytoneKeySettingsExtension::KeyFunction::Reverse;
  case KeyFunction::Encryption:        return AnytoneKeySettingsExtension::KeyFunction::Encryption;
  case KeyFunction::Call:              return AnytoneKeySettingsExtension::KeyFunction::Call;
  case KeyFunction::ToggleVFO:         return AnytoneKeySettingsExtension::KeyFunction::ToggleVFO;
  case KeyFunction::Scan:              return AnytoneKeySettingsExtension::KeyFunction::Scan;
  case KeyFunction::WFM:               return AnytoneKeySettingsExtension::KeyFunction::WFM;
  case KeyFunction::Alarm:             return AnytoneKeySettingsExtension::KeyFunction::Alarm;
  case KeyFunction::RecordSwitch:      return AnytoneKeySettingsExtension::KeyFunction::RecordSwitch;
  case KeyFunction::Record:            return AnytoneKeySettingsExtension::KeyFunction::Record;
  case KeyFunction::SMS:               return AnytoneKeySettingsExtension::KeyFunction::SMS;
  case KeyFunction::Dial:              return AnytoneKeySettingsExtension::KeyFunction::Dial;
  case KeyFunction::GPSInformation:    return AnytoneKeySettingsExtension::KeyFunction::GPSInformation;
  case KeyFunction::Monitor:           return AnytoneKeySettingsExtension::KeyFunction::Monitor;
  case KeyFunction::ToggleMainChannel: return AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel;
  case KeyFunction::HotKey1:           return AnytoneKeySettingsExtension::KeyFunction::HotKey1;
  case KeyFunction::HotKey2:           return AnytoneKeySettingsExtension::KeyFunction::HotKey2;
  case KeyFunction::HotKey3:           return AnytoneKeySettingsExtension::KeyFunction::HotKey3;
  case KeyFunction::HotKey4:           return AnytoneKeySettingsExtension::KeyFunction::HotKey4;
  case KeyFunction::HotKey5:           return AnytoneKeySettingsExtension::KeyFunction::HotKey5;
  case KeyFunction::HotKey6:           return AnytoneKeySettingsExtension::KeyFunction::HotKey6;
  case KeyFunction::WorkAlone:         return AnytoneKeySettingsExtension::KeyFunction::WorkAlone;
  case KeyFunction::SkipChannel:       return AnytoneKeySettingsExtension::KeyFunction::SkipChannel;
  case KeyFunction::DMRMonitor:        return AnytoneKeySettingsExtension::KeyFunction::DMRMonitor;
  case KeyFunction::SubChannel:        return AnytoneKeySettingsExtension::KeyFunction::SubChannel;
  case KeyFunction::PriorityZone:      return AnytoneKeySettingsExtension::KeyFunction::PriorityZone;
  case KeyFunction::VFOScan:           return AnytoneKeySettingsExtension::KeyFunction::VFOScan;
  case KeyFunction::MICSoundQuality:   return AnytoneKeySettingsExtension::KeyFunction::MICSoundQuality;
  case KeyFunction::LastCallReply:     return AnytoneKeySettingsExtension::KeyFunction::LastCallReply;
  case KeyFunction::ChannelType:       return AnytoneKeySettingsExtension::KeyFunction::ChannelType;
  case KeyFunction::Ranging:           return AnytoneKeySettingsExtension::KeyFunction::Ranging;
  case KeyFunction::Roaming:           return AnytoneKeySettingsExtension::KeyFunction::Roaming;
  case KeyFunction::ChannelRanging:    return AnytoneKeySettingsExtension::KeyFunction::ChannelRanging;
  case KeyFunction::MaxVolume:         return AnytoneKeySettingsExtension::KeyFunction::MaxVolume;
  case KeyFunction::Slot:              return AnytoneKeySettingsExtension::KeyFunction::Slot;
  case KeyFunction::APRSTypeSwitch:    return AnytoneKeySettingsExtension::KeyFunction::APRSTypeSwitch;
  case KeyFunction::Zone:              return AnytoneKeySettingsExtension::KeyFunction::Zone;
  case KeyFunction::MuteA:             return AnytoneKeySettingsExtension::KeyFunction::MuteA;
  case KeyFunction::MuteB:             return AnytoneKeySettingsExtension::KeyFunction::MuteB;
  case KeyFunction::RoamingSet:        return AnytoneKeySettingsExtension::KeyFunction::RoamingSet;
  case KeyFunction::APRSSet:           return AnytoneKeySettingsExtension::KeyFunction::APRSSet;
  case KeyFunction::ZoneUp:            return AnytoneKeySettingsExtension::KeyFunction::ZoneUp;
  case KeyFunction::ZoneDown:          return AnytoneKeySettingsExtension::KeyFunction::ZoneDown;
  case KeyFunction::Exit:              return AnytoneKeySettingsExtension::KeyFunction::Exit;
  case KeyFunction::Menu:              return AnytoneKeySettingsExtension::KeyFunction::Menu;
  case KeyFunction::XBandRepeater:     return AnytoneKeySettingsExtension::KeyFunction::XBandRepeater;
  case KeyFunction::Speaker:           return AnytoneKeySettingsExtension::KeyFunction::Speaker;
  case KeyFunction::ChannelName:       return AnytoneKeySettingsExtension::KeyFunction::ChannelName;
  case KeyFunction::Bluetooth:         return AnytoneKeySettingsExtension::KeyFunction::Bluetooth;
  case KeyFunction::GPS:               return AnytoneKeySettingsExtension::KeyFunction::GPS;
  case KeyFunction::CDTScan:           return AnytoneKeySettingsExtension::KeyFunction::CDTScan;
  case KeyFunction::TBSTSend:          return AnytoneKeySettingsExtension::KeyFunction::TBSTSend;
  case KeyFunction::APRSSend:          return AnytoneKeySettingsExtension::KeyFunction::APRSSend;
  case KeyFunction::APRSInfo:          return AnytoneKeySettingsExtension::KeyFunction::APRSInfo;
  case KeyFunction::GPSRoaming:        return AnytoneKeySettingsExtension::KeyFunction::GPSRoaming;
  case KeyFunction::Squelch:           return AnytoneKeySettingsExtension::KeyFunction::Squelch;
  case KeyFunction::NoiseReductionTX:  return AnytoneKeySettingsExtension::KeyFunction::NoiseReductionTX;
  default:                             return AnytoneKeySettingsExtension::KeyFunction::Off;
  }
}


/* ******************************************************************************************** *
 * Implementation of D578UVCodeplug::GeneralSettingsElement::TimeZone
 * ******************************************************************************************** */
QVector<QTimeZone>
D578UVCodeplug::GeneralSettingsElement::TimeZone::_timeZones = {
  QTimeZone(-43200), QTimeZone(-39600), QTimeZone(-36000), QTimeZone(-32400), QTimeZone(-28800),
  QTimeZone(-25200), QTimeZone(-21600), QTimeZone(-18000), QTimeZone(-14400), QTimeZone(-12600),
  QTimeZone(-10800), QTimeZone(- 7200), QTimeZone(- 3600), QTimeZone(     0), QTimeZone(  3600),
  QTimeZone(  7200), QTimeZone( 10800), QTimeZone( 12600), QTimeZone( 14400), QTimeZone( 16200),
  QTimeZone( 18000), QTimeZone( 19800), QTimeZone( 20700), QTimeZone( 21600), QTimeZone( 25200),
  QTimeZone( 28800), QTimeZone( 30600), QTimeZone( 32400), QTimeZone( 36000), QTimeZone( 39600),
  QTimeZone( 43200), QTimeZone( 46800) };

QTimeZone
D578UVCodeplug::GeneralSettingsElement::TimeZone::decode(uint8_t code) {
  if (code >= _timeZones.size())
    return _timeZones.back();
  return _timeZones.at(code);
}
uint8_t
D578UVCodeplug::GeneralSettingsElement::TimeZone::encode(const QTimeZone &zone) {
  if (! _timeZones.contains(zone))
    return 13; //<- UTC
  return _timeZones.indexOf(zone);
}


/* ******************************************************************************************** *
 * Implementation of D578UVCodeplug::GeneralSettingsElement
 * ******************************************************************************************** */
D578UVCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr, unsigned size)
  : AnytoneCodeplug::GeneralSettingsElement(ptr, size)
{
  // pass...
}

D578UVCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr)
  : AnytoneCodeplug::GeneralSettingsElement(ptr, GeneralSettingsElement::size())
{
  // pass...
}

bool
D578UVCodeplug::GeneralSettingsElement::keyToneEnabled() const {
  return 0x00 != getUInt8(Offset::enableKeyTone());
}
void
D578UVCodeplug::GeneralSettingsElement::enableKeyTone(bool enable) {
  setUInt8(Offset::enableKeyTone(), enable ? 0x01 : 0x00);
}

Interval
D578UVCodeplug::GeneralSettingsElement::transmitTimeout() const {
  return Interval::fromSeconds((unsigned)getUInt8(Offset::transmitTimeout())*30);
}
void
D578UVCodeplug::GeneralSettingsElement::setTransmitTimeout(const Interval &tot) {
  setUInt8(Offset::transmitTimeout(), tot.seconds()/30);
}

AnytoneDisplaySettingsExtension::Language
D578UVCodeplug::GeneralSettingsElement::language() const {
  return (AnytoneDisplaySettingsExtension::Language)getUInt8(Offset::language());
}
void
D578UVCodeplug::GeneralSettingsElement::setLanguage(AnytoneDisplaySettingsExtension::Language lang) {
  setUInt8(Offset::language(), (unsigned)lang);
}


Frequency
D578UVCodeplug::GeneralSettingsElement::vfoStepSize() const {
  switch ((VFOStepSize)getUInt8(Offset::vfoStepSize())) {
  case VFOStepSize::Hz2500:  return Frequency::fromHz(2500);
  case VFOStepSize::Hz5000:  return Frequency::fromHz(5000);
  case VFOStepSize::Hz6250:  return Frequency::fromHz(6250);
  case VFOStepSize::Hz8330:  return Frequency::fromHz(8330);
  case VFOStepSize::kHz10:   return Frequency::fromkHz(10);
  case VFOStepSize::Hz12500: return Frequency::fromHz(12500);
  case VFOStepSize::kHz20:   return Frequency::fromkHz(20);
  case VFOStepSize::kHz25:   return Frequency::fromkHz(25);
  case VFOStepSize::kHz30:   return Frequency::fromkHz(30);
  case VFOStepSize::kHz50:   return Frequency::fromkHz(50);
  }

  return Frequency::fromHz(2500);
}

void
D578UVCodeplug::GeneralSettingsElement::setVFOStepSize(const Frequency &f) {
  VFOStepSize stepSize = VFOStepSize::Hz2500;
  if (f.inHz() <= 2500)       stepSize = VFOStepSize::Hz2500;
  else if (f.inHz() <= 5000)  stepSize = VFOStepSize::Hz5000;
  else if (f.inHz() <= 6250)  stepSize = VFOStepSize::Hz6250;
  else if (f.inHz() <= 8330)  stepSize = VFOStepSize::Hz8330;
  else if (f.inkHz() <= 10)   stepSize = VFOStepSize::kHz10;
  else if (f.inHz() <= 12500) stepSize = VFOStepSize::Hz12500;
  else if (f.inkHz() <= 20)   stepSize = VFOStepSize::kHz20;
  else if (f.inkHz() <= 25)   stepSize = VFOStepSize::kHz25;
  else if (f.inkHz() <= 30)   stepSize = VFOStepSize::kHz30;
  else if (f.inkHz() <= 50)   stepSize = VFOStepSize::kHz50;
  setUInt8(Offset::vfoStepSize(), (unsigned) stepSize);
}


AnytoneSettingsExtension::VFOScanType
D578UVCodeplug::GeneralSettingsElement::vfoScanType() const {
  return (AnytoneSettingsExtension::VFOScanType)getUInt8(Offset::vfoScanType());
}
void
D578UVCodeplug::GeneralSettingsElement::setVFOScanType(AnytoneSettingsExtension::VFOScanType type) {
  setUInt8(Offset::vfoScanType(), (unsigned int) type);
}


Level
D578UVCodeplug::GeneralSettingsElement::dmrMicGain() const {
  return Level::fromValue(getUInt8(Offset::dmrMicGain()), Limit::micGain());
}

void
D578UVCodeplug::GeneralSettingsElement::setDMRMicGain(Level gain) {
  setUInt8(Offset::dmrMicGain(), gain.mapTo(Limit::micGain()));
}


bool
D578UVCodeplug::GeneralSettingsElement::vfoModeA() const {
  return getUInt8(Offset::vfoModeA());
}
void
D578UVCodeplug::GeneralSettingsElement::enableVFOModeA(bool enable) {
  setUInt8(Offset::vfoModeA(), (enable ? 0x01 : 0x00));
}

bool
D578UVCodeplug::GeneralSettingsElement::vfoModeB() const {
  return getUInt8(Offset::vfoModeB());
}
void
D578UVCodeplug::GeneralSettingsElement::enableVFOModeB(bool enable) {
  setUInt8(Offset::vfoModeB(), (enable ? 0x01 : 0x00));
}

AnytoneSettingsExtension::STEType
D578UVCodeplug::GeneralSettingsElement::steType() const {
  return (AnytoneSettingsExtension::STEType)getUInt8(Offset::steType());
}
void
D578UVCodeplug::GeneralSettingsElement::setSTEType(AnytoneSettingsExtension::STEType type) {
  setUInt8(Offset::steType(), (unsigned)type);
}
double
D578UVCodeplug::GeneralSettingsElement::steFrequency() const {
  switch ((STEFrequency)getUInt8(Offset::steFrequency())) {
  case STEFrequency::Off: return 0;
  case STEFrequency::Hz55_2: return 55.2;
  case STEFrequency::Hz259_2: return 259.2;
  }
  return 0;
}
void
D578UVCodeplug::GeneralSettingsElement::setSTEFrequency(double freq) {
  if (0 >= freq) {
    setUInt8(Offset::steFrequency(), (unsigned)STEFrequency::Off);
  } else if (100 > freq) {
    setUInt8(Offset::steFrequency(), (unsigned)STEFrequency::Hz55_2);
  } else {
    setUInt8(Offset::steFrequency(), (unsigned)STEFrequency::Hz259_2);
  }
}

Interval
D578UVCodeplug::GeneralSettingsElement::groupCallHangTime() const {
  return Interval::fromSeconds(getUInt8(Offset::groupCallHangTime()));
}
void
D578UVCodeplug::GeneralSettingsElement::setGroupCallHangTime(Interval intv) {
  setUInt8(Offset::groupCallHangTime(), intv.seconds());
}

Interval
D578UVCodeplug::GeneralSettingsElement::privateCallHangTime() const {
  return Interval::fromSeconds(getUInt8(Offset::privateCallHangTime()));
}
void
D578UVCodeplug::GeneralSettingsElement::setPrivateCallHangTime(Interval intv) {
  setUInt8(Offset::privateCallHangTime(), intv.seconds());
}

Interval
D578UVCodeplug::GeneralSettingsElement::wakeHeadPeriod() const {
  return Interval::fromMilliseconds(((unsigned)getUInt8(Offset::wakeHeadPeriod()))*20);
}
void
D578UVCodeplug::GeneralSettingsElement::setWakeHeadPeriod(Interval intv) {
  setUInt8(Offset::wakeHeadPeriod(), intv.milliseconds()/20);
}

unsigned
D578UVCodeplug::GeneralSettingsElement::wfmChannelIndex() const {
  return getUInt8(Offset::wfmChannelIndex());
}
void
D578UVCodeplug::GeneralSettingsElement::setWFMChannelIndex(unsigned idx) {
  setUInt8(Offset::wfmChannelIndex(), idx);
}

bool
D578UVCodeplug::GeneralSettingsElement::wfmVFOEnabled() const {
  return getUInt8(Offset::wfmVFOEnabled());
}
void
D578UVCodeplug::GeneralSettingsElement::enableWFMVFO(bool enable) {
  setUInt8(Offset::wfmVFOEnabled(), (enable ? 0x01 : 0x00));
}

unsigned
D578UVCodeplug::GeneralSettingsElement::memoryZoneA() const {
  return getUInt8(Offset::memZoneA());
}
void
D578UVCodeplug::GeneralSettingsElement::setMemoryZoneA(unsigned zone) {
  setUInt8(Offset::memZoneA(), zone);
}

unsigned
D578UVCodeplug::GeneralSettingsElement::memoryZoneB() const {
  return getUInt8(Offset::memZoneB());
}
void
D578UVCodeplug::GeneralSettingsElement::setMemoryZoneB(unsigned zone) {
  setUInt8(Offset::memZoneB(), zone);
}

bool
D578UVCodeplug::GeneralSettingsElement::wfmEnabled() const {
  return 0x00 != getUInt8(Offset::wfmEnable());
}
void
D578UVCodeplug::GeneralSettingsElement::enableWFM(bool enable) {
  setUInt8(Offset::wfmEnable(), enable ? 0x01 : 0x00);
}

bool
D578UVCodeplug::GeneralSettingsElement::recording() const {
  return getUInt8(Offset::enableRecoding());
}
void
D578UVCodeplug::GeneralSettingsElement::enableRecording(bool enable) {
  setUInt8(Offset::enableRecoding(), (enable ? 0x01 : 0x00));
}


unsigned
D578UVCodeplug::GeneralSettingsElement::brightness() const {
  return (getUInt8(Offset::displayBrightness())*10)/4;
}
void
D578UVCodeplug::GeneralSettingsElement::setBrightness(unsigned level) {
  setUInt8(Offset::displayBrightness(), (level*4)/10);
}

bool
D578UVCodeplug::GeneralSettingsElement::gps() const {
  return getUInt8(Offset::gpsEnable());
}
void
D578UVCodeplug::GeneralSettingsElement::enableGPS(bool enable) {
  setUInt8(Offset::gpsEnable(), (enable ? 0x01 : 0x00));
}

bool
D578UVCodeplug::GeneralSettingsElement::smsAlert() const {
  return getUInt8(Offset::smsAlert());
}
void
D578UVCodeplug::GeneralSettingsElement::enableSMSAlert(bool enable) {
  setUInt8(Offset::smsAlert(), (enable ? 0x01 : 0x00));
}

bool
D578UVCodeplug::GeneralSettingsElement::wfmMonitor() const {
  return getUInt8(Offset::wfmMonitor());
}
void
D578UVCodeplug::GeneralSettingsElement::enableWFMMonitor(bool enable) {
  setUInt8(Offset::wfmMonitor(), (enable ? 0x01 : 0x00));
}

bool
D578UVCodeplug::GeneralSettingsElement::activeChannelB() const {
  return getUInt8(Offset::activeChannelB());
}
void
D578UVCodeplug::GeneralSettingsElement::enableActiveChannelB(bool enable) {
  setUInt8(Offset::activeChannelB(), (enable ? 0x01 : 0x00));
}

bool
D578UVCodeplug::GeneralSettingsElement::subChannel() const {
  return getUInt8(Offset::subChannel());
}
void
D578UVCodeplug::GeneralSettingsElement::enableSubChannel(bool enable) {
  setUInt8(Offset::subChannel(), (enable ? 0x01 : 0x00));
}

Frequency
D578UVCodeplug::GeneralSettingsElement::tbstFrequency() const {
  switch ((TBSTFrequency)getUInt8(Offset::tbstFrequency())) {
  case TBSTFrequency::Hz1000: return Frequency::fromHz(1000);
  case TBSTFrequency::Hz1450: return Frequency::fromHz(1450);
  case TBSTFrequency::Hz1750: return Frequency::fromHz(1750);
  case TBSTFrequency::Hz2100: return Frequency::fromHz(2100);
  }
  return Frequency::fromHz(1750);
}
void
D578UVCodeplug::GeneralSettingsElement::setTBSTFrequency(Frequency freq) {
  if (1000 == freq.inHz()) {
    setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1000);
  } else if (1450 == freq.inHz()) {
    setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1450);
  } else if (1750 == freq.inHz()) {
    setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1750);
  } else if (2100 == freq.inHz()) {
    setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz2100);
  } else {
    setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1750);
  }
}

bool
D578UVCodeplug::GeneralSettingsElement::callAlert() const {
  return getUInt8(Offset::callAlert());
}
void
D578UVCodeplug::GeneralSettingsElement::enableCallAlert(bool enable) {
  setUInt8(Offset::callAlert(), (enable ? 0x01 : 0x00));
}

QTimeZone
D578UVCodeplug::GeneralSettingsElement::gpsTimeZone() const {
  return TimeZone::decode(getUInt8(Offset::gpsTimeZone()));
}
void
D578UVCodeplug::GeneralSettingsElement::setGPSTimeZone(const QTimeZone &zone) {
  setUInt8(Offset::gpsTimeZone(), TimeZone::encode(zone));
}

bool
D578UVCodeplug::GeneralSettingsElement::dmrTalkPermit() const {
  return getBit(Offset::talkPermit(), 0);
}
void
D578UVCodeplug::GeneralSettingsElement::enableDMRTalkPermit(bool enable) {
  return setBit(Offset::talkPermit(), 0, enable);
}

bool
D578UVCodeplug::GeneralSettingsElement::fmTalkPermit() const {
  return getBit(Offset::talkPermit(), 1);
}
void
D578UVCodeplug::GeneralSettingsElement::enableFMTalkPermit(bool enable) {
  return setBit(Offset::talkPermit(), 1, enable);
}

bool
D578UVCodeplug::GeneralSettingsElement::dmrResetTone() const {
  return getUInt8(Offset::dmrResetTone());
}
void
D578UVCodeplug::GeneralSettingsElement::enableDMRResetTone(bool enable) {
  return setUInt8(Offset::dmrResetTone(), (enable ? 0x01 : 0x00));
}

bool
D578UVCodeplug::GeneralSettingsElement::idleChannelTone() const {
  return getUInt8(Offset::idleChannelTone());
}
void
D578UVCodeplug::GeneralSettingsElement::enableIdleChannelTone(bool enable) {
  return setUInt8(Offset::idleChannelTone(), (enable ? 0x01 : 0x00));
}

Interval
D578UVCodeplug::GeneralSettingsElement::menuExitTime() const {
  return Interval::fromSeconds(5 + 5*((unsigned) getUInt8(Offset::menuExitTime())));
}
void
D578UVCodeplug::GeneralSettingsElement::setMenuExitTime(Interval intv) {
  setUInt8(Offset::menuExitTime(), (std::max(5ULL, intv.seconds())-5)/5);
}

bool
D578UVCodeplug::GeneralSettingsElement::filterOwnID() const {
  return getUInt8(Offset::filterOwnID());
}
void
D578UVCodeplug::GeneralSettingsElement::enableFilterOwnID(bool enable) {
  setUInt8(Offset::filterOwnID(), (enable ? 0x01 : 0x00));
}

bool
D578UVCodeplug::GeneralSettingsElement::startupTone() const {
  return getUInt8(Offset::startupTone());
}
void
D578UVCodeplug::GeneralSettingsElement::enableStartupTone(bool enable) {
  return setUInt8(Offset::startupTone(), (enable ? 0x01 : 0x00));
}

bool
D578UVCodeplug::GeneralSettingsElement::callEndPrompt() const {
  return getUInt8(Offset::callEndPrompt());
}
void
D578UVCodeplug::GeneralSettingsElement::enableCallEndPrompt(bool enable) {
  return setUInt8(Offset::callEndPrompt(), (enable ? 0x01 : 0x00));
}

Level
D578UVCodeplug::GeneralSettingsElement::maxSpeakerVolume() const {
  return Level::fromValue(getUInt8(Offset::maxSpeakerVolume()), Limit::volume());
}
void
D578UVCodeplug::GeneralSettingsElement::setMaxSpeakerVolume(Level level) {
  setUInt8(Offset::maxSpeakerVolume(), level.mapTo(Limit::volume()));
}

bool
D578UVCodeplug::GeneralSettingsElement::remoteStunKill() const {
  return getUInt8(Offset::remoteStunKill());
}
void
D578UVCodeplug::GeneralSettingsElement::enableRemoteStunKill(bool enable) {
  setUInt8(Offset::remoteStunKill(), (enable ? 0x01 : 0x00));
}

bool
D578UVCodeplug::GeneralSettingsElement::remoteMonitor() const {
  return getUInt8(Offset::remoteMonitor());
}
void
D578UVCodeplug::GeneralSettingsElement::enableRemoteMonitor(bool enable) {
  setUInt8(Offset::remoteMonitor(), (enable ? 0x01 : 0x00));
}

bool
D578UVCodeplug::GeneralSettingsElement::getGPSPosition() const {
  return getUInt8(Offset::getGPSPosition());
}
void
D578UVCodeplug::GeneralSettingsElement::enableGetGPSPosition(bool enable) {
  return setUInt8(Offset::getGPSPosition(), (enable ? 0x01 : 0x00));
}

Interval
D578UVCodeplug::GeneralSettingsElement::longPressDuration() const {
  return Interval::fromSeconds(((unsigned)getUInt8(Offset::longPressDuration()))+1);
}
void
D578UVCodeplug::GeneralSettingsElement::setLongPressDuration(Interval ms) {
  setUInt8(Offset::longPressDuration(), std::max(1ULL,ms.seconds())-1);
}

bool
D578UVCodeplug::GeneralSettingsElement::volumeChangePrompt() const {
  return getUInt8(Offset::volumeChangePrompt());
}
void
D578UVCodeplug::GeneralSettingsElement::enableVolumeChangePrompt(bool enable) {
  setUInt8(Offset::volumeChangePrompt(), (enable ? 0x01 : 0x00));
}

AnytoneAutoRepeaterSettingsExtension::Direction
D578UVCodeplug::GeneralSettingsElement::autoRepeaterDirectionA() const {
  return (AnytoneAutoRepeaterSettingsExtension::Direction) getUInt8(Offset::autoRepeaterDirA());
}
void
D578UVCodeplug::GeneralSettingsElement::setAutoRepeaterDirectionA(AnytoneAutoRepeaterSettingsExtension::Direction dir) {
  setUInt8(Offset::autoRepeaterDirA(), (unsigned)dir);
}

AnytoneDMRSettingsExtension::SlotMatch
D578UVCodeplug::GeneralSettingsElement::monitorSlotMatch() const {
  return (AnytoneDMRSettingsExtension::SlotMatch)getUInt8(Offset::monSlotMatch());
}
void
D578UVCodeplug::GeneralSettingsElement::setMonitorSlotMatch(AnytoneDMRSettingsExtension::SlotMatch match) {
  setUInt8(Offset::monSlotMatch(), (unsigned)match);
}

bool
D578UVCodeplug::GeneralSettingsElement::monitorColorCodeMatch() const {
  return getUInt8(Offset::monColorCodeMatch());
}
void
D578UVCodeplug::GeneralSettingsElement::enableMonitorColorCodeMatch(bool enable) {
  setUInt8(Offset::monColorCodeMatch(), (enable ? 0x01 : 0x00));
}

bool
D578UVCodeplug::GeneralSettingsElement::monitorIDMatch() const {
  return getUInt8(Offset::monIDMatch());
}
void
D578UVCodeplug::GeneralSettingsElement::enableMonitorIDMatch(bool enable) {
  setUInt8(Offset::monIDMatch(), (enable ? 0x01 : 0x00));
}

bool
D578UVCodeplug::GeneralSettingsElement::monitorTimeSlotHold() const {
  return getUInt8(Offset::monTimeSlotHold());
}
void
D578UVCodeplug::GeneralSettingsElement::enableMonitorTimeSlotHold(bool enable) {
  setUInt8(Offset::monTimeSlotHold(), (enable ? 0x01 : 0x00));
}

AnytoneDisplaySettingsExtension::LastCallerDisplayMode
D578UVCodeplug::GeneralSettingsElement::lastCallerDisplayMode() const {
  return (AnytoneDisplaySettingsExtension::LastCallerDisplayMode)getUInt8(Offset::lastCallerDisplay());
}
void
D578UVCodeplug::GeneralSettingsElement::setLastCallerDisplayMode(AnytoneDisplaySettingsExtension::LastCallerDisplayMode mode) {
  setUInt8(Offset::lastCallerDisplay(), (unsigned)mode);
}

unsigned
D578UVCodeplug::GeneralSettingsElement::fmCallHold() const {
  return getUInt8(Offset::fmCallHold());
}
void
D578UVCodeplug::GeneralSettingsElement::setFMCallHold(unsigned sec) {
  setUInt8(Offset::fmCallHold(), sec);
}

bool
D578UVCodeplug::GeneralSettingsElement::displayClock() const {
  return getUInt8(Offset::showClock());
}
void
D578UVCodeplug::GeneralSettingsElement::enableDisplayClock(bool enable) {
  setUInt8(Offset::showClock(), (enable ? 0x01 : 0x00));
}

bool
D578UVCodeplug::GeneralSettingsElement::gpsMessageEnabled() const {
  return getUInt8(Offset::enableGPSMessage());
}
void
D578UVCodeplug::GeneralSettingsElement::enableGPSMessage(bool enable) {
  setUInt8(Offset::enableGPSMessage(), (enable ? 0x01 : 0x00));
}

bool
D578UVCodeplug::GeneralSettingsElement::enhanceAudio() const {
  return getUInt8(Offset::enhanceAudio());
}
void
D578UVCodeplug::GeneralSettingsElement::enableEnhancedAudio(bool enable) {
  setUInt8(Offset::enhanceAudio(), (enable ? 0x01 : 0x00));
}

Frequency
D578UVCodeplug::GeneralSettingsElement::minVFOScanFrequencyUHF() const {
  return Frequency::fromHz(((unsigned)getUInt32_le(Offset::minVFOScanUHF()))*10);
}
void
D578UVCodeplug::GeneralSettingsElement::setMinVFOScanFrequencyUHF(Frequency freq) {
  setUInt32_le(Offset::minVFOScanUHF(), freq.inHz()/10);
}

Frequency
D578UVCodeplug::GeneralSettingsElement::maxVFOScanFrequencyUHF() const {
  return Frequency::fromHz(((unsigned)getUInt32_le(Offset::maxVFOScanUHF()))*10);
}
void
D578UVCodeplug::GeneralSettingsElement::setMaxVFOScanFrequencyUHF(Frequency freq) {
  setUInt32_le(Offset::maxVFOScanUHF(), freq.inHz()/10);
}

Frequency
D578UVCodeplug::GeneralSettingsElement::minVFOScanFrequencyVHF() const {
  return Frequency::fromHz(((unsigned)getUInt32_le(Offset::minVFOScanVHF()))*10);
}
void
D578UVCodeplug::GeneralSettingsElement::setMinVFOScanFrequencyVHF(Frequency freq) {
  setUInt32_le(Offset::minVFOScanVHF(), freq.inHz()/10);
}

Frequency
D578UVCodeplug::GeneralSettingsElement::maxVFOScanFrequencyVHF() const {
  return Frequency::fromHz(((unsigned)getUInt32_le(Offset::maxVFOScanVHF()))*10);
}
void
D578UVCodeplug::GeneralSettingsElement::setMaxVFOScanFrequencyVHF(Frequency freq) {
  setUInt32_le(Offset::maxVFOScanVHF(), freq.inHz()/10);
}

bool
D578UVCodeplug::GeneralSettingsElement::hasAutoRepeaterOffsetFrequencyIndexUHF() const {
  return 0xff != autoRepeaterOffsetFrequencyIndexUHF();
}
unsigned
D578UVCodeplug::GeneralSettingsElement::autoRepeaterOffsetFrequencyIndexUHF() const {
  return getUInt8(Offset::autoRepOffsetUHF());
}
void
D578UVCodeplug::GeneralSettingsElement::setAutoRepeaterOffsetFrequenyIndexUHF(unsigned idx) {
  setUInt8(Offset::autoRepOffsetUHF(), idx);
}
void
D578UVCodeplug::GeneralSettingsElement::clearAutoRepeaterOffsetFrequencyIndexUHF() {
  setAutoRepeaterOffsetFrequenyIndexUHF(0xff);
}

bool
D578UVCodeplug::GeneralSettingsElement::hasAutoRepeaterOffsetFrequencyIndexVHF() const {
  return 0xff != autoRepeaterOffsetFrequencyIndexVHF();
}
unsigned
D578UVCodeplug::GeneralSettingsElement::autoRepeaterOffsetFrequencyIndexVHF() const {
  return getUInt8(Offset::autoRepOffsetVHF());
}
void
D578UVCodeplug::GeneralSettingsElement::setAutoRepeaterOffsetFrequenyIndexVHF(unsigned idx) {
  setUInt8(Offset::autoRepOffsetVHF(), idx);
}
void
D578UVCodeplug::GeneralSettingsElement::clearAutoRepeaterOffsetFrequencyIndexVHF() {
  setAutoRepeaterOffsetFrequenyIndexVHF(0xff);
}

Frequency
D578UVCodeplug::GeneralSettingsElement::autoRepeaterMinFrequencyVHF() const {
  return Frequency::fromHz(getUInt32_le(Offset::autoRepMinVHF())*10);
}
void
D578UVCodeplug::GeneralSettingsElement::setAutoRepeaterMinFrequencyVHF(Frequency freq) {
  setUInt32_le(Offset::autoRepMinVHF(), freq.inHz()/10);
}
Frequency
D578UVCodeplug::GeneralSettingsElement::autoRepeaterMaxFrequencyVHF() const {
  return Frequency::fromHz(getUInt32_le(Offset::autoRepMaxVHF())*10);
}
void
D578UVCodeplug::GeneralSettingsElement::setAutoRepeaterMaxFrequencyVHF(Frequency freq) {
  setUInt32_le(Offset::autoRepMaxVHF(), freq.inHz()/10);
}

Frequency
D578UVCodeplug::GeneralSettingsElement::autoRepeaterMinFrequencyUHF() const {
  return Frequency::fromHz(getUInt32_le(Offset::autoRepMinUHF())*10);
}
void
D578UVCodeplug::GeneralSettingsElement::setAutoRepeaterMinFrequencyUHF(Frequency freq) {
  setUInt32_le(Offset::autoRepMinUHF(), freq.inHz()/10);
}
Frequency
D578UVCodeplug::GeneralSettingsElement::autoRepeaterMaxFrequencyUHF() const {
  return Frequency::fromHz(getUInt32_le(Offset::autoRepMaxUHF())*10);
}
void
D578UVCodeplug::GeneralSettingsElement::setAutoRepeaterMaxFrequencyUHF(Frequency freq) {
  setUInt32_le(Offset::autoRepMaxUHF(), freq.inHz()/10);
}

void
D578UVCodeplug::GeneralSettingsElement::callToneMelody(Melody &melody) const {
  QVector<QPair<double, unsigned int>> tones; tones.reserve(5);
  for (int i=0; i<5; i++) {
    double freq = getUInt16_le(Offset::callToneTones()+2*i);
    unsigned int duration = getUInt16_le(Offset::callToneDurations()+2*i);
    if (duration) tones.append({freq, duration});
  }
  melody.infer(tones);
}
void
D578UVCodeplug::GeneralSettingsElement::setCallToneMelody(const Melody &melody) {
  unsigned int n=std::min(5U, (unsigned int)melody.count());
  QVector<QPair<double, unsigned int>> tones = melody.toTones();
  for (unsigned int i=0; i<n; i++) {
    setUInt16_le(Offset::callToneTones()+2*i, tones.at(i).first);
    setUInt16_le(Offset::callToneDurations()+2*i, tones.at(i).second);
  }
}

void
D578UVCodeplug::GeneralSettingsElement::idleToneMelody(Melody &melody) const {
  QVector<QPair<double, unsigned int>> tones; tones.reserve(5);
  for (int i=0; i<5; i++) {
    double frequency = getUInt16_le(Offset::idleToneTones()+2*i);
    unsigned int duration  = getUInt16_le(Offset::idleToneDurations()+2*i);
    if (duration) tones.append({frequency, duration});
  }
  melody.infer(tones);
}
void
D578UVCodeplug::GeneralSettingsElement::setIdleToneMelody(const Melody &melody) {
  unsigned int n=std::min(5U, (unsigned int)melody.count());
  QVector<QPair<double, unsigned int>> tones = melody.toTones();
  for (unsigned int i=0; i<n; i++) {
    setUInt16_le(Offset::idleToneTones()+2*i, tones.at(i).first);
    setUInt16_le(Offset::idleToneDurations()+2*i, tones.at(i).second);
  }
}

void
D578UVCodeplug::GeneralSettingsElement::resetToneMelody(Melody &melody) const {
  QVector<QPair<double, unsigned int>> tones; tones.reserve(5);
  for (int i=0; i<5; i++) {
    double frequency = getUInt16_le(Offset::resetToneTones()+2*i);
    unsigned int duration  = getUInt16_le(Offset::resetToneDurations()+2*i);
    if (duration) tones.append({frequency, duration});
  }
  melody.infer(tones);
}
void
D578UVCodeplug::GeneralSettingsElement::setResetToneMelody(const Melody &melody) {
  unsigned int n=std::min(5U, (unsigned int)melody.count());
  QVector<QPair<double, unsigned int>> tones = melody.toTones();
  for (unsigned int i=0; i<n; i++) {
    setUInt16_le(Offset::resetToneTones()+2*i, tones.at(i).first);
    setUInt16_le(Offset::resetToneDurations()+2*i, tones.at(i).second);
  }
}

unsigned
D578UVCodeplug::GeneralSettingsElement::priorityZoneAIndex() const {
  return getUInt8(Offset::priorityZoneA());
}
void
D578UVCodeplug::GeneralSettingsElement::setPriorityZoneAIndex(unsigned idx) {
  setUInt8(Offset::priorityZoneA(), idx);
}
unsigned
D578UVCodeplug::GeneralSettingsElement::priorityZoneBIndex() const {
  return getUInt8(Offset::priorityZoneB());
}
void
D578UVCodeplug::GeneralSettingsElement::setPriorityZoneBIndex(unsigned idx) {
  setUInt8(Offset::priorityZoneB(), idx);
}

bool
D578UVCodeplug::GeneralSettingsElement::displayCall() const {
  return getUInt8(Offset::callDisplayMode());
}
void
D578UVCodeplug::GeneralSettingsElement::enableDisplayCall(bool enable) {
  setUInt8(Offset::callDisplayMode(), (enable ? 0x01 : 0x00));
}

bool
D578UVCodeplug::GeneralSettingsElement::bluetooth() const {
  return 0 != getUInt8(Offset::bluetooth());
}
void
D578UVCodeplug::GeneralSettingsElement::enableBluetooth(bool enable) {
  setUInt8(Offset::bluetooth(), enable ? 0x01 : 0x00);
}

bool
D578UVCodeplug::GeneralSettingsElement::btAndInternalMic() const {
  return 0 != getUInt8(Offset::btAndInternalMic());
}
void
D578UVCodeplug::GeneralSettingsElement::enableBTAndInternalMic(bool enable) {
  setUInt8(Offset::btAndInternalMic(), enable ? 0x01 : 0x00);
}

bool
D578UVCodeplug::GeneralSettingsElement::btAndInternalSpeaker() const {
  return 0 != getUInt8(Offset::btAndInternalSpeaker());
}
void
D578UVCodeplug::GeneralSettingsElement::enableBTAndInternalSpeaker(bool enable) {
  setUInt8(Offset::btAndInternalSpeaker(), enable ? 0x01 : 0x00);
}

bool
D578UVCodeplug::GeneralSettingsElement::pluginRecTone() const {
  return 0 != getUInt8(Offset::pluginRecTone());
}
void
D578UVCodeplug::GeneralSettingsElement::enablePluginRecTone(bool enable) {
  setUInt8(Offset::pluginRecTone(), enable ? 0x01 : 0x00);
}

Interval
D578UVCodeplug::GeneralSettingsElement::gpsUpdatePeriod() const {
  return Interval::fromSeconds(getUInt8(Offset::gpsRangingInterval()));
}
void
D578UVCodeplug::GeneralSettingsElement::setGPSUpdatePeriod(Interval intv) {
  setUInt8(Offset::gpsRangingInterval(), intv.seconds());
}

unsigned int
D578UVCodeplug::GeneralSettingsElement::btMicGain() const {
  return (getUInt8(Offset::btMicGain())+1)*2;
}
void
D578UVCodeplug::GeneralSettingsElement::setBTMicGain(unsigned int gain) {
  gain = std::min(10U, std::max(2U, gain));
  setUInt8(Offset::btMicGain(), gain/2-1);
}

unsigned int
D578UVCodeplug::GeneralSettingsElement::btSpeakerGain() const {
  return (getUInt8(Offset::btSpeakerGain())+1)*2;
}
void
D578UVCodeplug::GeneralSettingsElement::setBTSpeakerGain(unsigned int gain) {
  gain = std::min(10U, std::max(2U, gain));
  setUInt8(Offset::btSpeakerGain(), gain/2-1);
}

bool
D578UVCodeplug::GeneralSettingsElement::displayChannelNumber() const {
  return getUInt8(Offset::showChannelNumber());
}
void
D578UVCodeplug::GeneralSettingsElement::enableDisplayChannelNumber(bool enable) {
  setUInt8(Offset::showChannelNumber(), (enable ? 0x01 : 0x00));
}

bool
D578UVCodeplug::GeneralSettingsElement::showCurrentContact() const {
  return getUInt8(Offset::showCurrentContact());
}
void
D578UVCodeplug::GeneralSettingsElement::enableShowCurrentContact(bool enable) {
  setUInt8(Offset::showCurrentContact(), (enable ? 0x01 : 0x00));
}

Interval
D578UVCodeplug::GeneralSettingsElement::autoRoamPeriod() const {
  return Interval::fromMinutes(getUInt8(Offset::autoRoamPeriod()));
}
void
D578UVCodeplug::GeneralSettingsElement::setAutoRoamPeriod(Interval intv) {
  setUInt8(Offset::autoRoamPeriod(), intv.minutes());
}

AnytoneDisplaySettingsExtension::Color
D578UVCodeplug::GeneralSettingsElement::callDisplayColor() const {
  return NameColor::decode(getUInt8(Offset::callColor()));
}
void
D578UVCodeplug::GeneralSettingsElement::setCallDisplayColor(AnytoneDisplaySettingsExtension::Color color) {
  setUInt8(Offset::callColor(), NameColor::encode(color));
}

bool
D578UVCodeplug::GeneralSettingsElement::gpsUnitsImperial() const {
  return getUInt8(Offset::gpsUnits());
}
void
D578UVCodeplug::GeneralSettingsElement::enableGPSUnitsImperial(bool enable) {
  setUInt8(Offset::gpsUnits(), (enable ? 0x01 : 0x00));
}

bool
D578UVCodeplug::GeneralSettingsElement::knobLock() const {
  return getBit(Offset::knobLock());
}
void
D578UVCodeplug::GeneralSettingsElement::enableKnobLock(bool enable) {
  setBit(Offset::knobLock(), enable);
}
bool
D578UVCodeplug::GeneralSettingsElement::keypadLock() const {
  return getBit(Offset::keypadLock());
}
void
D578UVCodeplug::GeneralSettingsElement::enableKeypadLock(bool enable) {
  setBit(Offset::keypadLock(), enable);
}
bool
D578UVCodeplug::GeneralSettingsElement::sidekeysLock() const {
  return getBit(Offset::sideKeyLock());
}
void
D578UVCodeplug::GeneralSettingsElement::enableSidekeysLock(bool enable) {
  setBit(Offset::sideKeyLock(), enable);
}
bool
D578UVCodeplug::GeneralSettingsElement::keyLockForced() const {
  return getBit(Offset::forceKeyLock());
}
void
D578UVCodeplug::GeneralSettingsElement::enableKeyLockForced(bool enable) {
  setBit(Offset::forceKeyLock(), enable);
}

Interval
D578UVCodeplug::GeneralSettingsElement::autoRoamDelay() const {
  return Interval::fromSeconds(getUInt8(Offset::autoRoamDelay()));
}
void
D578UVCodeplug::GeneralSettingsElement::setAutoRoamDelay(Interval intv) {
  setUInt8(Offset::autoRoamDelay(), intv.seconds());
}

AnytoneDisplaySettingsExtension::Color
D578UVCodeplug::GeneralSettingsElement::standbyTextColor() const {
  return TextColor::decode(getUInt8(Offset::standbyTextColor()));
}
void
D578UVCodeplug::GeneralSettingsElement::setStandbyTextColor(AnytoneDisplaySettingsExtension::Color color) {
  setUInt8(Offset::standbyTextColor(), TextColor::encode(color));
}

AnytoneDisplaySettingsExtension::Color
D578UVCodeplug::GeneralSettingsElement::standbyBackgroundColor() const {
  return TextColor::decode(getUInt8(Offset::standbyBackground()));
}
void
D578UVCodeplug::GeneralSettingsElement::setStandbyBackgroundColor(AnytoneDisplaySettingsExtension::Color color) {
  setUInt8(Offset::standbyBackground(), TextColor::encode(color));
}

bool
D578UVCodeplug::GeneralSettingsElement::showLastHeard() const {
  return getUInt8(Offset::showLastHeard());
}
void
D578UVCodeplug::GeneralSettingsElement::enableShowLastHeard(bool enable) {
  setUInt8(Offset::showLastHeard(), (enable ? 0x01 : 0x00));
}


SMSExtension::Format
D578UVCodeplug::GeneralSettingsElement::smsFormat() const {
  switch ((SMSFormat) getUInt8(Offset::smsFormat())) {
  case SMSFormat::Motorola: return SMSExtension::Format::Motorola;
  case SMSFormat::Hytera: return SMSExtension::Format::Hytera;
  case SMSFormat::DMR: return SMSExtension::Format::DMR;
  }
  return SMSExtension::Format::Motorola;
}

void
D578UVCodeplug::GeneralSettingsElement::setSMSFormat(SMSExtension::Format fmt) {
  switch (fmt) {
  case SMSExtension::Format::Motorola:
    setUInt8(Offset::smsFormat(), (unsigned)SMSFormat::Motorola);
    break;
  case SMSExtension::Format::Hytera:
    setUInt8(Offset::smsFormat(), (unsigned)SMSFormat::Hytera);
    break;
  case SMSExtension::Format::DMR:
    setUInt8(Offset::smsFormat(), (unsigned)SMSFormat::DMR);
    break;
  }
}


AnytoneAutoRepeaterSettingsExtension::Direction
D578UVCodeplug::GeneralSettingsElement::autoRepeaterDirectionB() const {
  return (AnytoneAutoRepeaterSettingsExtension::Direction)getUInt8(Offset::autoRepeaterDirB());
}
void
D578UVCodeplug::GeneralSettingsElement::setAutoRepeaterDirectionB(AnytoneAutoRepeaterSettingsExtension::Direction dir) {
  setUInt8(Offset::autoRepeaterDirB(), (unsigned)dir);
}

bool
D578UVCodeplug::GeneralSettingsElement::fmSendIDAndContact() const {
  return 0 != getUInt8(Offset::fmSendIDAndContact());
}
void
D578UVCodeplug::GeneralSettingsElement::enableFMSendIDAndContact(bool enable) {
  setUInt8(Offset::fmSendIDAndContact(), enable ? 0x01 : 0x00);
}

bool
D578UVCodeplug::GeneralSettingsElement::defaultChannel() const {
  return getUInt8(Offset::defaultChannels());
}
void
D578UVCodeplug::GeneralSettingsElement::enableDefaultChannel(bool enable) {
  setUInt8(Offset::defaultChannels(), (enable ? 0x01 : 0x00));
}

unsigned
D578UVCodeplug::GeneralSettingsElement::defaultZoneIndexA() const {
  return getUInt8(Offset::defaultZoneA());
}
void
D578UVCodeplug::GeneralSettingsElement::setDefaultZoneIndexA(unsigned idx) {
  setUInt8(Offset::defaultZoneA(), idx);
}

unsigned
D578UVCodeplug::GeneralSettingsElement::defaultZoneIndexB() const {
  return getUInt8(Offset::defaultZoneB());
}
void
D578UVCodeplug::GeneralSettingsElement::setDefaultZoneIndexB(unsigned idx) {
  setUInt8(Offset::defaultZoneB(), idx);
}

bool
D578UVCodeplug::GeneralSettingsElement::defaultChannelAIsVFO() const {
  return 0xff == defaultChannelAIndex();
}
unsigned
D578UVCodeplug::GeneralSettingsElement::defaultChannelAIndex() const {
  return getUInt8(Offset::defaultChannelA());
}
void
D578UVCodeplug::GeneralSettingsElement::setDefaultChannelAIndex(unsigned idx) {
  setUInt8(Offset::defaultChannelA(), idx);
}
void
D578UVCodeplug::GeneralSettingsElement::setDefaultChannelAToVFO() {
  setDefaultChannelAIndex(0xff);
}

bool
D578UVCodeplug::GeneralSettingsElement::defaultChannelBIsVFO() const {
  return 0xff == defaultChannelBIndex();
}
unsigned
D578UVCodeplug::GeneralSettingsElement::defaultChannelBIndex() const {
  return getUInt8(Offset::defaultChannelB());
}
void
D578UVCodeplug::GeneralSettingsElement::setDefaultChannelBIndex(unsigned idx) {
  setUInt8(Offset::defaultChannelB(), idx);
}
void
D578UVCodeplug::GeneralSettingsElement::setDefaultChannelBToVFO() {
  setDefaultChannelBIndex(0xff);
}

unsigned
D578UVCodeplug::GeneralSettingsElement::defaultRoamingZoneIndex() const {
  return getUInt8(Offset::defaultRoamingZone());
}
void
D578UVCodeplug::GeneralSettingsElement::setDefaultRoamingZoneIndex(unsigned idx) {
  setUInt8(Offset::defaultRoamingZone(), idx);
}

bool
D578UVCodeplug::GeneralSettingsElement::repeaterRangeCheck() const {
  return getUInt8(Offset::repRangeCheck());
}
void
D578UVCodeplug::GeneralSettingsElement::enableRepeaterRangeCheck(bool enable) {
  setUInt8(Offset::repRangeCheck(), (enable ? 0x01 : 0x00));
}

Interval
D578UVCodeplug::GeneralSettingsElement::repeaterRangeCheckInterval() const {
  return Interval::fromSeconds(((unsigned)getUInt8(Offset::rangeCheckInterval()))*5);
}
void
D578UVCodeplug::GeneralSettingsElement::setRepeaterRangeCheckInterval(Interval intv) {
  setUInt8(Offset::rangeCheckInterval(), intv.seconds()/5);
}

unsigned
D578UVCodeplug::GeneralSettingsElement::repeaterRangeCheckCount() const {
  return getUInt8(Offset::rangeCheckCount());
}
void
D578UVCodeplug::GeneralSettingsElement::setRepeaterRangeCheckCount(unsigned n) {
  setUInt8(Offset::rangeCheckCount(), n);
}

AnytoneRoamingSettingsExtension::RoamStart
D578UVCodeplug::GeneralSettingsElement::roamingStartCondition() const {
  return (AnytoneRoamingSettingsExtension::RoamStart)getUInt8(Offset::roamStartCondition());
}
void
D578UVCodeplug::GeneralSettingsElement::setRoamingStartCondition(AnytoneRoamingSettingsExtension::RoamStart cond) {
  setUInt8(Offset::roamStartCondition(), (unsigned)cond);
}

bool
D578UVCodeplug::GeneralSettingsElement::separateDisplay() const {
  return getUInt8(Offset::displaySeparator());
}
void
D578UVCodeplug::GeneralSettingsElement::enableSeparateDisplay(bool enable) {
  setUInt8(Offset::displaySeparator(), (enable ? 0x01 : 0x00));
}

bool
D578UVCodeplug::GeneralSettingsElement::keepLastCaller() const {
  return getUInt8(Offset::keepLastCaller());
}
void
D578UVCodeplug::GeneralSettingsElement::enableKeepLastCaller(bool enable) {
  setUInt8(Offset::keepLastCaller(), (enable ? 0x01 : 0x00));
}

AnytoneDisplaySettingsExtension::Color
D578UVCodeplug::GeneralSettingsElement::channelNameColor() const {
  return NameColor::decode(getUInt8(Offset::channelNameColor()));
}
void
D578UVCodeplug::GeneralSettingsElement::setChannelNameColor(AnytoneDisplaySettingsExtension::Color color) {
  setUInt8(Offset::channelNameColor(), NameColor::encode(color));
}

bool
D578UVCodeplug::GeneralSettingsElement::repeaterCheckNotification() const {
  return getUInt8(Offset::repCheckNotify());
}
void
D578UVCodeplug::GeneralSettingsElement::enableRepeaterCheckNotification(bool enable) {
  setUInt8(Offset::repCheckNotify(), (enable ? 0x01 : 0x00));
}

bool
D578UVCodeplug::GeneralSettingsElement::roaming() const {
  return getUInt8(Offset::roaming());
}
void
D578UVCodeplug::GeneralSettingsElement::enableRoaming(bool enable) {
  setUInt8(Offset::roaming(), (enable ? 0x01 : 0x00));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::GeneralSettingsElement::funcKey1Short() const {
  return KeyFunction::decode(getUInt8(Offset::progFuncKey1Short()));
}
void
D578UVCodeplug::GeneralSettingsElement::setFuncKey1Short(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::progFuncKey1Short(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::GeneralSettingsElement::funcKey2Short() const {
  return KeyFunction::decode(getUInt8(Offset::progFuncKey2Short()));
}
void
D578UVCodeplug::GeneralSettingsElement::setFuncKey2Short(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::progFuncKey2Short(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::GeneralSettingsElement::funcKey3Short() const {
  return KeyFunction::decode(getUInt8(Offset::progFuncKey3Short()));
}
void
D578UVCodeplug::GeneralSettingsElement::setFuncKey3Short(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::progFuncKey3Short(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::GeneralSettingsElement::funcKey4Short() const {
  return KeyFunction::decode(getUInt8(Offset::progFuncKey4Short()));
}
void
D578UVCodeplug::GeneralSettingsElement::setFuncKey4Short(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::progFuncKey4Short(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::GeneralSettingsElement::funcKey5Short() const {
  return KeyFunction::decode(getUInt8(Offset::progFuncKey5Short()));
}
void
D578UVCodeplug::GeneralSettingsElement::setFuncKey5Short(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::progFuncKey5Short(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::GeneralSettingsElement::funcKey6Short() const {
  return KeyFunction::decode(getUInt8(Offset::progFuncKey6Short()));
}
void
D578UVCodeplug::GeneralSettingsElement::setFuncKey6Short(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::progFuncKey6Short(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::GeneralSettingsElement::funcKeyAShort() const {
  return KeyFunction::decode(getUInt8(Offset::progFuncKeyAShort()));
}
void
D578UVCodeplug::GeneralSettingsElement::setFuncKeyAShort(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::progFuncKeyAShort(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::GeneralSettingsElement::funcKeyBShort() const {
  return KeyFunction::decode(getUInt8(Offset::progFuncKeyBShort()));
}
void
D578UVCodeplug::GeneralSettingsElement::setFuncKeyBShort(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::progFuncKeyBShort(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::GeneralSettingsElement::funcKeyCShort() const {
  return KeyFunction::decode(getUInt8(Offset::progFuncKeyCShort()));
}
void
D578UVCodeplug::GeneralSettingsElement::setFuncKeyCShort(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::progFuncKeyCShort(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::GeneralSettingsElement::funcKeyDShort() const {
  return KeyFunction::decode(getUInt8(Offset::progFuncKeyDShort()));
}
void
D578UVCodeplug::GeneralSettingsElement::setFuncKeyDShort(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::progFuncKeyDShort(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::GeneralSettingsElement::funcKey1Long() const {
  return KeyFunction::decode(getUInt8(Offset::progFuncKey1Long()));
}
void
D578UVCodeplug::GeneralSettingsElement::setFuncKey1Long(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::progFuncKey1Long(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::GeneralSettingsElement::funcKey2Long() const {
  return KeyFunction::decode(getUInt8(Offset::progFuncKey2Long()));
}
void
D578UVCodeplug::GeneralSettingsElement::setFuncKey2Long(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::progFuncKey2Long(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::GeneralSettingsElement::funcKey3Long() const {
  return KeyFunction::decode(getUInt8(Offset::progFuncKey3Long()));
}
void
D578UVCodeplug::GeneralSettingsElement::setFuncKey3Long(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::progFuncKey3Long(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::GeneralSettingsElement::funcKey4Long() const {
  return KeyFunction::decode(getUInt8(Offset::progFuncKey4Long()));
}
void
D578UVCodeplug::GeneralSettingsElement::setFuncKey4Long(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::progFuncKey4Long(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::GeneralSettingsElement::funcKey5Long() const {
  return KeyFunction::decode(getUInt8(Offset::progFuncKey5Long()));
}
void
D578UVCodeplug::GeneralSettingsElement::setFuncKey5Long(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::progFuncKey5Long(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::GeneralSettingsElement::funcKey6Long() const {
  return KeyFunction::decode(getUInt8(Offset::progFuncKey6Long()));
}
void
D578UVCodeplug::GeneralSettingsElement::setFuncKey6Long(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::progFuncKey6Long(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::GeneralSettingsElement::funcKeyALong() const {
  return KeyFunction::decode(getUInt8(Offset::progFuncKeyALong()));
}
void
D578UVCodeplug::GeneralSettingsElement::setFuncKeyALong(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::progFuncKeyALong(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::GeneralSettingsElement::funcKeyBLong() const {
  return KeyFunction::decode(getUInt8(Offset::progFuncKeyBLong()));
}
void
D578UVCodeplug::GeneralSettingsElement::setFuncKeyBLong(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::progFuncKeyBLong(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::GeneralSettingsElement::funcKeyCLong() const {
  return KeyFunction::decode(getUInt8(Offset::progFuncKeyCLong()));
}
void
D578UVCodeplug::GeneralSettingsElement::setFuncKeyCLong(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::progFuncKeyCLong(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::GeneralSettingsElement::funcKeyDLong() const {
  return KeyFunction::decode(getUInt8(Offset::progFuncKeyDLong()));
}
void
D578UVCodeplug::GeneralSettingsElement::setFuncKeyDLong(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::progFuncKeyDLong(), KeyFunction::encode(func));
}


unsigned int
D578UVCodeplug::GeneralSettingsElement::repeaterCheckNumNotifications() const {
  return getUInt8(Offset::repCheckNumNotify())+1;
}
void
D578UVCodeplug::GeneralSettingsElement::setRepeaterCheckNumNotifications(unsigned int n) {
  n = Limit::repeaterOORNotificationCount().limit(n);
  setUInt8(Offset::repCheckNumNotify(), n-1);
}


Interval
D578UVCodeplug::GeneralSettingsElement::txBacklightDuration() const {
  return Interval::fromSeconds(getUInt8(Offset::txBacklightDuration()));
}

void
D578UVCodeplug::GeneralSettingsElement::setTXBacklightDuration(Interval intv) {
  auto seconds = std::min(30ULL, intv.seconds());
  setUInt8(Offset::txBacklightDuration(), seconds);
}


bool
D578UVCodeplug::GeneralSettingsElement::btHoldTimeEnabled() const {
  return 0x00 != getUInt8(Offset::btHoldTime());
}

bool
D578UVCodeplug::GeneralSettingsElement::btHoldTimeInfinite() const {
  return 121U == getUInt8(Offset::btHoldTime());
}

Interval
D578UVCodeplug::GeneralSettingsElement::btHoldTime() const {
  return Interval::fromSeconds(getUInt8(Offset::btHoldTime()));
}

void
D578UVCodeplug::GeneralSettingsElement::setBTHoldTime(Interval interval) {
  unsigned int seconds = std::min(120ULL, std::max(1ULL, interval.seconds()));
  setUInt8(Offset::btHoldTime(), seconds);
}

void
D578UVCodeplug::GeneralSettingsElement::setBTHoldTimeInfinite() {
  setUInt8(Offset::btHoldTime(), 121);
}

void
D578UVCodeplug::GeneralSettingsElement::disableBTHoldTime() {
  setUInt8(Offset::btHoldTime(), 0);
}


Interval
D578UVCodeplug::GeneralSettingsElement::btRXDelay() const {
  if (0 == getUInt8(Offset::btRXDelay()))
    return Interval::fromMilliseconds(30);
  return Interval::fromMilliseconds(((unsigned int)getUInt8(Offset::btRXDelay())+1)*500);
}

void
D578UVCodeplug::GeneralSettingsElement::setBTRXDelay(Interval delay) {
  if (500 >= delay.milliseconds()) {
    setUInt8(Offset::btRXDelay(), 0);
  } else {
    unsigned int millis = std::min(5500ULL, std::max(500ULL, delay.milliseconds()));
    setUInt8(Offset::btRXDelay(), (millis-500)/500);
  }
}


bool
D578UVCodeplug::GeneralSettingsElement::fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err) {
  if (! AnytoneCodeplug::GeneralSettingsElement::fromConfig(flags, ctx, err))
    return false;

  // Encode tone settings
  enableIdleChannelTone(
    ctx.config()->settings()->tone()->channelIdle().testFlag(Channel::Type::DMR));

  // Set transmit timeout
  setTransmitTimeout(ctx.config()->settings()->tot());

  // Set measurement system based on system locale (0x00==Metric)
  enableGPSUnitsImperial(GNSSSettings::Units::Archaic == ctx.config()->settings()->gnss()->units());

  // DMR settings
  setGroupCallHangTime(ctx.config()->settings()->dmr()->groupCallHangTime());
  setPrivateCallHangTime(ctx.config()->settings()->dmr()->privateCallHangTime());
  setSMSFormat(ctx.config()->smsExtension()->format());


  // Handle D578UV specific settings
  AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension();
  if (nullptr == ext) { // <- if extension is not set
    setPriorityZoneAIndex(0xff);
    setPriorityZoneBIndex(0xff);
    return true;
  }

  // Encode boot settings
  if (ext->bootSettings()->priorityZoneA()->isNull())
    setPriorityZoneAIndex(0xff);
  else
    setPriorityZoneAIndex(ctx.index(ext->bootSettings()->priorityZoneA()->as<Zone>()));
  if (ext->bootSettings()->priorityZoneB()->isNull())
    setPriorityZoneBIndex(0xff);
  else
    setPriorityZoneBIndex(ctx.index(ext->bootSettings()->priorityZoneB()->as<Zone>()));
  if (! ext->roamingSettings()->defaultZone()->isNull())
    setDefaultRoamingZoneIndex(ctx.index(ext->roamingSettings()->defaultZone()->as<RoamingZone>()));

  // Encode key settings
  enableKnobLock(ext->keySettings()->knobLockEnabled());
  enableKeypadLock(ext->keySettings()->keypadLockEnabled());
  enableSidekeysLock(ext->keySettings()->sideKeysLockEnabled());
  enableKeyLockForced(ext->keySettings()->forcedKeyLockEnabled());
  setFuncKey3Short(ext->keySettings()->funcKey3Short());
  setFuncKey3Long(ext->keySettings()->funcKey3Long());
  setFuncKey4Short(ext->keySettings()->funcKey4Short());
  setFuncKey4Long(ext->keySettings()->funcKey4Long());
  setFuncKey5Short(ext->keySettings()->funcKey5Short());
  setFuncKey5Long(ext->keySettings()->funcKey5Long());
  setFuncKey6Short(ext->keySettings()->funcKey6Short());
  setFuncKey6Long(ext->keySettings()->funcKey6Long());
  setFuncKeyDShort(ext->keySettings()->funcKeyDShort());
  setFuncKeyDLong(ext->keySettings()->funcKeyDLong());

  // Encode display settings
  setCallDisplayColor(ext->displaySettings()->callColor());
  setLanguage(ext->displaySettings()->language());
  enableDisplayChannelNumber(ext->displaySettings()->showChannelNumberEnabled());
  enableShowCurrentContact(ext->displaySettings()->showContact());
  setStandbyTextColor(ext->displaySettings()->standbyTextColor());
  enableShowLastHeard(ext->displaySettings()->showLastHeardEnabled());
  setChannelNameColor(ext->displaySettings()->channelNameColor());
  enableShowCurrentContact(ext->displaySettings()->showContact());
  setTXBacklightDuration(ext->displaySettings()->backlightDurationTX());

  // Encode menu settings
  enableSeparateDisplay(ext->menuSettings()->separatorEnabled());

  // Encode auto-repeater settings
  setAutoRepeaterDirectionB(ext->autoRepeaterSettings()->directionB());
  setAutoRepeaterMinFrequencyVHF(ext->autoRepeaterSettings()->vhfMin());
  setAutoRepeaterMaxFrequencyVHF(ext->autoRepeaterSettings()->vhfMax());
  setAutoRepeaterMinFrequencyUHF(ext->autoRepeaterSettings()->uhfMin());
  setAutoRepeaterMaxFrequencyUHF(ext->autoRepeaterSettings()->uhfMax());

  // Encode DMR settings
  setWakeHeadPeriod(ext->dmrSettings()->wakeHeadPeriod());
  enableFilterOwnID(ext->dmrSettings()->filterOwnIDEnabled());
  setMonitorSlotMatch(ext->dmrSettings()->monitorSlotMatch());
  enableMonitorColorCodeMatch(ext->dmrSettings()->monitorColorCodeMatchEnabled());
  enableMonitorIDMatch(ext->dmrSettings()->monitorIDMatchEnabled());
  enableMonitorTimeSlotHold(ext->dmrSettings()->monitorTimeSlotHoldEnabled());

  // Encode GPS settings
  setGPSTimeZone(ext->gpsSettings()->timeZone());
  enableGPSMessage(ext->gpsSettings()->positionReportingEnabled());
  setGPSUpdatePeriod(ext->gpsSettings()->updatePeriod());

  // Encode ranging/roaming settings.
  setAutoRoamPeriod(ext->roamingSettings()->autoRoamPeriod());
  setAutoRoamDelay(ext->roamingSettings()->autoRoamDelay());
  enableRepeaterRangeCheck(ext->roamingSettings()->repeaterRangeCheckEnabled());
  setRepeaterRangeCheckInterval(ext->roamingSettings()->repeaterCheckInterval());
  setRepeaterRangeCheckCount(ext->roamingSettings()->repeaterRangeCheckCount());
  setRoamingStartCondition(ext->roamingSettings()->roamingStartCondition());
  enableRepeaterCheckNotification(ext->roamingSettings()->notificationEnabled());
  setRepeaterCheckNumNotifications(ext->roamingSettings()->notificationCount());

  // Encode other settings
  enableKeepLastCaller(ext->keepLastCallerEnabled());
  setSTEType(ext->steType());
  setSTEFrequency(ext->steFrequency());
  setTBSTFrequency(ext->tbstFrequency());

  return true;
}

bool
D578UVCodeplug::GeneralSettingsElement::updateConfig(Context &ctx, const ErrorStack &err) {
  if (! AnytoneCodeplug::GeneralSettingsElement::updateConfig(ctx, err))
    return false;

  ctx.config()->settings()->setTOT(transmitTimeout());

  // decode tone settings
  ctx.config()->settings()->tone()->setChannelIdle(
    idleChannelTone() ? Channel::Type::DMR : Channel::Type::None
  );

  // decode GNSS settings
  ctx.config()->settings()->gnss()->setUnits(
        this->gpsUnitsImperial() ? GNSSSettings::Units::Archaic :
                                   GNSSSettings::Units::Metric);

  // decode DMR settings
  ctx.config()->settings()->dmr()->setGroupCallHangTime(this->groupCallHangTime());
  ctx.config()->settings()->dmr()->setPrivateCallHangTime(this->privateCallHangTime());
  ctx.config()->smsExtension()->setFormat(this->smsFormat());

  // Handle D578UV specific extension
  AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension();
  if (nullptr == ext) {
    ext = new AnytoneSettingsExtension();
    ctx.config()->settings()->setAnytoneExtension(ext);
  }

  // Decode key settings
  ext->keySettings()->enableKnobLock(this->knobLock());
  ext->keySettings()->enableKeypadLock(this->keypadLock());
  ext->keySettings()->enableSideKeysLock(this->sidekeysLock());
  ext->keySettings()->enableForcedKeyLock(this->keyLockForced());
  ext->keySettings()->setFuncKey3Short(funcKey3Short());
  ext->keySettings()->setFuncKey3Long(funcKey3Long());
  ext->keySettings()->setFuncKey4Short(funcKey4Short());
  ext->keySettings()->setFuncKey4Long(funcKey4Long());
  ext->keySettings()->setFuncKey5Short(funcKey5Short());
  ext->keySettings()->setFuncKey5Long(funcKey5Long());
  ext->keySettings()->setFuncKey6Short(funcKey6Short());
  ext->keySettings()->setFuncKey6Long(funcKey6Long());
  ext->keySettings()->setFuncKeyDShort(funcKeyDShort());
  ext->keySettings()->setFuncKeyDLong(funcKeyDLong());

  // Decode display settings
  ext->displaySettings()->setCallColor(this->callDisplayColor());
  ext->displaySettings()->setLanguage(this->language());
  ext->displaySettings()->enableShowChannelNumber(this->displayChannelNumber());
  ext->displaySettings()->enableShowContact(this->showCurrentContact());
  ext->displaySettings()->setStandbyTextColor(this->standbyTextColor());
  ext->displaySettings()->enableShowLastHeard(this->showLastHeard());
  ext->displaySettings()->setChannelNameColor(this->channelNameColor());
  ext->displaySettings()->enableShowContact(this->showCurrentContact());
  ext->displaySettings()->setBacklightDurationTX(this->txBacklightDuration());

  // Decode menu settings
  ext->menuSettings()->enableSeparator(this->separateDisplay());

  // Decode auto-repeater settings
  ext->autoRepeaterSettings()->setDirectionB(autoRepeaterDirectionB());
  ext->autoRepeaterSettings()->setVHFMin(this->autoRepeaterMinFrequencyVHF());
  ext->autoRepeaterSettings()->setVHFMax(this->autoRepeaterMaxFrequencyVHF());
  ext->autoRepeaterSettings()->setUHFMin(this->autoRepeaterMinFrequencyUHF());
  ext->autoRepeaterSettings()->setUHFMax(this->autoRepeaterMaxFrequencyUHF());

  // Encode dmr settings
  ext->dmrSettings()->setWakeHeadPeriod(this->wakeHeadPeriod());
  ext->dmrSettings()->enableFilterOwnID(this->filterOwnID());
  ext->dmrSettings()->setMonitorSlotMatch(this->monitorSlotMatch());
  ext->dmrSettings()->enableMonitorColorCodeMatch(this->monitorColorCodeMatch());
  ext->dmrSettings()->enableMonitorIDMatch(this->monitorIDMatch());
  ext->dmrSettings()->enableMonitorTimeSlotHold(this->monitorTimeSlotHold());

  // Encode GPS settings
  ext->gpsSettings()->setTimeZone(this->gpsTimeZone());
  ext->gpsSettings()->enablePositionReporting(this->gpsMessageEnabled());
  ext->gpsSettings()->setUpdatePeriod(this->gpsUpdatePeriod());

  // Encode ranging/roaming settings
  ext->roamingSettings()->setAutoRoamPeriod(this->autoRoamPeriod());
  ext->roamingSettings()->setAutoRoamDelay(this->autoRoamDelay());
  ext->roamingSettings()->enableRepeaterRangeCheck(this->repeaterRangeCheck());
  ext->roamingSettings()->setRepeaterCheckInterval(this->repeaterRangeCheckInterval());
  ext->roamingSettings()->setRepeaterRangeCheckCount(this->repeaterRangeCheckCount());
  ext->roamingSettings()->setRoamingStartCondition(this->roamingStartCondition());
  ext->roamingSettings()->enableNotification(this->repeaterCheckNotification());
  ext->roamingSettings()->setNotificationCount(this->repeaterCheckNumNotifications());

  // Decode other settings
  ext->enableKeepLastCaller(this->keepLastCaller());
  ext->setSTEType(this->steType());
  ext->setSTEFrequency(this->steFrequency());
  ext->setTBSTFrequency(this->tbstFrequency());

  return true;
}

bool
D578UVCodeplug::GeneralSettingsElement::linkSettings(RadioSettings *settings, Context &ctx, const ErrorStack &err) {
  if (! AnytoneCodeplug::GeneralSettingsElement::linkSettings(settings, ctx, err))
    return false;

  AnytoneSettingsExtension *ext = settings->anytoneExtension();

  if (0xff != priorityZoneAIndex()) {
    if (! ctx.has<Zone>(priorityZoneAIndex())) {
      errMsg(err) << "Cannot link priority zone A index " << priorityZoneAIndex()
                  << ": Zone with that index not defined.";
      return false;
    }
    ext->bootSettings()->priorityZoneA()->set(ctx.get<Zone>(priorityZoneAIndex()));
  }
  if (0xff != priorityZoneBIndex()) {
    if (! ctx.has<Zone>(priorityZoneBIndex())) {
      errMsg(err) << "Cannot link priority zone B index " << priorityZoneBIndex()
                  << ": Zone with that index not defined.";
      return false;
    }
    ext->bootSettings()->priorityZoneB()->set(ctx.get<Zone>(priorityZoneBIndex()));
  }
  if (ctx.has<RoamingZone>(defaultRoamingZoneIndex())) {
    ext->roamingSettings()->defaultZone()->set(ctx.get<RoamingZone>(this->defaultRoamingZoneIndex()));
  }

  return true;
}


/* ******************************************************************************************** *
 * Implementation of D578UVCodeplug::ExtendedSettingsElement
 * ******************************************************************************************** */
D578UVCodeplug::ExtendedSettingsElement::ExtendedSettingsElement(uint8_t *ptr, unsigned int size)
  : AnytoneCodeplug::ExtendedSettingsElement(ptr, size)
{
  // pass...
}

D578UVCodeplug::ExtendedSettingsElement::ExtendedSettingsElement(uint8_t *ptr)
  : AnytoneCodeplug::ExtendedSettingsElement(ptr, size())
{
  // pass...
}


void
D578UVCodeplug::ExtendedSettingsElement::clear() {
  AnytoneCodeplug::ExtendedSettingsElement::clear();
}


AnytoneDMRSettingsExtension::TalkerAliasSource
D578UVCodeplug::ExtendedSettingsElement::talkerAliasSource() const {
  return (AnytoneDMRSettingsExtension::TalkerAliasSource)getUInt8(Offset::talkerAliasDisplay());
}
void
D578UVCodeplug::ExtendedSettingsElement::setTalkerAliasSource(AnytoneDMRSettingsExtension::TalkerAliasSource mode) {
  setUInt8(Offset::talkerAliasDisplay(), (unsigned)mode);
}


DMRSettings::TalkerAliasEncoding
D578UVCodeplug::ExtendedSettingsElement::talkerAliasEncoding() const {
  switch ((TalkerAliasEncoding)getUInt8(Offset::talkerAliasEncoding())) {
  case TalkerAliasEncoding::ISO7: return DMRSettings::TalkerAliasEncoding::Iso7;
  case TalkerAliasEncoding::ISO8: return DMRSettings::TalkerAliasEncoding::Iso8;
  case TalkerAliasEncoding::Unicode: return DMRSettings::TalkerAliasEncoding::Unicode;
  }
  return DMRSettings::TalkerAliasEncoding::Iso8;
}
void
D578UVCodeplug::ExtendedSettingsElement::setTalkerAliasEncoding(DMRSettings::TalkerAliasEncoding enc) {
  switch (enc) {
  case DMRSettings::TalkerAliasEncoding::Iso7:
    setUInt8(Offset::talkerAliasEncoding(), (unsigned)TalkerAliasEncoding::ISO7);
    break;
  case DMRSettings::TalkerAliasEncoding::Iso8:
    setUInt8(Offset::talkerAliasEncoding(), (unsigned)TalkerAliasEncoding::ISO8);
    break;
  case DMRSettings::TalkerAliasEncoding::Unicode:
    setUInt8(Offset::talkerAliasEncoding(), (unsigned)TalkerAliasEncoding::Unicode);
    break;
  }
}


bool
D578UVCodeplug::ExtendedSettingsElement::weatherAlarmEnabled() const {
  return 0 != getUInt8(Offset::weatherAlarm());
}

void
D578UVCodeplug::ExtendedSettingsElement::enableWeatherAlarm(bool enabled) {
  setUInt8(Offset::weatherAlarm(), enabled ? 0x01 : 0x00);
}


bool
D578UVCodeplug::ExtendedSettingsElement::repeaterEnabled() const {
  return 0 != getUInt8(Offset::repeater());
}

void
D578UVCodeplug::ExtendedSettingsElement::enableRepeater(bool enabled) {
  setUInt8(Offset::repeater(), enabled ? 0x01 : 0x00);
}


AnytoneAudioSettingsExtension::Speaker
D578UVCodeplug::ExtendedSettingsElement::speaker() const {
  switch ((Speaker) getUInt8(Offset::speakers())) {
  case Speaker::Microphone: return AnytoneAudioSettingsExtension::Speaker::Handset;
  case Speaker::Radio: return AnytoneAudioSettingsExtension::Speaker::Radio;
  case Speaker::Both: return AnytoneAudioSettingsExtension::Speaker::Both;
  }
  return AnytoneAudioSettingsExtension::Speaker::Radio;
}

void
D578UVCodeplug::ExtendedSettingsElement::setSpeaker(AnytoneAudioSettingsExtension::Speaker speaker) {
  switch (speaker) {
  case AnytoneAudioSettingsExtension::Speaker::Handset:
    setUInt8(Offset::speakers(), (unsigned)Speaker::Microphone);
    break;
  case AnytoneAudioSettingsExtension::Speaker::Radio:
    setUInt8(Offset::speakers(), (unsigned)Speaker::Radio);
    break;
  case AnytoneAudioSettingsExtension::Speaker::Both:
    setUInt8(Offset::speakers(), (unsigned)Speaker::Both);
    break;
  }
}


AnytoneAudioSettingsExtension::HandsetSpeakerSource
D578UVCodeplug::ExtendedSettingsElement::micSpeakerSource() const {
  switch ((SpeakerSource) getUInt8(Offset::micSpeakerSource())) {
  case SpeakerSource::MainChannel:
    return AnytoneAudioSettingsExtension::HandsetSpeakerSource::MainChannel;
  case SpeakerSource::SubChannel:
    return AnytoneAudioSettingsExtension::HandsetSpeakerSource::SubChannel;
  }
  return AnytoneAudioSettingsExtension::HandsetSpeakerSource::MainChannel;
}

void
D578UVCodeplug::ExtendedSettingsElement::setMicSpeakerSource(AnytoneAudioSettingsExtension::HandsetSpeakerSource source) {
  switch (source) {
  case AnytoneAudioSettingsExtension::HandsetSpeakerSource::MainChannel:
    setUInt8(Offset::micSpeakerSource(), (unsigned) SpeakerSource::MainChannel);
    break;
  case AnytoneAudioSettingsExtension::HandsetSpeakerSource::SubChannel:
    setUInt8(Offset::micSpeakerSource(), (unsigned) SpeakerSource::SubChannel);
    break;
  }
}


GNSSSettings::Systems
D578UVCodeplug::ExtendedSettingsElement::gnss() const {
  switch ((GPSMode)getUInt8(Offset::gpsMode())) {
  case GPSMode::GPS: return GNSSSettings::System::GPS;
  case GPSMode::Beidou: return GNSSSettings::System::Beidou;
  case GPSMode::GPS_Beidou: return GNSSSettings::System::GPS | GNSSSettings::System::Beidou;
  }
  return GNSSSettings::System::GPS;
}

void
D578UVCodeplug::ExtendedSettingsElement::setGNSS(GNSSSettings::Systems mode) {
  if (mode.testFlag(GNSSSettings::System::GPS))
    setUInt8(Offset::gpsMode(), (unsigned)GPSMode::GPS);
  if (mode.testFlag(GNSSSettings::System::Beidou))
    setUInt8(Offset::gpsMode(), (unsigned)GPSMode::Beidou);
  if (mode.testFlags(GNSSSettings::System::GPS|GNSSSettings::System::Beidou))
    setUInt8(Offset::gpsMode(), (unsigned)GPSMode::GPS_Beidou);
}


bool
D578UVCodeplug::ExtendedSettingsElement::bluetoothPTTLatch() const {
  return getUInt8(Offset::btPTTLatch());
}
void
D578UVCodeplug::ExtendedSettingsElement::enableBluetoothPTTLatch(bool enable) {
  setUInt8(Offset::btPTTLatch(), enable ? 0x01 : 0x00);
}

bool
D578UVCodeplug::ExtendedSettingsElement::infiniteBluetoothPTTSleepDelay() const {
  return  bluetoothPTTSleepDelay().isNull();
}
Interval
D578UVCodeplug::ExtendedSettingsElement::bluetoothPTTSleepDelay() const {
  return Interval::fromMinutes(getUInt8(Offset::btPTTSleepDelay()));
}
void
D578UVCodeplug::ExtendedSettingsElement::setBluetoothPTTSleepDelay(Interval delay) {
  unsigned int t = std::min(Limit::maxBluetoothPTTSleepDelay(), (unsigned int)delay.minutes());
  setUInt8(Offset::btPTTSleepDelay(), t);
}
void
D578UVCodeplug::ExtendedSettingsElement::setInfiniteBluetoothPTTSleepDelay() {
  setBluetoothPTTSleepDelay(Interval::fromMinutes(0));
}


AnytoneSettingsExtension::FanControl
D578UVCodeplug::ExtendedSettingsElement::fanControl() const {
  switch ((FanControl) getUInt8(Offset::fanControl())) {
  case FanControl::PTT: return AnytoneSettingsExtension::FanControl::PTT;
  case FanControl::Temperature: return AnytoneSettingsExtension::FanControl::Temperature;
  case FanControl::Both: return AnytoneSettingsExtension::FanControl::Both;
  }
  return AnytoneSettingsExtension::FanControl::PTT;
}

void
D578UVCodeplug::ExtendedSettingsElement::setFanControl(AnytoneSettingsExtension::FanControl ctrl) {
  switch (ctrl) {
  case AnytoneSettingsExtension::FanControl::PTT:
    setUInt8(Offset::fanControl(), (unsigned) FanControl::PTT);
    break;
  case AnytoneSettingsExtension::FanControl::Temperature:
    setUInt8(Offset::fanControl(), (unsigned) FanControl::Temperature);
    break;
  case AnytoneSettingsExtension::FanControl::Both:
    setUInt8(Offset::fanControl(), (unsigned) FanControl::Both);
    break;
  }
}


unsigned int
D578UVCodeplug::ExtendedSettingsElement::weatherChannelIndex() const {
  return getUInt8(Offset::weatherChannelIndex());
}

void
D578UVCodeplug::ExtendedSettingsElement::setWeatherChannelIndex(unsigned int idx) {
  setUInt8(Offset::weatherChannelIndex(), std::min(Limit::maxWeatherChannelIndex(), idx));
}


bool
D578UVCodeplug::ExtendedSettingsElement::infiniteManDialGroupCallHangTime() const {
  return manDialGroupCallHangTime().isNull();
}

Interval
D578UVCodeplug::ExtendedSettingsElement::manDialGroupCallHangTime() const {
  unsigned int t = getUInt8(Offset::manGrpCallHangTime())+1;
  if (t<=30)
    return Interval::fromSeconds(t);
  else if (31==t)
    return Interval::fromMinutes(30);
  return Interval();
}

void
D578UVCodeplug::ExtendedSettingsElement::setManDialGroupCallHangTime(Interval dur) {
  unsigned int t = dur.seconds();
  if (t > 30)
    t = 31;  // = 30min
  else if (0 == t)
    t = 32;  // = infinite
  setUInt8(Offset::manGrpCallHangTime(), t-1);
}

void
D578UVCodeplug::ExtendedSettingsElement::setManDialGroupCallHangTimeInfinite() {
  setManDialGroupCallHangTime(Interval::fromSeconds(0));
}


bool
D578UVCodeplug::ExtendedSettingsElement::infiniteManDialPrivateCallHangTime() const {
  return manDialPrivateCallHangTime().isNull();
}

Interval
D578UVCodeplug::ExtendedSettingsElement::manDialPrivateCallHangTime() const {
  unsigned int t = getUInt8(Offset::manPrivCallHangTime())+1;
  if (t<=30)
    return Interval::fromSeconds(t);
  else if (31==t)
    return Interval::fromMinutes(30);
  return Interval();
}

void
D578UVCodeplug::ExtendedSettingsElement::setManDialPrivateCallHangTime(Interval dur) {
  unsigned int t = dur.seconds();
  if (t > 30)
    t = 31;  // = 30min
  else if (0 == t)
    t = 32;  // = infinite
  setUInt8(Offset::manPrivCallHangTime(), t-1);
}

void
D578UVCodeplug::ExtendedSettingsElement::setManDialPrivateCallHangTimeInfinite() {
  setManDialPrivateCallHangTime(Interval::fromSeconds(0));
}


AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::ExtendedSettingsElement::chKnobShortPressFunction() const {
  return KeyFunction::decode(getUInt8(Offset::chKnobShortPressFunction()));
}

void
D578UVCodeplug::ExtendedSettingsElement::setChKnobShortPressFunction(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::chKnobShortPressFunction(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::ExtendedSettingsElement::chKnobLongPressFunction() const {
  return KeyFunction::decode(getUInt8(Offset::chKnobLongPressFunction()));
}

void
D578UVCodeplug::ExtendedSettingsElement::setChKnobLongPressFunction(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::chKnobLongPressFunction(), KeyFunction::encode(func));
}

AnytoneDisplaySettingsExtension::Color
D578UVCodeplug::ExtendedSettingsElement::channelBNameColor() const {
  return NameColor::decode(getUInt8(Offset::channelBNameColor()));
}

void
D578UVCodeplug::ExtendedSettingsElement::setChannelBNameColor(AnytoneDisplaySettingsExtension::Color color) {
  setUInt8(Offset::channelBNameColor(), NameColor::encode(color));
}


AnytoneDMRSettingsExtension::EncryptionType
D578UVCodeplug::ExtendedSettingsElement::encryption() const {
  return (AnytoneDMRSettingsExtension::EncryptionType)getUInt8(Offset::encryptionType());
}
void
D578UVCodeplug::ExtendedSettingsElement::setEncryption(AnytoneDMRSettingsExtension::EncryptionType mode) {
  setUInt8(Offset::encryptionType(), (unsigned int)mode);
}


bool
D578UVCodeplug::ExtendedSettingsElement::professionalMode() const {
  return 1 == getUInt8(Offset::uiMode());
}

void
D578UVCodeplug::ExtendedSettingsElement::enableProfessionalMode(bool enable) {
  setUInt8(Offset::uiMode(), enable ? 0x01 : 0x00);
}


Interval
D578UVCodeplug::ExtendedSettingsElement::steDuration() const {
  return Interval::fromMilliseconds((getUInt8(Offset::steDuration())+1)*10);
}
void
D578UVCodeplug::ExtendedSettingsElement::setSTEDuration(Interval dur) {
  unsigned int t = std::max(10U, std::min(1000U, (unsigned int)dur.milliseconds()));
  setUInt8(Offset::steDuration(), t/10-1);
}


AnytoneAudioSettingsExtension::HandsetType
D578UVCodeplug::ExtendedSettingsElement::micType() const {
  switch ((MicType)getUInt8(Offset::micType())) {
  case MicType::AnyTone: return AnytoneAudioSettingsExtension::HandsetType::Anytone;
  case MicType::Generic: return AnytoneAudioSettingsExtension::HandsetType::Generic;
  }
  return AnytoneAudioSettingsExtension::HandsetType::Anytone;
}

void
D578UVCodeplug::ExtendedSettingsElement::setMicType(AnytoneAudioSettingsExtension::HandsetType type) {
  switch (type) {
  case AnytoneAudioSettingsExtension::HandsetType::Anytone:
    setUInt8(Offset::micType(), (unsigned) MicType::AnyTone);
    break;
  case AnytoneAudioSettingsExtension::HandsetType::Generic:
    setUInt8(Offset::micType(), (unsigned) MicType::Generic);
    break;
  }
}


AnytoneDisplaySettingsExtension::Color
D578UVCodeplug::ExtendedSettingsElement::zoneANameColor() const {
  return NameColor::decode(getUInt8(Offset::zoneANameColor()));
}

void
D578UVCodeplug::ExtendedSettingsElement::setZoneANameColor(AnytoneDisplaySettingsExtension::Color color) {
  setUInt8(Offset::zoneANameColor(), NameColor::encode(color));
}

AnytoneDisplaySettingsExtension::Color
D578UVCodeplug::ExtendedSettingsElement::zoneBNameColor() const {
  return NameColor::decode(getUInt8(Offset::zoneBNameColor()));
}

void
D578UVCodeplug::ExtendedSettingsElement::setZoneBNameColor(AnytoneDisplaySettingsExtension::Color color) {
  setUInt8(Offset::zoneBNameColor(), NameColor::encode(color));
}


bool
D578UVCodeplug::ExtendedSettingsElement::resetAutoShutdownOnCall() const {
  return 0x00 != getUInt8(Offset::autoShutdownMode());
}

void
D578UVCodeplug::ExtendedSettingsElement::enableResetAutoShutdownOnCall(bool enable) {
  setUInt8(Offset::autoShutdownMode(), enable ? 0x01 : 0x00);
}


bool
D578UVCodeplug::ExtendedSettingsElement::showColorCode() const {
  return getBit(Offset::displayColorCode());
}

void
D578UVCodeplug::ExtendedSettingsElement::enableShowColorCode(bool enable) {
  setBit(Offset::displayColorCode(), enable);
}

bool
D578UVCodeplug::ExtendedSettingsElement::showTimeSlot() const {
  return getBit(Offset::displayTimeSlot());
}

void
D578UVCodeplug::ExtendedSettingsElement::enableShowTimeSlot(bool enable) {
  setBit(Offset::displayTimeSlot(), enable);
}

bool
D578UVCodeplug::ExtendedSettingsElement::showChannelType() const {
  return getBit(Offset::displayChannelType());
}

void
D578UVCodeplug::ExtendedSettingsElement::enableShowChannelType(bool enable) {
  setBit(Offset::displayChannelType(), enable);
}


bool
D578UVCodeplug::ExtendedSettingsElement::fmIdleTone() const {
  return 0x01 == getUInt8(Offset::fmIdleTone());
}

void
D578UVCodeplug::ExtendedSettingsElement::enableFMIdleTone(bool enable) {
  setUInt8(Offset::fmIdleTone(), enable ? 0x01 : 0x00);
}


AnytoneDisplaySettingsExtension::DateFormat
D578UVCodeplug::ExtendedSettingsElement::dateFormat() const {
  return (AnytoneDisplaySettingsExtension::DateFormat)getUInt8(Offset::dateFormat());
}

void
D578UVCodeplug::ExtendedSettingsElement::setDateFormat(AnytoneDisplaySettingsExtension::DateFormat format) {
  setUInt8(Offset::dateFormat(), (unsigned int)format);
}


Level
D578UVCodeplug::ExtendedSettingsElement::fmMicGain() const {
  return Level::fromValue(getUInt8(Offset::analogMicGain())+1, Limit::micGain());
}

void
D578UVCodeplug::ExtendedSettingsElement::setFMMicGain(Level gain) {
  setUInt8(Offset::analogMicGain(), gain.mapTo(Limit::micGain())-1);
}


AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::ExtendedSettingsElement::btSK1ShortPressFunction() const {
  return KeyFunction::decode(getUInt8(Offset::btSK1ShortPressFunction()));
}

void
D578UVCodeplug::ExtendedSettingsElement::setBtSK1ShortPressFunction(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::btSK1ShortPressFunction(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::ExtendedSettingsElement::btSK2ShortPressFunction() const {
  return KeyFunction::decode(getUInt8(Offset::btSK2ShortPressFunction()));
}

void
D578UVCodeplug::ExtendedSettingsElement::setBtSK2ShortPressFunction(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::btSK2ShortPressFunction(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::ExtendedSettingsElement::btSK3ShortPressFunction() const {
  return KeyFunction::decode(getUInt8(Offset::btSK3ShortPressFunction()));
}

void
D578UVCodeplug::ExtendedSettingsElement::setBtSK3ShortPressFunction(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::btSK3ShortPressFunction(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::ExtendedSettingsElement::btSK1LongPressFunction() const {
  return KeyFunction::decode(getUInt8(Offset::btSK1LongPressFunction()));
}

void
D578UVCodeplug::ExtendedSettingsElement::setBtSK1LongPressFunction(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::btSK1LongPressFunction(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::ExtendedSettingsElement::btSK2LongPressFunction() const {
  return KeyFunction::decode(getUInt8(Offset::btSK2LongPressFunction()));
}

void
D578UVCodeplug::ExtendedSettingsElement::setBtSK2LongPressFunction(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::btSK2LongPressFunction(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::ExtendedSettingsElement::btSK3LongPressFunction() const {
  return KeyFunction::decode(getUInt8(Offset::btSK3LongPressFunction()));
}

void
D578UVCodeplug::ExtendedSettingsElement::setBtSK3LongPressFunction(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::btSK3LongPressFunction(), KeyFunction::encode(func));
}


unsigned int
D578UVCodeplug::ExtendedSettingsElement::btHandsetMicGain() const {
  return 2*(getUInt8(Offset::btHSMicGain())+1);
}

void
D578UVCodeplug::ExtendedSettingsElement::setBtHandsetMicGain(unsigned int gain) {
  gain = std::min(10U, std::max(1U, gain));
  setUInt8(Offset::btHSMicGain(), (gain-1)/2);
}


Interval
D578UVCodeplug::ExtendedSettingsElement::btHandsetBacklightDuration() const {
  unsigned int val = getUInt8(Offset::btHSBacklightDuration());

  if ((1 <= val) && (5 >= val))
    return Interval::fromSeconds(5 * val);
  else if ((6 <= val) && (10 >= val))
    return Interval::fromMinutes(val-5);
  else if (11 == val)
    return Interval::fromMinutes(15);
  else if (12 == val)
    return Interval::fromMinutes(35);
  else if (13 == val)
    return Interval::fromMinutes(45);
  else if (14 == val)
    return Interval::fromMinutes(60);

  return Interval::infinity();
}

void
D578UVCodeplug::ExtendedSettingsElement::setBtHandsetBacklightDuration(Interval dur) {
  if (dur.seconds() < 5) setUInt8(Offset::btHSBacklightDuration(), 1);
  else if (dur.seconds() < 30) setUInt8(Offset::btHSBacklightDuration(), dur.seconds()/5);
  else if (dur.minutes() < 1) setUInt8(Offset::btHSBacklightDuration(), 6);
  else if (dur.minutes() < 10) setUInt8(Offset::btHSBacklightDuration(), 5+dur.minutes());
  else if (dur.minutes() <= 15) setUInt8(Offset::btHSBacklightDuration(), 11);
  else if (dur.minutes() <= 35) setUInt8(Offset::btHSBacklightDuration(), 12);
  else if (dur.minutes() <= 45) setUInt8(Offset::btHSBacklightDuration(), 13);
  else if (dur.minutes() <= 60) setUInt8(Offset::btHSBacklightDuration(), 14);
  else setUInt8(Offset::btHSBacklightDuration(), 0);
}


AnytoneKeySettingsExtension::UpDownKeyFunction
D578UVCodeplug::ExtendedSettingsElement::micUpDownKeyFunction() const {
  switch ((UpDownKeyFunction)getUInt8(Offset::upDownKeyFunction())) {
  case UpDownKeyFunction::Channel: return AnytoneKeySettingsExtension::UpDownKeyFunction::Channel;
  case UpDownKeyFunction::Volume: return AnytoneKeySettingsExtension::UpDownKeyFunction::Volume;
  }
  return AnytoneKeySettingsExtension::UpDownKeyFunction::Channel;
}

void
D578UVCodeplug::ExtendedSettingsElement::setMicUpDownKeyFunction(AnytoneKeySettingsExtension::UpDownKeyFunction func) {
  switch (func) {
  case AnytoneKeySettingsExtension::UpDownKeyFunction::Channel:
    setUInt8(Offset::upDownKeyFunction(), (unsigned)UpDownKeyFunction::Channel);
    break;
  case AnytoneKeySettingsExtension::UpDownKeyFunction::Volume:
    setUInt8(Offset::upDownKeyFunction(), (unsigned)UpDownKeyFunction::Volume);
    break;
  }
}


bool
D578UVCodeplug::ExtendedSettingsElement::totNotification() const {
  return 0x00 != getUInt8(Offset::totNotification());
}

void
D578UVCodeplug::ExtendedSettingsElement::enableTOTNotification(bool enable) {
  setUInt8(Offset::totNotification(), enable ? 0x01 : 0x00);
}


bool
D578UVCodeplug::ExtendedSettingsElement::gpsRoaming() const {
  return 0x00 != getUInt8(Offset::gpsRoaming());
}

void
D578UVCodeplug::ExtendedSettingsElement::enableGPSRoaming(bool enable) {
  setUInt8(Offset::gpsRoaming(), enable ? 0x01 : 0x00);
}


AnytoneRepeaterSettingsExtension::ColorCode
D578UVCodeplug::ExtendedSettingsElement::repColorCodeMatch() const {
  switch ((RepeaterColorCodeMatch)getUInt8(Offset::repeaterColorCode())) {
  case RepeaterColorCodeMatch::None: return AnytoneRepeaterSettingsExtension::ColorCode::Ignored;
  case RepeaterColorCodeMatch::VFO_A: return AnytoneRepeaterSettingsExtension::ColorCode::VFOA;
  case RepeaterColorCodeMatch::VFO_B: return AnytoneRepeaterSettingsExtension::ColorCode::VFOB;
  }
  return AnytoneRepeaterSettingsExtension::ColorCode::Ignored;
}

void
D578UVCodeplug::ExtendedSettingsElement::setRepColorCodeMatch(AnytoneRepeaterSettingsExtension::ColorCode mode) {
  switch (mode) {
  case AnytoneRepeaterSettingsExtension::ColorCode::Ignored:
    setUInt8(Offset::repeaterColorCode(), (unsigned) RepeaterColorCodeMatch::None);
    break;
  case AnytoneRepeaterSettingsExtension::ColorCode::VFOA:
    setUInt8(Offset::repeaterColorCode(), (unsigned) RepeaterColorCodeMatch::VFO_A);
    break;
  case AnytoneRepeaterSettingsExtension::ColorCode::VFOB:
    setUInt8(Offset::repeaterColorCode(), (unsigned) RepeaterColorCodeMatch::VFO_B);
    break;
  }
}


AnytoneRepeaterSettingsExtension::TimeSlot
D578UVCodeplug::ExtendedSettingsElement::repTimeSlotAMatch() const {
  switch ((RepeaterTimeSlotMatch)getUInt8(Offset::repeaterATimeslot())) {
  case RepeaterTimeSlotMatch::Any: return AnytoneRepeaterSettingsExtension::TimeSlot::Any;
  case RepeaterTimeSlotMatch::RX1_TX2: return AnytoneRepeaterSettingsExtension::TimeSlot::TS1;
  case RepeaterTimeSlotMatch::RX2_TX1: return AnytoneRepeaterSettingsExtension::TimeSlot::TS2;
  }
  return AnytoneRepeaterSettingsExtension::TimeSlot::Any;
}

void
D578UVCodeplug::ExtendedSettingsElement::setRepTimeSlotAMatch(AnytoneRepeaterSettingsExtension::TimeSlot mode) {
  switch (mode) {
  case AnytoneRepeaterSettingsExtension::TimeSlot::TS1:
    setUInt8(Offset::repeaterATimeslot(), (unsigned) RepeaterTimeSlotMatch::RX1_TX2);
    break;
  case AnytoneRepeaterSettingsExtension::TimeSlot::TS2:
    setUInt8(Offset::repeaterATimeslot(), (unsigned) RepeaterTimeSlotMatch::RX2_TX1);
    break;
  case AnytoneRepeaterSettingsExtension::TimeSlot::Any:
  default:
    setUInt8(Offset::repeaterATimeslot(), (unsigned) RepeaterTimeSlotMatch::Any);
    break;
  }
}

AnytoneRepeaterSettingsExtension::TimeSlot
D578UVCodeplug::ExtendedSettingsElement::repTimeSlotBMatch() const {
  switch ((RepeaterTimeSlotMatch)getUInt8(Offset::repeaterBTimeslot())) {
  case RepeaterTimeSlotMatch::Any: return AnytoneRepeaterSettingsExtension::TimeSlot::Any;
  case RepeaterTimeSlotMatch::RX1_TX2: return AnytoneRepeaterSettingsExtension::TimeSlot::TS1;
  case RepeaterTimeSlotMatch::RX2_TX1: return AnytoneRepeaterSettingsExtension::TimeSlot::TS2;
  }
  return AnytoneRepeaterSettingsExtension::TimeSlot::Any;
}

void
D578UVCodeplug::ExtendedSettingsElement::setRepTimeSlotBMatch(AnytoneRepeaterSettingsExtension::TimeSlot mode) {
  switch (mode) {
  case AnytoneRepeaterSettingsExtension::TimeSlot::TS1:
    setUInt8(Offset::repeaterBTimeslot(), (unsigned) RepeaterTimeSlotMatch::RX1_TX2);
    break;
  case AnytoneRepeaterSettingsExtension::TimeSlot::TS2:
    setUInt8(Offset::repeaterBTimeslot(), (unsigned) RepeaterTimeSlotMatch::RX2_TX1);
    break;
  case AnytoneRepeaterSettingsExtension::TimeSlot::Any:
  default:
    setUInt8(Offset::repeaterBTimeslot(), (unsigned) RepeaterTimeSlotMatch::Any);
    break;
  }
}


unsigned int
D578UVCodeplug::ExtendedSettingsElement::btHandsetSquelch() const {
  if (0 == getUInt8(Offset::btHSRxNoiseReduction()))
    return 0;
  return (getUInt8(Offset::btHSRxNoiseReduction())*10)/9;
}

void
D578UVCodeplug::ExtendedSettingsElement::setBtHandsetSquelch(unsigned int level) {
  level = std::min(10U, level);
  if (1 >= level)
    setUInt8(Offset::btHSRxNoiseReduction(), level);
  else
    setUInt8(Offset::btHSRxNoiseReduction(), (level*9)/10);
}


bool
D578UVCodeplug::ExtendedSettingsElement::btHandsetAutoPowerOffEnabled() const {
  return 0x01 == getUInt8(Offset::btHSShutDown());
}

void
D578UVCodeplug::ExtendedSettingsElement::enableBtHandsetAutoPowerOff(bool enable) {
  setUInt8(Offset::btHSShutDown(), enable ? 0x01 : 0x00);
}


AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::ExtendedSettingsElement::btSK1VeryLongPressFunction() const {
  return KeyFunction::decode(getUInt8(Offset::btSK1VeryLongPressFunction()));
}

void
D578UVCodeplug::ExtendedSettingsElement::setBtSK1VeryLongPressFunction(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::btSK1VeryLongPressFunction(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::ExtendedSettingsElement::btSK2VeryLongPressFunction() const {
  return KeyFunction::decode(getUInt8(Offset::btSK2VeryLongPressFunction()));
}

void
D578UVCodeplug::ExtendedSettingsElement::setBtSK2VeryLongPressFunction(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::btSK2VeryLongPressFunction(), KeyFunction::encode(func));
}

AnytoneKeySettingsExtension::KeyFunction
D578UVCodeplug::ExtendedSettingsElement::btSK3VeryLongPressFunction() const {
  return KeyFunction::decode(getUInt8(Offset::btSK3VeryLongPressFunction()));
}

void
D578UVCodeplug::ExtendedSettingsElement::setBtSK3VeryLongPressFunction(AnytoneKeySettingsExtension::KeyFunction func) {
  setUInt8(Offset::btSK3VeryLongPressFunction(), KeyFunction::encode(func));
}


unsigned int
D578UVCodeplug::ExtendedSettingsElement::btHandsetTxNoiseRedLevel() const {
  if (0 == getUInt8(Offset::btHSTxNoiseReduction()))
    return 0;
  return (getUInt8(Offset::btHSTxNoiseReduction())*10)/9;
}

void
D578UVCodeplug::ExtendedSettingsElement::setBtHandsetTxNoiseRedLevel(unsigned int level) {
  level = std::min(10U, level);
  if (1 >= level)
    setUInt8(Offset::btHSTxNoiseReduction(), level);
  else
    setUInt8(Offset::btHSTxNoiseReduction(), (level*9)/10);
}


unsigned int
D578UVCodeplug::ExtendedSettingsElement::btHandsetVOXLevel() const {
  return ((getUInt8(Offset::btHSVOXLevel())+1)*10)/9;
}

void
D578UVCodeplug::ExtendedSettingsElement::setBtHandsetVOXLevel(unsigned int level) {
  level = std::max(1U, std::min(10U, level));
  setUInt8(Offset::btHSVOXLevel(), ((level-1)*8)/9);
}


Interval
D578UVCodeplug::ExtendedSettingsElement::btHandsetVOXDelay() const {
  return Interval::fromMilliseconds((unsigned int)(1+getUInt8(Offset::btHSVOXDelay()))*500);
}

void
D578UVCodeplug::ExtendedSettingsElement::setBtHandsetVOXDelay(Interval delay) {
  auto ms = std::max(500ull, delay.milliseconds());
  setUInt8(Offset::btHSVOXDelay(), (ms/500)-1);
}


unsigned int
D578UVCodeplug::ExtendedSettingsElement::btHandsetVolumeA() const {
  unsigned int val = getUInt8(Offset::btHSVolumeA());
  if (0 == val)
    return 0;
  return 1+((val-1)*9)/31;
}

void
D578UVCodeplug::ExtendedSettingsElement::setBtHandsetVolumeA(unsigned int vol) {
  setUInt8(Offset::btHSVolumeA(), (std::min(10u, vol)*32)/10);
}

unsigned int
D578UVCodeplug::ExtendedSettingsElement::btHandsetVolumeB() const {
  unsigned int val = getUInt8(Offset::btHSVolumeB());
  if (0 == val)
    return 0;
  return 1+((val-1)*9)/31;
}

void
D578UVCodeplug::ExtendedSettingsElement::setBtHandsetVolumeB(unsigned int vol) {
  setUInt8(Offset::btHSVolumeB(), (std::min(10u, vol)*32)/10);
}


void
D578UVCodeplug::ExtendedSettingsElement::callEndToneMelody(Melody &melody) const {
  QVector<QPair<double, unsigned int>> tones; tones.reserve(5);
  for (int i=0; i<5; i++) {
    double freq = getUInt16_le(Offset::callEndTones()+2*i);
    unsigned int duration = getUInt16_le(Offset::callEndDurations()+2*i);
    if (duration) tones.append({freq, duration});
  }
  melody.infer(tones);
}

void
D578UVCodeplug::ExtendedSettingsElement::setCallEndToneMelody(const Melody &melody) {
  unsigned int n=std::min(5U, (unsigned int)melody.count());
  QVector<QPair<double, unsigned int>> tones = melody.toTones();
  for (unsigned int i=0; i<n; i++) {
    setUInt16_le(Offset::callEndTones()+2*i, tones.at(i).first);
    setUInt16_le(Offset::callEndDurations()+2*i, tones.at(i).second);
  }
}

void
D578UVCodeplug::ExtendedSettingsElement::allCallToneMelody(Melody &melody) const {
  QVector<QPair<double, unsigned int>> tones; tones.reserve(5);
  for (int i=0; i<5; i++) {
    double freq = getUInt16_le(Offset::allCallTones()+2*i);
    unsigned int duration = getUInt16_le(Offset::allCallDurations()+2*i);
    if (duration) tones.append({freq, duration});
  }
  melody.infer(tones);
}

void
D578UVCodeplug::ExtendedSettingsElement::setAllCallToneMelody(const Melody &melody) {
  unsigned int n=std::min(5U, (unsigned int)melody.count());
  QVector<QPair<double, unsigned int>> tones = melody.toTones();
  for (unsigned int i=0; i<n; i++) {
    setUInt16_le(Offset::allCallTones()+2*i, tones.at(i).first);
    setUInt16_le(Offset::allCallDurations()+2*i, tones.at(i).second);
  }
}


bool
D578UVCodeplug::ExtendedSettingsElement::fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err) {
  Q_UNUSED(err);

  if (! flags.updateCodeplug())
    this->clear();

  if (! AnytoneCodeplug::ExtendedSettingsElement::fromConfig(flags, ctx, err))
    return false;

  // Encode tone settings
  enableFMIdleTone(ctx.config()->settings()->tone()->channelIdle().testFlag(Channel::Type::FM));
  setCallEndToneMelody(*ctx.config()->settings()->tone()->callEndMelody());

  // Encode GPS settings
  setGNSS(ctx.config()->settings()->gnss()->systems());
  // Encode audio settings
  if (ctx.config()->settings()->audio()->fmMicGainEnabled())
    setFMMicGain(ctx.config()->settings()->audio()->fmMicGain());
  else
    setFMMicGain(ctx.config()->settings()->audio()->micGain());

  setTalkerAliasEncoding(ctx.config()->settings()->dmr()->talkerAliasEncoding());

  if (nullptr == ctx.config()->settings()->anytoneExtension())
    return true;

  // Get extension
  AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension();

  // Encode DMR settings
  setTalkerAliasSource(ext->dmrSettings()->talkerAliasSource());

  // Some general settings
  setSTEDuration(ext->steDuration());
  enableProfessionalMode(ext->proModeEnabled());
  setFanControl(ext->fan());

  // Key settings
  setChKnobShortPressFunction(ext->keySettings()->funcKnobShort());
  setChKnobLongPressFunction(ext->keySettings()->funcKnobLong());
  setMicUpDownKeyFunction(ext->keySettings()->upDownKeyFunction());

  // Power save settings
  enableResetAutoShutdownOnCall(ext->powerSaveSettings()->resetAutoShutdownOnCall());

  // Encode tone settings
  enableTOTNotification(ext->toneSettings()->totNotification());

  // Encode audio settings
  setSpeaker(ext->audioSettings()->speaker());
  setMicSpeakerSource(ext->audioSettings()->handsetSpeaker());
  setMicType(ext->audioSettings()->handsetType());

  // Encode DMR settings
  setManDialGroupCallHangTime(ext->dmrSettings()->manualGroupCallHangTime());
  setManDialPrivateCallHangTime(ext->dmrSettings()->manualPrivateCallHangTime());
  setEncryption(ext->dmrSettings()->encryption());

  // Encode display settings
  enableShowColorCode(ext->displaySettings()->showColorCode());
  enableShowTimeSlot(ext->displaySettings()->showTimeSlot());
  enableShowChannelType(ext->displaySettings()->showChannelType());
  setDateFormat(ext->displaySettings()->dateFormat());

  // Encode roaming settings
  enableGPSRoaming(ext->roamingSettings()->gpsRoaming());

  // Encode bluetooth settings
  enableBluetoothPTTLatch(ext->bluetoothSettings()->pttLatch());
  setBluetoothPTTSleepDelay(ext->bluetoothSettings()->pttSleepTimer());

  // Encode BT handset settings
  enableBtHandsetAutoPowerOff(ext->bluetoothSettings()->handset()->shutdownEnabled());
  setBtHandsetBacklightDuration(ext->bluetoothSettings()->handset()->backlight());
  setBtHandsetVolumeA(ext->bluetoothSettings()->handset()->volumeLevelA());
  setBtHandsetVolumeB(ext->bluetoothSettings()->handset()->volumeLevelB());
  setBtHandsetMicGain(ext->bluetoothSettings()->handset()->micGain());
  setBtHandsetSquelch(ext->bluetoothSettings()->handset()->squelch());
  setBtHandsetTxNoiseRedLevel(ext->bluetoothSettings()->handset()->txNoiseReduction());
  setBtHandsetVOXLevel(ext->bluetoothSettings()->handset()->voxLevel());
  setBtHandsetVOXDelay(ext->bluetoothSettings()->handset()->voxDelay());
  setBtSK1ShortPressFunction(ext->bluetoothSettings()->handset()->funcKeyAShort());
  setBtSK2ShortPressFunction(ext->bluetoothSettings()->handset()->funcKeyBShort());
  setBtSK3ShortPressFunction(ext->bluetoothSettings()->handset()->funcKeyCShort());
  setBtSK1LongPressFunction(ext->bluetoothSettings()->handset()->funcKeyALong());
  setBtSK2LongPressFunction(ext->bluetoothSettings()->handset()->funcKeyBLong());
  setBtSK3LongPressFunction(ext->bluetoothSettings()->handset()->funcKeyCLong());
  setBtSK1VeryLongPressFunction(ext->bluetoothSettings()->handset()->funcKeyAVeryLong());
  setBtSK2VeryLongPressFunction(ext->bluetoothSettings()->handset()->funcKeyBVeryLong());
  setBtSK3VeryLongPressFunction(ext->bluetoothSettings()->handset()->funcKeyCVeryLong());

  // Encode repeater settings
  enableRepeater(ext->repeaterSettings()->enabled());
  setRepColorCodeMatch(ext->repeaterSettings()->colorCode());
  setRepTimeSlotAMatch(ext->repeaterSettings()->timeSlot());
  setRepTimeSlotBMatch(ext->repeaterSettings()->secTimeSlot());

  return true;
}


bool
D578UVCodeplug::ExtendedSettingsElement::updateConfig(Context &ctx, const ErrorStack &err) {
  Q_UNUSED(ctx); Q_UNUSED(err);

  if (! AnytoneCodeplug::ExtendedSettingsElement::updateConfig(ctx, err))
    return false;

  // Store GPS settings
  ctx.config()->settings()->gnss()->setSystems(this->gnss());

  // Store audio settings
  if (ctx.config()->settings()->audio()->micGain() == fmMicGain())
    ctx.config()->settings()->audio()->disableFMMicGain();
  else
    ctx.config()->settings()->audio()->setFMMicGain(fmMicGain());

  // Store tone settings
  ctx.config()->settings()->tone()->setChannelIdle(
    ctx.config()->settings()->tone()->channelIdle()
      | (fmIdleTone() ? Channel::Type::FM : Channel::Type::None));
  this->callEndToneMelody(*ctx.config()->settings()->tone()->callEndMelody());

  // Store DMR settings
  ctx.config()->config()->settings()->dmr()->setTalkerAliasEncoding(talkerAliasEncoding());

  // Get or add extension if not present
  AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension();
  if (nullptr == ext) {
    ext = new AnytoneSettingsExtension();
    ctx.config()->settings()->setAnytoneExtension(ext);
  }

  // Store DMR settings
  ext->dmrSettings()->setTalkerAliasSource(talkerAliasSource());

  // Some general settings
  ext->setSTEDuration(this->steDuration());
  ext->enableProMode(this->professionalMode());
  ext->setFan(this->fanControl());

  // Some key settings
  ext->keySettings()->setFuncKnobShort(chKnobShortPressFunction());
  ext->keySettings()->setFuncKnobLong(chKnobLongPressFunction());
  ext->keySettings()->setUpDownKeyFunction(micUpDownKeyFunction());

  // Some power-save settings
  ext->powerSaveSettings()->enableResetAutoShutdownOnCall(this->resetAutoShutdownOnCall());

  // Store tone settings
  ext->toneSettings()->enableTOTNotification(this->totNotification());

  // Store FM mic gain separately, if different
  ext->audioSettings()->setSpeaker(speaker());
  ext->audioSettings()->setHandsetSpeaker(micSpeakerSource());
  ext->audioSettings()->setHandsetType(micType());

  // Store display settings
  ext->displaySettings()->enableShowColorCode(this->showColorCode());
  ext->displaySettings()->enableShowTimeSlot(this->showTimeSlot());
  ext->displaySettings()->enableShowChannelType(this->showChannelType());
  ext->displaySettings()->setDateFormat(this->dateFormat());

  // Store some DMR settings
  ext->dmrSettings()->setManualGroupCallHangTime(this->manDialGroupCallHangTime());
  ext->dmrSettings()->setManualPrivateCallHangTime(this->manDialPrivateCallHangTime());
  ext->dmrSettings()->setEncryption(this->encryption());

  // Store roaming settings
  ext->roamingSettings()->enableGPSRoaming(this->gpsRoaming());

  // Store bluetooth settings
  ext->bluetoothSettings()->enablePTTLatch(this->bluetoothPTTLatch());
  ext->bluetoothSettings()->setPTTSleepTimer(this->bluetoothPTTSleepDelay());

  // Store BT handset settings
  ext->bluetoothSettings()->handset()->enableShutdown(this->btHandsetAutoPowerOffEnabled());
  ext->bluetoothSettings()->handset()->setBacklight(this->btHandsetBacklightDuration());
  ext->bluetoothSettings()->handset()->setVolumeLevelA(this->btHandsetVolumeA());
  ext->bluetoothSettings()->handset()->setVolumeLevelB(this->btHandsetVolumeB());
  ext->bluetoothSettings()->handset()->setMicGain(this->btHandsetMicGain());
  ext->bluetoothSettings()->handset()->setSquelch(this->btHandsetSquelch());
  ext->bluetoothSettings()->handset()->setTxNoiseReduction(this->btHandsetTxNoiseRedLevel());
  ext->bluetoothSettings()->handset()->setVoxLevel(this->btHandsetVOXLevel());
  ext->bluetoothSettings()->handset()->setVoxDelay(this->btHandsetVOXDelay());
  ext->bluetoothSettings()->handset()->setFuncKeyAShort(this->btSK1ShortPressFunction());
  ext->bluetoothSettings()->handset()->setFuncKeyBShort(this->btSK2ShortPressFunction());
  ext->bluetoothSettings()->handset()->setFuncKeyCShort(this->btSK3ShortPressFunction());
  ext->bluetoothSettings()->handset()->setFuncKeyALong(this->btSK1LongPressFunction());
  ext->bluetoothSettings()->handset()->setFuncKeyBLong(this->btSK2LongPressFunction());
  ext->bluetoothSettings()->handset()->setFuncKeyCLong(this->btSK3LongPressFunction());
  ext->bluetoothSettings()->handset()->setFuncKeyAVeryLong(this->btSK1VeryLongPressFunction());
  ext->bluetoothSettings()->handset()->setFuncKeyBVeryLong(this->btSK2VeryLongPressFunction());
  ext->bluetoothSettings()->handset()->setFuncKeyCVeryLong(this->btSK3VeryLongPressFunction());

  return true;
}



/* ******************************************************************************************** *
 * Implementation of D578UVCodeplug::HotKeySettingsElement
 * ******************************************************************************************** */
D578UVCodeplug::HotKeySettingsElement::HotKeySettingsElement(uint8_t *ptr, size_t size)
  : AnytoneCodeplug::HotKeySettingsElement(ptr, size)
{
  // pass...
}

D578UVCodeplug::HotKeySettingsElement::HotKeySettingsElement(uint8_t *ptr)
  : AnytoneCodeplug::HotKeySettingsElement(ptr, HotKeySettingsElement::size())
{
  // pass...
}

uint8_t *
D578UVCodeplug::HotKeySettingsElement::hotKeySetting(unsigned int n) const {
  if (n >= Limit::numEntries())
    return nullptr;
  return _data + Offset::hotKeySettings() + n*Offset::betweenHotKeySettings();
}


/* ******************************************************************************************** *
 * Implementation of D578UVCodeplug::AirBandChannelElement
 * ******************************************************************************************** */
D578UVCodeplug::AirBandChannelElement::AirBandChannelElement(uint8_t *ptr, size_t size)
  : Element(ptr, size)
{
  // pass...
}


D578UVCodeplug::AirBandChannelElement::AirBandChannelElement(uint8_t *ptr)
  : Element(ptr, AirBandChannelElement::size())
{
  // pass...
}


void
D578UVCodeplug::AirBandChannelElement::clear() {
  memset(_data, 0, _size);
}


Frequency
D578UVCodeplug::AirBandChannelElement::frequency() const {
  return Frequency::fromHz(((unsigned long long)getBCD8_be(Offset::frequency()))*10);
}

void
D578UVCodeplug::AirBandChannelElement::setFrequency(Frequency freq) {
  setBCD8_be(Offset::frequency(), freq.inHz()/10);
}


QString
D578UVCodeplug::AirBandChannelElement::name() const {
  return readASCII(Offset::name(), Limit::nameLength(), 0x00);
}

void
D578UVCodeplug::AirBandChannelElement::setName(const QString &name) {
  writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);
}


bool
D578UVCodeplug::AirBandChannelElement::encode(AMChannel *am, Context &ctx, const ErrorStack &err) {
  Q_UNUSED(ctx); Q_UNUSED(err);
  if (nullptr == am)
    return false;

  setName(am->name());
  setFrequency(am->rxFrequency());

  return true;
}


AMChannel *
D578UVCodeplug::AirBandChannelElement::decode(Context &ctx, const ErrorStack &err) const {
  Q_UNUSED(ctx); Q_UNUSED(err);

  auto am = new AMChannel();
  am->setName(name());
  am->setRXFrequency(frequency());

  return am;
}



/* ******************************************************************************************** *
 * Implementation of D578UVCodeplug::AirBandBitmapElement
 * ******************************************************************************************** */
D578UVCodeplug::AirBandBitmapElement::AirBandBitmapElement(uint8_t *ptr, size_t size)
  : BitmapElement(ptr, size)
{
  // pass...
}

D578UVCodeplug::AirBandBitmapElement::AirBandBitmapElement(uint8_t *ptr)
  : BitmapElement(ptr, AirBandBitmapElement::size())
{
  // pass...
}


/* ******************************************************************************************** *
 * Implementation of D578UVCodeplug
 * ******************************************************************************************** */
D578UVCodeplug::D578UVCodeplug(const QString &label, QObject *parent)
  : D878UVCodeplug(label, parent)
{
  // pass...
}

D578UVCodeplug::D578UVCodeplug(QObject *parent)
  : D878UVCodeplug("AnyTone AT-D578UV Codeplug", parent)
{
  // pass...
}

bool
D578UVCodeplug::allocateBitmaps() {
  if (! D878UVCodeplug::allocateBitmaps())
    return false;

  image(0).addElement(Offset::airBandChannelBitmap(), AirBandBitmapElement::size());
  image(0).addElement(Offset::airBandScanBitmap(), AirBandBitmapElement::size());

  return true;
}


void
D578UVCodeplug::setBitmaps(Context &ctx) {
  D878UVCodeplug::setBitmaps(ctx);

  AirBandBitmapElement air_band_bitmap(data(Offset::airBandChannelBitmap())),
    air_scan_bitmap(data(Offset::airBandScanBitmap()));
  unsigned int num_am_channels = std::min(Limit::airBandChannels(), ctx.count<AMChannel>());
  air_band_bitmap.clear(); air_band_bitmap.enableFirst(num_am_channels);
  air_scan_bitmap.clear(); air_scan_bitmap.enableFirst(num_am_channels);
}


void
D578UVCodeplug::allocateForEncoding() {
  // First allocate everything common
  D878UVCodeplug::allocateForEncoding();
  this->allocateAirBandChannels();
}

void
D578UVCodeplug::allocateForDecoding() {
  // First allocate everything common
  D878UVCodeplug::allocateForDecoding();
  this->allocateAirBandChannels();
}


void
D578UVCodeplug::allocateHotKeySettings() {
  image(0).addElement(Offset::hotKeySettings(), HotKeySettingsElement::size());
}


Config *
D578UVCodeplug::preprocess(Config *config, const ErrorStack &err) const {
  // Apply base preprocessing
  auto intermediate = Codeplug::preprocess(config, err);
  if (nullptr == intermediate) {
    errMsg(err) << "Cannot apply preprocessing for D578UV.";
    return nullptr;
  }

  // Split A/B zones into two.
  ZoneSplitVisitor splitter;
  if (! splitter.process(intermediate, err)) {
    errMsg(err) << "Split multi-VFO zones.";
    delete intermediate;
    return nullptr;
  }

  // Keep 16bit DMR, 128 bit AES and 256 bit AES keys.
  EncryptionKeyFilterVisitor filter(
    { EncryptionKeyFilterVisitor::Filter(BasicEncryptionKey::staticMetaObject, 16, 16),
      EncryptionKeyFilterVisitor::Filter(AESEncryptionKey::staticMetaObject, 128, 128),
      EncryptionKeyFilterVisitor::Filter(AESEncryptionKey::staticMetaObject, 256, 256),});
  if (! filter.process(intermediate, err)) {
    errMsg(err) << "Cannot remove unsupported exncryption.";
    delete intermediate;
    return nullptr;
  }

  return intermediate;
}


bool
D578UVCodeplug:: encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err) {
  if (! D878UVCodeplug::encodeElements(flags, ctx, err)) {
    errMsg(err) << "Cannot encode elements for D578UV.";
    return false;
  }

  if (! encodeAirBandChannels(flags, ctx, err)) {
    errMsg(err) << "Cannot encode AM channels.";
    return false;
  }

  return true;
}

bool
D578UVCodeplug::createElements(Context &ctx, const ErrorStack &err) {
  if (! D878UVCodeplug::createElements(ctx, err)) {
    errMsg(err) << "Cannot create elements for D578UV.";
    return false;
  }

  if (! createAirBandChannels(ctx, err)) {
    errMsg(err) << "Cannot create AM channels.";
    return false;
  }

  return true;
}


bool
D578UVCodeplug::encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err) {
  Q_UNUSED(flags); Q_UNUSED(err)

  // Encode channels
  for (unsigned int i=0; i<ctx.count<Channel>(); i++) {
    // enable channel
    uint16_t bank = i/Limit::channelsPerBank(), idx = i%Limit::channelsPerBank();
    uint32_t addr = Offset::channelBanks() + bank*Offset::betweenChannelBanks()
                    + idx*ChannelElement::size();
    ChannelElement ch(data(addr));
    ch.fromChannelObj(ctx.get<Channel>(i), ctx);

    ChannelExtensionElement ext(data(addr + Offset::toChannelExtension()));
    ext.clear();
  }
  return true;
}

bool
D578UVCodeplug::createChannels(Context &ctx, const ErrorStack &err) {
  Q_UNUSED(err)

  ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap()));

  // Create channels
  for (uint16_t i=0; i<Limit::numChannels(); i++) {
    // Check if channel is enabled:
    uint16_t bank = i/Limit::channelsPerBank(), idx = i%Limit::channelsPerBank();
    if (! channel_bitmap.isEncoded(i))
      continue;
    ChannelElement ch(data(Offset::channelBanks() + bank*Offset::betweenChannelBanks()
                           + idx*ChannelElement::size()));
    if (Channel *obj = ch.toChannelObj(ctx)) {
      ctx.config()->channelList()->add(obj); ctx.add(obj, i);
    }
  }
  return true;
}

bool
D578UVCodeplug::linkChannels(Context &ctx, const ErrorStack &err) {
  Q_UNUSED(err)

  ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap()));

  // Link channel objects
  for (uint16_t i=0; i<Limit::numChannels(); i++) {
    // Check if channel is enabled:
    uint16_t bank = i/Limit::channelsPerBank(), idx = i%Limit::channelsPerBank();
    if (! channel_bitmap.isEncoded(i))
      continue;
    ChannelElement ch(data(Offset::channelBanks() + bank*Offset::betweenChannelBanks()
                           + idx*ChannelElement::size()));
    if (ctx.has<Channel>(i))
      ch.linkChannelObj(ctx.get<Channel>(i), ctx);
  }

  return true;
}


void
D578UVCodeplug::allocateContacts() {
  /* Allocate contacts */
  ContactBitmapElement contact_bitmap(data(Offset::contactBitmap()));
  unsigned contactCount=0;
  for (uint16_t i=0; i<Limit::numContacts(); i++) {
    if (! contact_bitmap.isEncoded(i))
      continue;
    contactCount++;
    uint32_t bank_addr = Offset::contactBanks() + (i/Limit::contactsPerBank())*Offset::betweenContactBanks();
    uint32_t addr = bank_addr + (i%Limit::contactsPerBank())*Offset::betweenContactBlocks();
    if (!isAllocated(addr, 0)) {
      image(0).addElement(addr, Offset::betweenContactBlocks());
      memset(data(addr), 0x00, Offset::betweenContactBlocks());
    }
  }
  if (contactCount) {
    image(0).addElement(Offset::contactIndex(), align_size(4*contactCount, 16));
    memset(data(Offset::contactIndex()), 0xff, align_size(4*contactCount, 16));
    image(0).addElement(Offset::contactIdTable(), align_size(ContactMapElement::size()*(1+contactCount), 16));
    memset(data(Offset::contactIdTable()), 0xff, align_size(ContactMapElement::size()*(1+contactCount), 16));
  }
}

bool
D578UVCodeplug::encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err) {
  Q_UNUSED(flags); Q_UNUSED(err)

  QVector<DMRContact*> contacts;
  // Encode contacts and also collect id<->index map
  for (unsigned int i=0; i<ctx.count<DigitalContact>(); i++) {
    uint32_t bank_addr = Offset::contactBanks() + (i/Limit::contactsPerBank())*Offset::betweenContactBanks();
    uint32_t addr = bank_addr + (i%Limit::contactsPerBank())*ContactElement::size();
    ContactElement con(data(addr));
    DMRContact *contact = ctx.get<DMRContact>(i);
    if(! con.fromContactObj(contact, ctx))
      return false;
    ((uint32_t *)data(Offset::contactIndex()))[i] = qToLittleEndian(i);
    contacts.append(contact);
  }
  // encode index map for contacts
  std::sort(contacts.begin(), contacts.end(),
            [](DMRContact *a, DMRContact *b) {
    return a->number() < b->number();
  });
  for (int i=0; i<contacts.size(); i++) {
    ContactMapElement el(data(Offset::contactIdTable() + i*ContactMapElement::size()));
    el.setID(contacts[i]->number(), (DMRContact::GroupCall==contacts[i]->type()));
    el.setIndex(ctx.index(contacts[i]));
  }
  return true;
}


void
D578UVCodeplug::allocateGeneralSettings() {
  // override allocation of general settings for D878UV code-plug. General settings are larger!
  image(0).addElement(Offset::settings(), GeneralSettingsElement::size());
  image(0).addElement(Offset::gpsMessages(), DMRAPRSMessageElement::size());
  image(0).addElement(Offset::settingsExtension(), ExtendedSettingsElement::size());

}

bool
D578UVCodeplug::encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {
  Q_UNUSED(err)

  GeneralSettingsElement(data(Offset::settings())).fromConfig(flags, ctx, err);
  DMRAPRSMessageElement(data(Offset::gpsMessages())).fromConfig(flags, ctx);
  ExtendedSettingsElement(data(Offset::settingsExtension())).fromConfig(flags, ctx);
  return true;
}

bool
D578UVCodeplug::decodeGeneralSettings(Context &ctx, const ErrorStack &err) {
  Q_UNUSED(err)

  GeneralSettingsElement(data(Offset::settings())).updateConfig(ctx, err);
  DMRAPRSMessageElement(data(Offset::gpsMessages())).updateConfig(ctx);
  ExtendedSettingsElement(data(Offset::settingsExtension())).updateConfig(ctx);
  return true;
}

bool
D578UVCodeplug::linkGeneralSettings(Context &ctx, const ErrorStack &err) {
  if (! GeneralSettingsElement(data(Offset::settings())).linkSettings(ctx.config()->settings(), ctx, err)) {
    errMsg(err) << "Cannot link general settings extension.";
    return false;
  }

  if (! ExtendedSettingsElement(data(Offset::settingsExtension())).linkConfig(ctx, err)) {
    errMsg(err) << "Cannot link general settings extension.";
    return false;
  }

  return true;
}


bool
D578UVCodeplug::allocateAirBandChannels() {
  // Allocate valid air-band channels
  AirBandBitmapElement bitmap(data(Offset::airBandChannelBitmap()));
  for (unsigned int i=0; i<Limit::airBandChannels(); i++)  {
    if (! bitmap.isEncoded(i))
      continue;
    image(0).addElement(Offset::airBandChannels() + i*AirBandChannelElement::size(),
                        AirBandChannelElement::size());
  }
  // allocate air-band VFO channel
  image(0).addElement(Offset::airBandVFO(), AirBandChannelElement::size());
  return true;
}


bool
D578UVCodeplug::encodeAirBandChannels(const Flags &flags, Context &ctx, const ErrorStack &err) {
  Q_UNUSED(flags);

  AirBandBitmapElement air_band_bitmap(data(Offset::airBandChannelBitmap()));
  for (unsigned int i=0; i<Limit::airBandChannels(); i++) {
    if (! air_band_bitmap.isEncoded(i))
      continue;
    if (! ctx.has<AMChannel>(i)) {
      errMsg(err) << "Cannot resolve AM channel at index " << i << ". Not defined!";
      return false;
    }
    AirBandChannelElement ch(data(Offset::airBandChannels() + i*AirBandChannelElement::size()));
    if (! ch.encode(ctx.get<AMChannel>(i), ctx, err)) {
      errMsg(err) << "Cannot encode AM channel at index " << i << ".";
      return false;
    }
  }

  return true;
}


bool
D578UVCodeplug::createAirBandChannels(Context &ctx, const ErrorStack &err) {
  AirBandBitmapElement air_band_bitmap(data(Offset::airBandChannelBitmap()));
  for (unsigned int i=0; i<Limit::airBandChannels(); i++) {
    if (! air_band_bitmap.isEncoded(i))
      continue;
    AirBandChannelElement ch(data(Offset::airBandChannels() + i*AirBandChannelElement::size()));
    auto am = ch.decode(ctx, err);
    if (nullptr == am) {
      errMsg(err) << "Cannot decode AM channel at index " << i << ".";
      return false;
    }
    ctx.config()->channelList()->add(am);
    ctx.add(am, i);
  }

  return true;
}
