// See discussion online at https://github.com/keymanapp/keyman/issues/1285
// Introduced in Keyman 11. Back ported to Keyman 10. If Microsoft introduces
// a fix in a later release, we will need to update this to turn the mitigation
// off for that version. In that situation, users will need to reinstall their
// language profile in order to get the corrected language.
unit Keyman.System.MitigateWin10_1803LanguageInstall;

interface

uses
  Winapi.Windows;

type
  TMitigateWin10_1803 = class
  type
    TLanguageReference = record
      Code: LANGID;
      BCP47: string;
      Name: string;
    end;

    TMitigatedLanguage = record
      OriginalLanguage: TLanguageReference;
      NewLanguage: TLanguageReference;
    end;

    class function IsMitigationRequired(Code: LANGID; var lang: TMitigateWin10_1803.TMitigatedLanguage): Boolean; overload;
    class function IsMitigationRequired(BCP47Tag: string; var lang: TMitigateWin10_1803.TMitigatedLanguage): Boolean; overload;
  end;

implementation

uses
  System.SysUtils;

const
  MitigatedLanguages: array[0..2] of TMitigateWin10_1803.TMitigatedLanguage = (
    (OriginalLanguage: (Code: $005E; BCP47: 'am'; Name: 'Amharic'); NewLanguage: (BCP47: 'gez-Ethi-ET'; Name: 'Geez')),
    (OriginalLanguage: (Code: $0073; BCP47: 'ti'; Name: 'Tigrinya'); NewLanguage: (BCP47: 'gez-Ethi-ET'; Name: 'Geez')),
    (OriginalLanguage: (Code: $005B; BCP47: 'si'; Name: 'Sinhala'); NewLanguage: (Code: $0409; BCP47: 'en-US'; Name: 'English (US)'))
  );

{ TMitigateWin10_1803 }

const
  WINDOWS_10_MAJORVERSION = 10;
  WINDOWS_10_MINORVERSION = 0;
  WINDOWS_10_BUILDNUMBER_WithFailure = 17134;
  WINDOWS_10_BUILDNUMBER_Fixed = 19597;

function IsWindows10_OrGreater_Build(Build: DWORD): BOOL; inline;
var
  osvi: OSVERSIONINFOEXW;
  dwlConditionMask: DWORDLONG;
begin
  // Code pattern comes from Keyman.Winapi.VersionHelpers, but testing also
  // build number so cannot use functions already there. This also makes back-porting
  // to Keyman 10 simpler.
  FillChar(osvi, SizeOf(osvi), 0);
  osvi.dwOSVersionInfoSize := SizeOf(osvi);
  dwlConditionMask := VerSetConditionMask(
    VerSetConditionMask(
    VerSetConditionMask(
      0, VER_MAJORVERSION, VER_GREATER_EQUAL),
         VER_MINORVERSION, VER_GREATER_EQUAL),
         VER_BUILDNUMBER, VER_GREATER_EQUAL);

  osvi.dwMajorVersion := WINDOWS_10_MAJORVERSION;
  osvi.dwMinorVersion := WINDOWS_10_MINORVERSION;
  osvi.dwBuildNumber := Build;

  Result := VerifyVersionInfoW(osvi, VER_MAJORVERSION or VER_MINORVERSION or VER_BUILDNUMBER, dwlConditionMask) <> False;
end;

class function TMitigateWin10_1803.IsMitigationRequired(Code: LANGID; var lang: TMitigateWin10_1803.TMitigatedLanguage): Boolean;
var
  I: Integer;
begin
  if not IsWindows10_OrGreater_Build(WINDOWS_10_BUILDNUMBER_WithFailure) then
    Exit(False);

  if IsWindows10_OrGreater_Build(WINDOWS_10_BUILDNUMBER_Fixed) then
    Exit(False);

  Code := PRIMARYLANGID(Code);
  for I := Low(MitigatedLanguages) to High(MitigatedLanguages) do
    if MitigatedLanguages[I].OriginalLanguage.Code = Code then
    begin
      lang := MitigatedLanguages[I];
      Exit(True);
    end;
  // lang value undefined.
  Result := False;
end;

class function TMitigateWin10_1803.IsMitigationRequired(BCP47Tag: string;
  var lang: TMitigateWin10_1803.TMitigatedLanguage): Boolean;
var
  I: Integer;
begin
  if not IsWindows10_OrGreater_Build(WINDOWS_10_BUILDNUMBER_WithFailure) then
    Exit(False);

  if IsWindows10_OrGreater_Build(WINDOWS_10_BUILDNUMBER_Fixed) then
    Exit(False);

  // We only want to look at the primary language tag for comparison
  // And we assume that the tag has been canonicalized from ISO639-3 first
  BCP47Tag := LowerCase(BCP47Tag);
  I := Pos('-', BCP47Tag);
  if I > 0 then
    BCP47Tag := Copy(BCP47Tag, 1, I-1);

  for I := Low(MitigatedLanguages) to High(MitigatedLanguages) do
    if MitigatedLanguages[I].OriginalLanguage.BCP47 = BCP47Tag then
    begin
      lang := MitigatedLanguages[I];
      Exit(True);
    end;
  // lang value undefined.
  Result := False;
end;

end.
