program ClipBdCalc;
//{$mode objfpc}{$H+}
{
クリップボードに計算したい文字列をcopyして　実行すれば
結果がクリップボードに帰ります
プログラムを作る時に、この関数の結果はどうなるのと確認する為に作ったものです
何もクリップボードにないか、引数が何かあればコンソールモードになります。
計算は浮動小数点だけで整数や文字列は扱えません
 label=式 か label:=式で  変数を定義出来ます
 式には四則演算やAND OR 等のビット演算が記述可能 ^はべき乗です
sin ,cos ,tan ,log ,ln ,exp ,ArcCos ,ArcSin,ArcTan2 ,Power
FRAC ,ROUND ,TRUNC  ,coTan ,Sinh ,Cosh ,Tanh ,ArcCosh ,ArcSinh ,ArcTanh
LnXP1 ,Log10 ,Log2 ,ArcTanh ,Hypot ,LogN    が組み込まれtいます
}
uses
  Windows,
  SysUtils,
  uAnsiCharTools in 'uAnsiCharTools.pas',
  CalcAnsiScript in 'CalcAnsiScript.pas',
  uSysCol in 'uSysCol.pas',
  Pas2Javasc in 'Pas2Javasc.pas',
  uScrExt in 'uScrExt.pas';

procedure toClip(const s: ansistring);
var
  size: integer;
  dh: THandle;
  dt: pansichar;
begin
  size := Length(s) + 1;
  OpenClipBoard(0);
  EmptyClipboard;
  try
    dh := GlobalAlloc(GMEM_MOVEABLE + GMEM_DDESHARE, size);
    try
      dt := GlobalLock(dh);
      try
        Move(pansichar(s)^, dt^, size);
        SetClipboardData(CF_TEXT, dh);
      finally
        GlobalUnlock(dh);
      end;
    except
      GlobalFree(dh); //エラー時のみ
    end;
  finally CloseClipboard; end;
end;

procedure clrClip;
begin
  OpenClipBoard(0);
  EmptyClipboard;
  CloseClipboard;
end;

function fromClip: ansistring;
var
  dh: THandle;
  dt: pansichar;
  s: ansistring;
begin
  OpenClipboard(0);
  try
    dH := GetClipboardData(CF_TEXT);
    Result := '';
    if dH = 0 then exit;
    try
      dt := GlobalLock(dH);
      s := dt; //PAnsiChar->string 型変換
        //文字列型はそのまま代入するとポインタだけの複製なので Lock〜Unlockの間にcopyする
      setlength(Result, length(s));
      move(s[1], Result[1], Length(s));
    finally
      GlobalUnlock(dh);
    end;
  finally CloseClipboard; end;
end;

function ConsReadLine(): AnsiString;
var
  rdSize: DWORD;
begin
  SetLength(Result, $4000);
//StdHandleは何度でも Getしてよい
  ReadConsoleA(GetStdHandle(STD_INPUT_HANDLE), @Result[1], length(Result) - 1, rdSize, nil);
  setlength(Result, rdSize);
end;


procedure ConsWtLine(s: string);
var
  rdSize: DWORD;
begin
  //せっかくUTF8にしても s+#13#10 と文字列の加算するだけでUTF8に戻る
  WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), @s[1], length(s), rdSize, nil);
  s := #13#10;
  WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), @s[1], length(s), rdSize, nil);
 //rdSizeは見てない。まあ出力出来なかったらどうしろというのか判らないし
end;


procedure ConsWtCvtLn(s: AnsiString);
begin
  ConsWtLine(cvU(s));
end;

// コンソールモード用のスクリプト
type
  TMyConsCalculate = class(TAnsiCalculate)
  public
    procedure ExecInit; override;
    procedure PutLine(add: string); override;
    procedure MsgOutLn(msg: string); override;
  end;

procedure TMyConsCalculate.ExecInit;
begin
  p := PAnsiChar(MyCmd);
//  SetLength(Labels, 0); ラベルを消さないようにするだけ
  OutMsg := '';
  eMsg := '';
end;

procedure TMyConsCalculate.MsgOutLn(msg: string);
begin
  ConsWtLine(msg);
end;

procedure TMyConsCalculate.PutLine(add: string);
begin
  if add <> '' then ConsWtLine(add);
end;

var
  i, filc: Integer;
  p: PChar;
  s, clp: string;
label
  cons;

begin
  if ParamCount < 1 then //クリップボードモード
  begin
    clp := fromClip();
    p := PChar(clp);
    CAnsiSkip(p, [#9, ' ', #13, #10]); //先頭の空白と改行文字だけなら
    if p^ = #0 then begin
      allocConsole; //何もなければコンソールモード
      ConsWtCvtLn('クリップボードに計算したい文字列をcopyして　実行すれば');
      ConsWtCvtLn('結果がクリップボードに帰ります');
      ConsWtCvtLn('何もクリップボードにないか、引数が存在するファイル名でなければコンソールモードになります。');
      ConsWtCvtLn('計算は浮動小数点だけで整数や文字列は扱えません');
      ConsWtCvtLn(' label=式 か label:=式で  変数を定義出来ます');
      ConsWtCvtLn(' 式には四則演算やAND OR 等のビット演算が記述可能 ^はべき乗です');
      ConsWtCvtLn('sin ,cos ,tan ,log ,ln ,exp ,ArcCos ,ArcSin,ArcTan2 ,Power');
      ConsWtCvtLn('FRAC ,ROUND ,TRUNC  ,coTan ,Sinh ,Cosh ,Tanh ,ArcCosh ,ArcSinh ,ArcTanh');
      ConsWtCvtLn('LnXP1 ,Log10 ,Log2 ,ArcTanh ,Hypot ,LogN    が組み込まれtいます');
      ConsWtCvtLn('# のみの行で 説明追加');
      ConsWtCvtLn('exitで帰りますが窓を閉じるのが早いでしょう');
      goto cons;
    end;
    with TAnsiCalculate.Create(clp) do try
      ExecCmd();
      toClip(OutMsg);
    finally
      free;
    end;
    exit;
  end;

/////////////ファイルモード/////////////////////
  clp := ''; filc := 0;
  for i := 1 to ParamCount do
    if FileExists(ParamStr(i)) then begin
      s := readFileStr(cvU(ParamStr(i)));
      with TAnsiCalculate.Create(s) do try
        if FileExec(ParamStr(i)) then inc(filc) else ExecCmd();
        clp := clp + OutMsg;
      finally
        free;
      end;
    end;
  if clp <> '' then begin
    toClip(clp);
    exit;
  end;
  if filc > 0 then exit;
  allocConsole;
  cons:
/////////////コンソールモード/////////////////////
  with TMyConsCalculate.Create('') do try
    while true do begin
      MyCmd := ConsReadLine(); //readln はFPCでコンソールモードでないとエラーになるので
      if sameText(copy(MyCmd, 1, 4), 'exit') then exit;
      ExecCmd();
      toClip(OutMsg);
    end;
  finally
    free;
  end;
end.

