uses
  darg,
  derror,
  dansiview,
  dparser,
  dparser_old,
  dtypes,
  dstrnum,
  dutf8,
  dstream,
  dstreamfile,
  dxml_sax;

var
  InputFile: AnsiString;
  OutputFile: AnsiString;
  TextureFile: AnsiString;

type
TFontReader = object(TXmlReader)
private
  FFile: TextFile;
  FSpaces: AnsiString;
  FComma: Boolean;
  FWidth: LongInt;
  FOffset: array[0..1] of LongInt;
  FRect: array[0..3] of LongInt;
  FCode: LongInt;
  FTag: AnsiString;
public
  constructor Init;
  destructor Done; virtual;
  procedure OnDocumentBegin; virtual;
  procedure OnDocumentEnd; virtual;
  procedure OnHeaderBegin(const HeaderTag: TAnsiView); virtual;
  procedure OnHeaderEnd(const HeaderTag: TAnsiView); virtual;
  procedure OnOpenTagBegin(Parser: PXmlParser; const Name: TAnsiView); virtual;
  procedure OnAttribute(Parser: PXmlParser; const Name, Value: TAnsiView); virtual;
  procedure OnOpenTagEnd(Parser: PXmlParser; const Name: TAnsiView); virtual;
  procedure OnContent(Parser: PXmlParser; const Content: TAnsiView); virtual;
  procedure OnCloseTag(Parser: PXmlParser; const Name: TAnsiView); virtual;
end;

constructor TFontReader.Init;
begin
  Assign(FFile, OutputFile);
  Rewrite(FFile);
end;

destructor TFontReader.Done;
begin
  Close(FFile);
end;

procedure TFontReader.OnDocumentBegin;
begin
  Writeln(FFile, '{');
  FSpaces := '    ';
  Writeln(FFile, FSpaces, '"name": "test",');
  Writeln(FFile, FSpaces, '"fonts": [');
  FSpaces := '        ';
  Writeln(FFile, FSpaces, '{');
  FSpaces := '            ';
  Writeln(FFile, FSpaces, '"name": "default",');
  Writeln(FFile, FSpaces, '"texture": "', TextureFile, '",');
  Writeln(FFile, FSpaces, '"letters": [');
  FSpaces := '                ';
  FComma := False;
end;

procedure TFontReader.OnDocumentEnd;
begin
  Writeln(FFile);
  FSpaces := '            ';
  Writeln(FFile, FSpaces, ']');
  FSpaces := '        ';
  Writeln(FFile, FSpaces, '}');
  FSpaces := '    ';
  Writeln(FFile, FSpaces, ']');
  Writeln(FFile, '}');
end;

procedure TFontReader.OnHeaderBegin(const HeaderTag: TAnsiView);
begin
end;

procedure TFontReader.OnHeaderEnd(const HeaderTag: TAnsiView);
begin
end;

procedure TFontReader.OnOpenTagBegin(Parser: PXmlParser; const Name: TAnsiView);
begin
  FTag := Name.ToString;
  if Name = 'Char' then begin
    FWidth := 0;
    FOffset[0] := 0;
    FOffset[1] := 0;
    FRect[0] := 0;
    FRect[1] := 0;
    FRect[2] := 0;
    FRect[3] := 0;
    FCode := 0;
  end;
end;

procedure TFontReader.OnAttribute(Parser: PXmlParser; const Name, Value: TAnsiView);
var
  Unescaped: AnsiString;
  UnescapedP: PAnsiChar;
  L, R: AnsiString;
  I: LongInt;
begin
  if FTag = 'Char' then begin
    if (Name = 'width') or (Name = 'xadvance') then begin
      FWidth := StrToInt(Value.ToString);
    end else if Name = 'offset' then begin
      R := Value.ToString;
      I := 0;
      while ParseBinary(R, ' ', L, R) do begin
        FOffset[I] := StrToInt(L);
        Inc(I);
        if I > 1 then
          Break;
      end;
      FOffset[I] := StrToInt(R);
      Inc(I);
      while I <= 1 do begin
        FOffset[I] := 0;
        Inc(I);
      end;
    end else if Name = 'x' then begin
      FRect[0] := StrToInt(Value.ToString);
    end else if Name = 'y' then begin
      FRect[1] := StrToInt(Value.ToString);
    end else if Name = 'w' then begin
      FRect[2] := StrToInt(Value.ToString);
    end else if Name = 'h' then begin
      FRect[3] := StrToInt(Value.ToString);
    end else if Name = 'xoffset' then begin
      FOffset[0] := StrToInt(Value.ToString);
    end else if Name = 'yoffset' then begin
      FOffset[1] := StrToInt(Value.ToString);
    end else if Name = 'rect' then begin
      R := Value.ToString;
      I := 0;
      while ParseBinary(R, ' ', L, R) do begin
        FRect[I] := StrToInt(L);
        Inc(I);
        if I > 3 then
          Break;
      end;
      FRect[I] := StrToInt(R);
      Inc(I);
      while I <= 3 do begin
        FRect[I] := 0;
        Inc(I);
      end;
    end else if Name = 'code' then begin
      Unescaped := UnescapeHTML(Value);
      UnescapedP := @Unescaped[1];
      FCode := Cardinal(DecodeUTF8CharUnsafe(UnescapedP));
    end else if Name = 'id' then begin
      Unescaped := UnescapeHTML(Value);
      FCode := StrToInt(Unescaped);
    end;
  end;
end;

procedure TFontReader.OnOpenTagEnd(Parser: PXmlParser; const Name: TAnsiView);
begin
  if Name = 'Char' then begin
    if FComma then begin
      Writeln(FFile, ',');
    end else
      FComma := True;
    Writeln(FFile, FSpaces, '{');
    Writeln(FFile, FSpaces, '    "code": ', FCode, ',');
    Writeln(FFile, FSpaces, '    "count": ', 1, ',');
    Writeln(FFile, FSpaces, '    "width": ', FWidth, ',');
    Writeln(FFile, FSpaces, '    "offset": [', FOffset[0], ', ', FOffset[1], '],');
    Writeln(FFile, FSpaces, '    "rect": [', FRect[0], ', ', FRect[1], ', ', FRect[2], ', ', FRect[3], ']');
    Write  (FFile, FSpaces, '}');
  end;
end;

procedure TFontReader.OnContent(Parser: PXmlParser; const Content: TAnsiView);
begin
end;

procedure TFontReader.OnCloseTag(Parser: PXmlParser; const Name: TAnsiView);
begin
  if Name = 'Char' then begin
  end;
end;

procedure Fatal(const Msg: AnsiString);
begin
  Writeln(stderr, 'Fatal Error: ', Msg);
  Halt(1);
end;

var
  Error: TError;
  FontReader: TFontReader;

begin
  Arg('i', 'input', InputFile, '');
  Arg('o', 'output', OutputFile, '');
  Arg('t', 'texture', TextureFile, '');
  ParseArgs;
  if InputFile = '' then
    Fatal('input file is unspecified');
  FontReader.Init;
  if not ParseXml(InputFile, @FontReader, @Error) then
    Fatal('Failed to parse ' + InputFile + ': ' + Error.Msg);
  FontReader.Done;
end.
