program bbutils_parsespecialcasing;

{$mode objfpc}{$H+}
{$modeswitch advancedrecords}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes, bbutils, sysutils
  { you can add units after this };

const COL_LOWER = 1;
const COL_UPPER = 3;

type TCasingMap = record
  code: string;
  block: string;
  blocklen: integer;
  procedure addMap(codepoint, replacementdata: string);
  function generate(name: string): string;
end;

var line: string;

procedure TCasingMap.addMap(codepoint, replacementdata: string);
var
  replacement: TStringArray;
  j, delta: Integer;
  replacementStr: String;
begin
  if (codepoint = replacementdata) or (replacementdata = '') then exit;

  replacement := strSplit(replacementdata, ' ');
  replacementStr := '';
  for j := 0 to High(replacement) do
    replacementStr += strGetUnicodeCharacter(strtoint('$'+replacement[j]));

  delta := length(replacementStr);
  code += '    $' + codepoint + ': special := $'+ IntToHex((blocklen shl 16) or delta,8)+'; //' + strGetUnicodeCharacter(StrToInt('$' + codepoint)) + ' ' + line + LineEnding;
  for j := 1 to length(replacementStr) do begin
    if blocklen <> 0 then block += ', ';
    inc(blocklen);
    block += '$' + IntToHex(byte(ord(replacementStr[j])), 2);
  end;
end;

function TCasingMap.generate(name: string): string;
begin
  result :=
'function '+name+'(codePoint: integer): string;' + LineEnding +
'const block: array[0..'+IntToStr(blocklen - 1)+'] of byte = ( ' + block + ');' + LineEnding +
'var special: integer;' + LineEnding +
'begin' + LineEnding +
'  special := 0;' + LineEnding +
'  case codePoint of' + LineEnding +
     code +
'  end;' + LineEnding +
'  if special <> 0 then begin setlength(result, special and $FFFF); move(block[special shr 16], result[1], length(result)); end ' +LineEnding +
'  else result := strGetUnicodeCharacter(CodePoint);' + LineEnding +
'end;' + LineEnding;

end;




var upmap, lowmap: TCasingMap;
    replacementStr: string;
    split, replacement: TStringArray;
    i, j: Integer;
    delta: LongInt;


begin
  upmap.blocklen := 0;
  lowmap.blocklen := 0;
  while not EOF do begin
    readln(line);
    if strContains(line, '#') then line := strBefore(line, '#');
    line := strTrimAndNormalize(line);
    if line = '' then continue;

    split := strSplit(line, ';');
    if (length(split) >= 5) and (strTrimAndNormalize(split[4]) <> '') then begin writeln(stderr, 'Skipping: ',line); continue; end;
    for i := 0 to high(split) do
      split[i] := strTrimAndNormalize(split[i]);
    lowmap.addMap(split[0], split[COL_LOWER]);
    upmap.addMap(split[0], split[COL_UPPER]);
  end;

  writeln(upmap.generate('strUpperCaseSpecialUTF8'));
  writeln(lowmap.generate('strLowerCaseSpecialUTF8'));
end.

