unit _frmMain;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, dxSkinsCore, dxSkinOffice2016Colorful,
  Vcl.StdCtrls, cxClasses, cxLookAndFeels, dxSkinsForm, Vcl.ExtCtrls,
  dxGDIPlusClasses, cxGraphics, cxControls, cxLookAndFeelPainters,
  dxSkinsdxStatusBarPainter, dxStatusBar, IdIOHandler, IdIOHandlerSocket,
  IdIOHandlerStack, IdSSL, IdSSLOpenSSL, IdBaseComponent, IdComponent,
  IdTCPConnection, IdTCPClient, IdHTTP, _JSONConfig, dxSkinsdxBarPainter, dxBar,
  cxContainer, cxEdit, cxTextEdit, cxMaskEdit, cxDropDownEdit, Vcl.Menus,
  cxButtons, BCEditor.Editor.Base, BCEditor.Editor, dxSkinscxPCPainter,
  dxBarBuiltInMenu, cxPC, System.ImageList, Vcl.ImgList, cxCheckBox;

type
  TfrmMain = class(TForm)
    OpenDialog: TOpenDialog;
    SaveDialog: TSaveDialog;
    Panel1: TPanel;
    dxSkinController: TdxSkinController;
    Label1: TLabel;
    Image: TImage;
    HTTP: TIdHTTP;
    SSLIOHandlerSocketOpenSSL: TIdSSLIOHandlerSocketOpenSSL;
    dxStatusBar: TdxStatusBar;
    dxBarManager: TdxBarManager;
    dxBarManagerBar1: TdxBar;
    dxBarSubItem3: TdxBarSubItem;
    dxBarButton3: TdxBarButton;
    btnSave: TdxBarButton;
    btnSaveAs: TdxBarButton;
    dxBarButton6: TdxBarButton;
    dxBarSubItem1: TdxBarSubItem;
    dxBarButton7: TdxBarButton;
    dxBarSubItem2: TdxBarSubItem;
    tbbtnRun: TdxBarButton;
    Label2: TLabel;
    edtFullURL: TcxTextEdit;
    btnRun: TcxButton;
    EditorContent: TBCEditor;
    cxPageControl1: TcxPageControl;
    tabContent: TcxTabSheet;
    tabHeaders: TcxTabSheet;
    EditorResponseHeaders: TBCEditor;
    Panel2: TPanel;
    dxBarSubItem4: TdxBarSubItem;
    tbbtnStayOnTop: TdxBarButton;
    edtBaseURL: TcxTextEdit;
    Label3: TLabel;
    btnAdd: TcxButton;
    cxImageList: TcxImageList;
    btnDelete: TcxButton;
    comboPresets: TcxComboBox;
    Label4: TLabel;
    btnEdit: TcxButton;
    btnCopyPreset: TcxButton;
    Label5: TLabel;
    EditorURLParams: TBCEditor;
    Label6: TLabel;
    comboMethod: TcxComboBox;
    PageControlParams: TcxPageControl;
    tabURLParams: TcxTabSheet;
    cxTabSheet2: TcxTabSheet;
    Panel3: TPanel;
    EditorExtraHeaders: TBCEditor;
    Panel4: TPanel;
    Label7: TLabel;
    tabPostData: TcxTabSheet;
    EditorPostData: TBCEditor;
    tbbtnAutoOpenLastUsed: TdxBarButton;
    Label8: TLabel;
    Panel5: TPanel;
    comboPostContentType: TcxComboBox;
    cxTabSheet3: TcxTabSheet;
    chkUseBasicAuth: TcxCheckBox;
    Label9: TLabel;
    edtAuthUsername: TcxTextEdit;
    edtAuthPassword: TcxTextEdit;
    Label10: TLabel;
    dxBarButton2: TdxBarButton;
    tbbtnAutoSaveProject: TdxBarButton;
    PopupJsonContent: TdxBarPopupMenu;
    pmnuCutJsonContent: TdxBarButton;
    pmnuCopyJsonContent: TdxBarButton;
    pmnuPasteJsonContent: TdxBarButton;
    pmnuFormatCodeJsonContent: TdxBarButton;
    pmnuUndoJsonContent: TdxBarButton;
    pmnuRedoJsonContent: TdxBarButton;
    pmnuSelectAll: TdxBarButton;
    pmnuCompactJson: TdxBarButton;
    btnCopyFullURL: TcxButton;
    dxBarDockControl1: TdxBarDockControl;
    procedure FormShow(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure dxBarButton6Click(Sender: TObject);
    procedure dxBarButton3Click(Sender: TObject);
    procedure btnSaveAsClick(Sender: TObject);
    procedure dxBarButton7Click(Sender: TObject);
    procedure tbbtnRunClick(Sender: TObject);
    procedure dxBarSubItem3Popup(Sender: TObject);
    procedure btnRunClick(Sender: TObject);
    procedure FormResize(Sender: TObject);
    procedure tbbtnStayOnTopClick(Sender: TObject);
    procedure pmnuCutJsonContentClick(Sender: TObject);
    procedure pmnuPasteJsonContentClick(Sender: TObject);
    procedure pmnuCopyJsonContentClick(Sender: TObject);
    procedure btnAddClick(Sender: TObject);
    procedure btnDeleteClick(Sender: TObject);
    procedure comboPresetsPropertiesChange(Sender: TObject);
    procedure btnSaveClick(Sender: TObject);
    procedure btnEditClick(Sender: TObject);
    procedure btnCopyPresetClick(Sender: TObject);
    procedure edtBaseURLPropertiesChange(Sender: TObject);
    procedure EditorURLParamsChange(Sender: TObject);
    procedure comboMethodPropertiesChange(Sender: TObject);
    procedure tbbtnAutoOpenLastUsedClick(Sender: TObject);
    procedure dxBarButton2Click(Sender: TObject);
    procedure tbbtnAutoSaveProjectClick(Sender: TObject);
    procedure pmnuFormatCodeJsonContentClick(Sender: TObject);
    procedure EditorContentMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure EditorPostDataMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure PopupJsonContentPopup(Sender: TObject);
    procedure pmnuUndoJsonContentClick(Sender: TObject);
    procedure pmnuRedoJsonContentClick(Sender: TObject);
    procedure pmnuSelectAllClick(Sender: TObject);
    procedure pmnuCompactJsonClick(Sender: TObject);
    procedure EditorResponseHeadersMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure EditorURLParamsMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure EditorExtraHeadersMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure btnCopyFullURLClick(Sender: TObject);
  private
    FMainConfig: TJSONConfig;
    FProjectConfig: TJSONConfig;
    FActiveProject: string;
    FActivePreset: string;
    FIsTempProjectLoaded: Boolean;
    FTempProjectFile: string;
    FActiveEditor: TBCEditor;
    FRoamingSavePath: string;
    function GetTempProjectFile: string;
    function GetAppVersion: string;
    procedure UpdateFullURL;
    procedure LoadProject;
    procedure SaveProject;
    procedure SaveProjectAs(const LoadAfterSave: Boolean);
    procedure Run;
    procedure AddPreset(const APresetName: string);
    procedure DeletePreset(const AID: Integer);
    procedure LoadPreset;
    procedure SavePreset;
  public
    { Public-Deklarationen }
  end;

var
  frmMain: TfrmMain;

implementation

{$R *.dfm}

uses System.NetEncoding, System.UITypes, _EncryptStr, JsonDataObjects, Clipbrd, IdGlobal, System.IOUtils;

procedure TfrmMain.tbbtnRunClick(Sender: TObject);
begin
  Run;
end;

procedure TfrmMain.tbbtnStayOnTopClick(Sender: TObject);
begin
  if tbbtnStayOnTop.Down then
  begin
    FormStyle := fsStayOnTop;
  end
  else
  begin
    FormStyle := fsNormal;
  end;
end;

procedure TfrmMain.UpdateFullURL;
var
  i: Integer;
  dmy: string;
  strlist: TStringList;
begin
  edtFullURL.Text := edtBaseURL.Text;
  if EditorURLParams.Lines.Count > 0 then
  begin
    dmy := '?';
    for i := 0 to EditorURLParams.Lines.Count - 1 do
    begin
      if dmy <> '?' then dmy := dmy + '&';
      strlist := TStringList.Create;
      try
        strlist.Text := StringReplace(EditorURLParams.Lines[i], '=', #13#10, [rfReplaceAll]);
        if strlist.Count = 2 then
        begin
          dmy := dmy + TNetEncoding.URL.Encode(strlist[0]) + '=' + TNetEncoding.URL.Encode(strlist[1]);
        end;
      finally
        strlist.Free;
      end;
    end;

    edtFullURL.Text := edtFullURL.Text + dmy;
  end;
end;

procedure TfrmMain.tbbtnAutoOpenLastUsedClick(Sender: TObject);
begin
  FMainConfig.WriteBool('Project', 'AutoOpenLastUsed', tbbtnAutoOpenLastUsed.Down);
end;

procedure TfrmMain.tbbtnAutoSaveProjectClick(Sender: TObject);
begin
  FMainConfig.WriteBool('Project', 'AutoSaveProject', tbbtnAutoSaveProject.Down);
end;

procedure TfrmMain.dxBarButton2Click(Sender: TObject);
begin
  FActiveProject := GetTempProjectFile;
  FIsTempProjectLoaded := True;
  LoadProject;
end;

procedure TfrmMain.dxBarButton3Click(Sender: TObject);
begin
  OpenDialog.InitialDir := FMainConfig.ReadString('Dialogs', 'LastOpenDir', ExtractFilePath(ParamStr(0)));
  if OpenDialog.Execute then
  begin
    FMainConfig.WriteString('Dialogs', 'LastOpenDir', ExtractFilePath(OpenDialog.FileName));
    FActiveProject := OpenDialog.FileName;
    FIsTempProjectLoaded := False;
    LoadProject;
  end;
end;

procedure TfrmMain.AddPreset(const APresetName: string);
begin
  comboPresets.Properties.Items.Add(APresetName);
  comboPresets.ItemIndex := comboPresets.Properties.Items.Count - 1;

  FProjectConfig.WriteString('Presets', 'Presets', comboPresets.Properties.Items.Text);
end;

procedure TfrmMain.btnAddClick(Sender: TObject);
var
  dmy: string;
begin
  if InputQuery('Add Preset', 'Enter a preset name:', dmy) then
  begin
    AddPreset(dmy);
  end;
end;

procedure TfrmMain.btnDeleteClick(Sender: TObject);
begin
  if comboPresets.Properties.Items.Count = 1 then
  begin
    MessageDlg('Thet last preset can''t be deleted. Please create a new preset if you want to delete this.', mtError, [mbOK], 0);
  end
  else
  begin
    if MessageDlg('Really delete preset "' + comboPresets.Text + '"?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
    begin
      DeletePreset(comboPresets.ItemIndex);
      FProjectConfig.WriteString('Presets', 'Presets', comboPresets.Properties.Items.Text);
    end;
  end;
end;

procedure TfrmMain.btnEditClick(Sender: TObject);
var
  dmy: string;
  idx: Integer;
begin
  SavePreset;
  dmy := comboPresets.Text;
  idx := comboPresets.ItemIndex;
  if InputQuery('Edit Preset', 'Rename preset:', dmy) then
  begin
    comboPresets.Properties.Items[comboPresets.ItemIndex] := dmy;
    comboPresets.ItemIndex := idx;

    FProjectConfig.WriteString('Presets', 'Presets', comboPresets.Properties.Items.Text);
  end;
end;

procedure TfrmMain.btnCopyFullURLClick(Sender: TObject);
begin
  Clipboard.SetTextBuf(PChar(edtFullURL.Text));
end;

procedure TfrmMain.btnCopyPresetClick(Sender: TObject);
var
  idx: Integer;
begin
  SavePreset;
  idx := comboPresets.ItemIndex;
  AddPreset('Copy of ' + comboPresets.Text);
  FProjectConfig.CopySection('Preset_' + idx.ToString, 'Preset_' + comboPresets.ItemIndex.ToString);
  LoadPreset;
end;

procedure TfrmMain.btnRunClick(Sender: TObject);
begin
  Run;
end;

procedure TfrmMain.btnSaveAsClick(Sender: TObject);
begin
  SaveProjectAs(True);
end;

procedure TfrmMain.btnSaveClick(Sender: TObject);
begin
  SaveProject;
end;

procedure TfrmMain.comboMethodPropertiesChange(Sender: TObject);
begin
  tabPostData.TabVisible := comboMethod.ItemIndex = 1;

  if (comboMethod.ItemIndex = 0) and (PageControlParams.ActivePage = nil) then
  begin
    PageControlParams.ActivePage := tabURLParams;
  end;
end;

procedure TfrmMain.comboPresetsPropertiesChange(Sender: TObject);
begin
  if FActivePreset <> '' then
  begin
    SavePreset;
  end;

  FActivePreset := comboPresets.Text;
  LoadPreset;
end;

procedure TfrmMain.pmnuCompactJsonClick(Sender: TObject);
var
  ja: TJsonArray;
  jo: TJsonObject;
begin
  if FActiveEditor.Lines.Count > 0 then
  begin
    if Length(FActiveEditor.Lines[0]) > 0 then
    begin
      if FActiveEditor.Lines[0][1] = '{' then
      begin
        jo := TJsonObject.Create;
        try
          try
            jo.FromJSON(FActiveEditor.Lines.Text);
          except
            on E: Exception do
            begin
              MessageDlg(E.Message, mtError, [mbOK], 0);
            end;
          end;
          FActiveEditor.Lines.Clear;
          FActiveEditor.Lines.Text := jo.ToJSON(True);
        finally
          jo.Free;
        end;
      end
      else
      if FActiveEditor.Lines[0][1] = '[' then
      begin
        ja := TJsonArray.Create;
        try
          try
            ja.FromJSON(FActiveEditor.Lines.Text);
          except
            on E: Exception do
            begin
              MessageDlg(E.Message, mtError, [mbOK], 0);
            end;
          end;
          FActiveEditor.Lines.Clear;
          FActiveEditor.Lines.Text := ja.ToJSON(True);
        finally
          ja.Free;
        end;
      end;
    end;
  end;
end;

procedure TfrmMain.pmnuCopyJsonContentClick(Sender: TObject);
begin
  FActiveEditor.CopyToClipboard;
end;

procedure TfrmMain.pmnuCutJsonContentClick(Sender: TObject);
begin
  FActiveEditor.CutToClipboard;
end;

procedure TfrmMain.DeletePreset(const AID: Integer);
begin
  FActivePreset := '';
  comboPresets.Properties.Items.Delete(AID);
  FProjectConfig.DeleteSection('Preset_' + AID.ToString);
  comboPresets.ItemIndex := comboPresets.Properties.Items.Count - 1;
end;

procedure TfrmMain.dxBarButton6Click(Sender: TObject);
begin
  Close;
end;

procedure TfrmMain.dxBarButton7Click(Sender: TObject);
begin
  MessageDlg('Simnet REST Debugger' + #13#10 + GetAppVersion, mtInformation, [mbOK], 0);
end;

procedure TfrmMain.dxBarSubItem3Popup(Sender: TObject);
begin
  btnSave.Enabled := not FIsTempProjectLoaded;
end;

procedure TfrmMain.EditorContentMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbRight then
  begin
    FActiveEditor := EditorContent;
    PopupJsonContent.PopupFromCursorPos;
  end;
end;

procedure TfrmMain.EditorExtraHeadersMouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbRight then
  begin
    FActiveEditor := EditorExtraHeaders;
    PopupJsonContent.PopupFromCursorPos;
  end;
end;

procedure TfrmMain.EditorResponseHeadersMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbRight then
  begin
    FActiveEditor := EditorResponseHeaders;
    PopupJsonContent.PopupFromCursorPos;
  end;
end;

procedure TfrmMain.EditorPostDataMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbRight then
  begin
    FActiveEditor := EditorPostData;
    PopupJsonContent.PopupFromCursorPos;
  end;
end;

procedure TfrmMain.EditorURLParamsChange(Sender: TObject);
begin
  UpdateFullURL;
end;

procedure TfrmMain.EditorURLParamsMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbRight then
  begin
    FActiveEditor := EditorURLParams;
    PopupJsonContent.PopupFromCursorPos;
  end;
end;

procedure TfrmMain.edtBaseURLPropertiesChange(Sender: TObject);
begin
  UpdateFullURL;
end;

procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  FMainConfig.WriteInteger('MainWindowPos', 'Left', Left);
  FMainConfig.WriteInteger('MainWindowPos', 'Top', Top);
  FMainConfig.WriteInteger('MainWindowPos', 'Width', Width);
  FMainConfig.WriteInteger('MainWindowPos', 'Height', Height);
  FMainConfig.WriteBool('MainWindowPos', 'StayOnTop', FormStyle = fsStayOnTop);

  SavePreset;

  if FProjectConfig.FileModified then
  begin
    if (tbbtnAutoSaveProject.Down) and (not FIsTempProjectLoaded) then
    begin
      SaveProject;
    end
    else
    begin
      if MessageDlg('The project file has changed. Do you want to save it?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
      begin
        if FIsTempProjectLoaded then
        begin
          SaveProjectAs(False);
        end
        else
        begin
          SaveProject;
        end;
      end;
    end;
  end;

  if FileExists(FTempProjectFile) then
  begin
    DeleteFile(FTempProjectFile);
  end;
end;

procedure TfrmMain.FormCreate(Sender: TObject);
begin
  FRoamingSavePath := IncludeTrailingPathDelimiter(TPath.GetHomePath) + 'Simnet\RESTDebugger\';
  ForceDirectories(FRoamingSavePath);

  FMainConfig := TJSONConfig.Create(FRoamingSavePath + 'AppConfig.json');
  FActiveProject := '';
  FActivePreset := '';

  EditorContent.Highlighter.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'Highlighters\JSON.json');
  EditorContent.Highlighter.Colors.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'Colors\Blue.json');

  EditorResponseHeaders.Highlighter.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'Highlighters\Headers.json');
  EditorResponseHeaders.Highlighter.Colors.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'Colors\Blue.json');

  EditorURLParams.Highlighter.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'Highlighters\Params.json');
  EditorURLParams.Highlighter.Colors.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'Colors\Blue.json');

  EditorExtraHeaders.Highlighter.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'Highlighters\Params.json');
  EditorExtraHeaders.Highlighter.Colors.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'Colors\Blue.json');

  EditorPostData.Highlighter.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'Highlighters\JSON.json');
  EditorPostData.Highlighter.Colors.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'Colors\Blue.json');

  FActiveEditor := nil;

  HTTP.Request.UserAgent := 'Simnet REST Debugger/' + GetAppVersion;

  if (not FMainConfig.ReadBool('Main', 'FirstStartShown')) and (FileExists(IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0))) + 'Example Config.rdproj')) then
  begin
    FActiveProject := IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0))) + 'Example Config.rdproj';
    FIsTempProjectLoaded := False;
    FMainConfig.WriteBool('Main', 'FirstStartShown', True);
    FMainConfig.WriteString('Project', 'LastUsed', FActiveProject);
  end
  else
  begin
    if ParamStr(1) <> '' then
    begin
      FActiveProject := ParamStr(1);
      FIsTempProjectLoaded := False;
    end
    else
    begin
      if (FMainConfig.ReadBool('Project', 'AutoOpenLastUsed', True)) and (FileExists(FMainConfig.ReadString('Project', 'LastUsed'))) then
      begin
        FActiveProject := FMainConfig.ReadString('Project', 'LastUsed');
        FIsTempProjectLoaded := False;
      end
      else
      begin
        FActiveProject := GetTempProjectFile;
        FIsTempProjectLoaded := True;
      end;
    end;
  end;

  LoadProject;
end;

procedure TfrmMain.FormDestroy(Sender: TObject);
begin
  FProjectConfig.Free;
  FMainConfig.Free;
end;

procedure TfrmMain.FormResize(Sender: TObject);
begin
  Image.Left := Width - 70;
end;

procedure TfrmMain.FormShow(Sender: TObject);
begin
  dxStatusBar.Panels[0].Text := 'Copyright © by Simnet ' + FormatDateTime('yyyy', now);

  Left := FMainConfig.ReadInteger('MainWindowPos', 'Left', Round((Screen.Width - Width) div 2));
  Top := FMainConfig.ReadInteger('MainWindowPos', 'Top', Round((Screen.Height - Height) div 2));
  Width := FMainConfig.ReadInteger('MainWindowPos', 'Width', 720);
  Height := FMainConfig.ReadInteger('MainWindowPos', 'Height', 930);
  if FMainConfig.ReadBool('MainWindowPos', 'StayOnTop') then
  begin
    FormStyle := fsStayOnTop;
    tbbtnStayOnTop.Down := True;
  end
  else
  begin
    FormStyle := fsNormal;
    tbbtnStayOnTop.Down := False;
  end;

  tbbtnAutoOpenLastUsed.Down := FMainConfig.ReadBool('Project', 'AutoOpenLastUsed', True);
  tbbtnAutoSaveProject.Down := FMainConfig.ReadBool('Project', 'AutoSaveProject', True);

  EditorContent.Lines.Clear;
  EditorResponseHeaders.Lines.Clear;

  dxStatusBar.Panels[1].Text := '';
end;

function TfrmMain.GetAppVersion: string;
var
  VerInfoSize: DWORD;
  VerInfo: Pointer;
  VerValueSize: DWORD;
  VerValue: PVSFixedFileInfo;
  dmy: DWORD;
begin
  Result := '';
  VerInfoSize := GetFileVersionInfoSize(PChar(ParamStr(0)), dmy);
  if VerInfoSize = 0 then Exit;

  GetMem(VerInfo, VerInfoSize);
  try
    GetFileVersionInfo(PChar(ParamStr(0)), 0, VerInfoSize, VerInfo);
    VerQueryValue(VerInfo, '\', Pointer(VerValue), VerValueSize);
    with VerValue^ do
    begin
      Result := IntToStr(dwFileVersionMS shr 16);
      Result := Result + '.' + IntToStr(dwFileVersionMS and $FFFF);
      Result := Result + '.' + IntToStr(dwFileVersionLS shr 16);
      Result := Result + '.' + IntToStr(dwFileVersionLS and $FFFF);
    end;
  finally
    FreeMem(VerInfo, VerInfoSize);
  end;
end;

function TfrmMain.GetTempProjectFile: string;
var
  iSize: Integer;
  guid: TGUID;
begin
  iSize := Winapi.Windows.GetTempPath(0, nil);
  SetLength(Result, iSize);
  iSize := Winapi.Windows.GetTempPath(iSize, @Result[1]);
  SetLength(Result, iSize);
  Result := IncludeTrailingPathDelimiter(Result);
  CreateGUID(guid);

  Result := Result + guid.ToString + '.rdproj';
  FTempProjectFile := Result;
end;

procedure TfrmMain.LoadPreset;
var
  preset: string;
  idx: Integer;
begin
  idx := comboPresets.Properties.Items.IndexOf(FActivePreset);
  if idx > -1 then
  begin
    preset := 'Preset_' + idx.ToString;
    edtBaseURL.Text := FProjectConfig.ReadString(preset, 'BaseURL');
    comboMethod.ItemIndex := FProjectConfig.ReadInteger(preset, 'Method', 0);
    EditorURLParams.Lines.Text := FProjectConfig.ReadString(preset, 'URLParams');
    EditorExtraHeaders.Lines.Text := FProjectConfig.ReadString(preset, 'ExtraHeaders');
    EditorPostData.Lines.Text := FProjectConfig.ReadString(preset, 'PostData');
    comboPostContentType.Text := FProjectConfig.ReadString(preset, 'PostDataContentType', 'application/json');

    chkUseBasicAuth.Checked := FProjectConfig.ReadBool(preset, 'UseBasicAuth');
    edtAuthUsername.Text := TEncryptStr.DecryptString(FProjectConfig.ReadString(preset, 'AuthUsername'));
    edtAuthPassword.Text := TEncryptStr.DecryptString(FProjectConfig.ReadString(preset, 'AuthPassword'));

    UpdateFullURL;
  end;
end;

procedure TfrmMain.LoadProject;
begin
  if FIsTempProjectLoaded then
  begin
    dxStatusBar.Panels[2].Text := 'Default project loaded.';
  end
  else
  begin
    dxStatusBar.Panels[2].Text := 'Loaded project: ' + FActiveProject;
    FMainConfig.WriteString('Project', 'LastUsed', FActiveProject);
  end;

  if Assigned(FProjectConfig) then
  begin
    FProjectConfig.Free;
  end;

  FProjectConfig := TJSONConfig.Create(FActiveProject, False);
  FProjectConfig.AutoSave := False;
  comboPresets.Properties.Items.Text := FProjectConfig.ReadString('Presets', 'Presets');
  if comboPresets.Properties.Items.Count = 0 then
  begin
    // Add default
    AddPreset('Default');
    FProjectConfig.Save;
  end
  else
  begin
    comboPresets.ItemIndex := FProjectConfig.ReadInteger('Presets', 'LastUsed', 0);
  end;
end;

procedure TfrmMain.pmnuPasteJsonContentClick(Sender: TObject);
begin
  FActiveEditor.PasteFromClipboard;
end;

procedure TfrmMain.pmnuRedoJsonContentClick(Sender: TObject);
begin
  FActiveEditor.DoRedo;
end;

procedure TfrmMain.pmnuSelectAllClick(Sender: TObject);
begin
  FActiveEditor.SelectAll;
end;

procedure TfrmMain.pmnuUndoJsonContentClick(Sender: TObject);
begin
  FActiveEditor.DoUndo;
end;

procedure TfrmMain.PopupJsonContentPopup(Sender: TObject);
begin
  pmnuPasteJsonContent.Enabled := FActiveEditor.CanPaste;
  pmnuUndoJsonContent.Enabled := FActiveEditor.CanUndo;
  pmnuRedoJsonContent.Enabled := FActiveEditor.CanRedo;
  if FActiveEditor.Highlighter.Name = 'JSON' then
  begin
    pmnuFormatCodeJsonContent.Visible := ivAlways;
    pmnuCompactJson.Visible := ivAlways;
  end
  else
  begin
    pmnuFormatCodeJsonContent.Visible := ivNever;
    pmnuCompactJson.Visible := ivNever
  end;
end;

procedure TfrmMain.pmnuFormatCodeJsonContentClick(Sender: TObject);
var
  ja: TJsonArray;
  jo: TJsonObject;
begin
  if FActiveEditor.Lines.Count > 0 then
  begin
    if Length(FActiveEditor.Lines[0]) > 0 then
    begin
      if FActiveEditor.Lines[0][1] = '{' then
      begin
        jo := TJsonObject.Create;
        try
          try
            jo.FromJSON(FActiveEditor.Lines.Text);
          except
            on E: Exception do
            begin
              MessageDlg(E.Message, mtError, [mbOK], 0);
            end;
          end;
          FActiveEditor.Lines.Clear;
          FActiveEditor.Lines.Text := jo.ToJSON(False);
        finally
          jo.Free;
        end;
      end
      else
      if FActiveEditor.Lines[0][1] = '[' then
      begin
        ja := TJsonArray.Create;
        try
          try
            ja.FromJSON(FActiveEditor.Lines.Text);
          except
            on E: Exception do
            begin
              MessageDlg(E.Message, mtError, [mbOK], 0);
            end;
          end;
          FActiveEditor.Lines.Clear;
          FActiveEditor.Lines.Text := ja.ToJSON(False);
        finally
          ja.Free;
        end;
      end;
    end;
  end;
end;

procedure TfrmMain.Run;
var
  dmy: string;
  tick: Cardinal;
  strstream: TStringStream;
begin
  EditorContent.Lines.Clear;
  EditorResponseHeaders.Lines.Clear;

  Screen.Cursor := crHourGlass;
  tick := GetTickCount;
  try
    HTTP.Request.CustomHeaders.Clear;
    HTTP.Request.CustomHeaders.AddStrings(EditorExtraHeaders.Lines);

    if chkUseBasicAuth.Checked then
    begin
      HTTP.Request.Username := edtAuthUsername.Text;
      HTTP.Request.Password := edtAuthPassword.Text;
      HTTP.Request.BasicAuthentication := True;
    end
    else
    begin
      HTTP.Request.BasicAuthentication := False;
      HTTP.Request.Username := '';
      HTTP.Request.Password := '';
    end;

    if comboMethod.ItemIndex = 0 then
    begin
      // GET
      try
        dmy := HTTP.Get(edtFullURL.Text);
        if HTTP.ResponseCode <> 200 then
        begin
          MessageDlg(HTTP.ResponseText, mtError, [mbOK], 0);
        end;
      except
        on E: Exception do
        begin
          MessageDlg(E.Message, mtError, [mbOK], 0);
        end;
      end;
    end
    else
    if comboMethod.ItemIndex = 1 then
    begin
      // POST
      strstream := TStringStream.Create(EditorPostData.Lines.Text, TEncoding.UTF8);
      try
        HTTP.Request.ContentType := comboPostContentType.Text;
        try
          dmy := HTTP.Post(edtFullURL.Text, strstream);
          if HTTP.ResponseCode <> 200 then
          begin
            MessageDlg(HTTP.ResponseText, mtError, [mbOK], 0);
          end;
        except
          on E: Exception do
          begin
            MessageDlg(E.Message, mtError, [mbOK], 0);
          end;
        end;
      finally
        strstream.Free;
      end;
    end;

    EditorContent.Lines.Text := dmy;
    EditorResponseHeaders.Lines.Text := HTTP.Response.RawHeaders.Text;
  finally
    dxStatusBar.Panels[1].Text := FormatFloat('Response Time: 0, ms', GetTickCount - tick);
    Screen.Cursor := crDefault;
  end;
end;

procedure TfrmMain.SavePreset;
var
  preset: string;
  idx: Integer;
begin
  idx := comboPresets.Properties.Items.IndexOf(FActivePreset);
  if idx > -1 then
  begin
    preset := 'Preset_' + idx.ToString;
    FProjectConfig.WriteString(preset, 'BaseURL', edtBaseURL.Text);
    FProjectConfig.WriteInteger(preset, 'Method', comboMethod.ItemIndex);
    FProjectConfig.WriteString(preset, 'URLParams', EditorURLParams.Lines.Text);
    FProjectConfig.WriteString(preset, 'PostData', EditorPostData.Lines.Text);
    FProjectConfig.WriteString(preset, 'ExtraHeaders', EditorExtraHeaders.Lines.Text);
    FProjectConfig.WriteString(preset, 'PostDataContentType', comboPostContentType.Text);

    FProjectConfig.WriteBool(preset, 'UseBasicAuth', chkUseBasicAuth.Checked);
    FProjectConfig.WriteString(preset, 'AuthUsername', TEncryptStr.EncryptString(edtAuthUsername.Text));
    FProjectConfig.WriteString(preset, 'AuthPassword', TEncryptStr.EncryptString(edtAuthPassword.Text));

    FProjectConfig.WriteInteger('Presets', 'LastUsed', idx);
  end;
end;

procedure TfrmMain.SaveProject;
begin
  SavePreset;
  FProjectConfig.Save;
end;

procedure TfrmMain.SaveProjectAs(const LoadAfterSave: Boolean);
var
  fname: string;
begin
  SaveDialog.InitialDir := FMainConfig.ReadString('Dialogs', 'LastSaveDir', ExtractFilePath(ParamStr(0)));
  if SaveDialog.Execute then
  begin
    fname := SaveDialog.FileName;
    FMainConfig.WriteString('Dialogs', 'LastSaveDir', ExtractFilePath(fname));

    fname := ChangeFileExt(fname, '');
    fname := fname + '.rdproj';

    SaveProject;
    CopyFile(PChar(FProjectConfig.FileName), PChar(fname), False);

    if LoadAfterSave then
    begin
      FIsTempProjectLoaded := False;
      FActiveProject := fname;
      LoadProject;
    end;
  end;
end;

end.
