unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ComCtrls,
  StdCtrls, Buttons, ExtCtrls, Spin, ClipBrd, ShlepsUtils, Config,
  blcksock, synsock, synautil, synamisc, Unit2;

type

  { TForm1 }

  TForm1 = class(TForm)
    AddChildNodeSpeedButton: TSpeedButton;
    AddSiblingNodeSpeedButton: TSpeedButton;
    CollapseSpeedButton: TSpeedButton;
    MoveUpSpeedButton: TSpeedButton;
    MoveDnSpeedButton: TSpeedButton;
    MoveLevelUpSpeedButton: TSpeedButton;
    SettingsSpeedButton: TSpeedButton;
    TimeEdit: TEdit;
    TimeLabel: TLabel;
    PositionLabel: TLabel;
    PositionEdit: TEdit;
    DelNodeSpeedButton: TSpeedButton;
    CommandEdit: TEdit;
    EditModeSpeedButton: TSpeedButton;
    ExpandSpeedButton: TSpeedButton;
    CommentMemo: TMemo;
    EditPanel: TPanel;
    FilePanel: TPanel;
    CommandLabel: TLabel;
    NewProjectSpeedButton: TSpeedButton;
    OpenProjectSpeedButton: TSpeedButton;
    KeepCollapsedSpeedButton: TSpeedButton;
    IndentSpinEdit: TSpinEdit;
    ItemOptionsPanel: TPanel;
    TreeViewPanel: TPanel;
    ViewModeSpeedButton: TSpeedButton;
    PrevSpeedButton: TSpeedButton;
    PeakViewSpeedButton: TSpeedButton;
    PlayPanel: TPanel;
    BeginSpeedButton: TSpeedButton;
    EndSpeedButton: TSpeedButton;
    ProjectOpenDialog: TOpenDialog;
    Panel1: TPanel;
    Panel2: TPanel;
    ProjectSaveDialog: TSaveDialog;
    NextSpeedButton: TSpeedButton;
    AutoPlaySpeedButton: TSpeedButton;
    SaveProjectSpeedButton: TSpeedButton;
    Splitter1: TSplitter;
    TreeView: TTreeView;
    Procedure AddChildNodeSpeedButtonClick(Sender: TObject);
    Procedure AddSiblingNodeSpeedButtonClick(Sender: TObject);
    Procedure BeginSpeedButtonClick(Sender: TObject);
    Procedure CollapseSpeedButtonClick(Sender: TObject);
    Procedure CommandEditChange(Sender: TObject);
    Procedure DelNodeSpeedButtonClick(Sender: TObject);
    Procedure DownSpeedButtonClick(Sender: TObject);
    Procedure EditModeSpeedButtonClick(Sender: TObject);
    Procedure EndSpeedButtonClick(Sender: TObject);
    Procedure ExpandSpeedButtonClick(Sender: TObject);
    Procedure FormCreate(Sender: TObject);
    Procedure IndentSpinEditChange(Sender: TObject);
    Procedure CommentMemoChange(Sender: TObject);
    Procedure MoveDnSpeedButtonClick(Sender: TObject);
    Procedure MoveLevelUpSpeedButtonClick(Sender: TObject);
    Procedure MoveUpSpeedButtonClick(Sender: TObject);
    Procedure NewProjectSpeedButtonClick(Sender: TObject);
    Procedure NextSpeedButtonClick(Sender: TObject);
    Procedure OpenProjectSpeedButtonClick(Sender: TObject);
    Procedure PositionEditDblClick(Sender: TObject);
    procedure SettingsSpeedButtonClick(Sender: TObject);
    procedure SettingsSpeedButtonMouseMove(Sender: TObject; Shift: TShiftState;
      X, Y: Integer);
    procedure TreeViewSelectionChanged(Sender: TObject);
    Procedure ViewModeSpeedButtonClick(Sender: TObject);
    Procedure PositionEditChange(Sender: TObject);
    Procedure PrevSpeedButtonClick(Sender: TObject);
    Procedure SaveProjectSpeedButtonClick(Sender: TObject);
    Procedure TimeEditChange(Sender: TObject);
//    Procedure TreeViewChange(Sender: TObject; Node: TTreeNode);
    Procedure TreeViewDragOver(Sender, Source: TObject; X, Y: Integer;
      State: TDragState; Var Accept: Boolean);
    Procedure TreeViewEndDrag(Sender, Target: TObject; X, Y: Integer);
    Procedure UPSpeedButtonClick(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;

Const  TitleString='ChaosWarehouse narrator 20210811';

var
  Form1: TForm1;
  BaseProjectName:String;
  ProjectPath:String;
  NodeList:TStringList;
  TimeList:TStringList;
  CommandList:TStringList;
  PositionList:TStringList;
  NodeNumerator:Longint;
  NavigatingFlag:Boolean; //used to avoid write to file while changing item
  TCPSocket:TTCPBlockSocket;


implementation

{$R *.lfm}

{ TForm1 }

Procedure TForm1.DelNodeSpeedButtonClick(Sender: TObject);
Var Node:TTreeNode;
    IDs,FileName:String;
begin
  Node:=TreeView.Selected;
  IDs:=IntToStr(Longint(Node.Data));
  FileName:=ProjectPath+BaseProjectName+'-'+IDs+'.txt';
  TreeView.Items.Delete(Node);
  If FileExists(FileName) Then
     DeleteFile(FileName);
end;

Procedure TForm1.DownSpeedButtonClick(Sender: TObject);
Var Node,NewNode:TTreeNode;
    I,Index:Longint;
Begin
  Node:=TreeView.Selected;
  For I:=0 To TreeView.Items.Count-1 Do
    If TreeView.Items.Item[I]=Node Then Begin Index:=I; Break; End;
  If Index<TreeView.Items.Count-1 Then Begin
    NewNode:=TreeView.Items.Item[Index+1];
    TreeView.Selected:=NewNode;
  End;
end;

Procedure TForm1.EditModeSpeedButtonClick(Sender: TObject);
Begin
  PlayPanel.Hide;
  EditPanel.Show;
  EditPanel.Align:=alClient;
  TreeView.ReadOnly:=False;
  TreeView.DragMode:=dmAutomatic;
  CommentMemo.ReadOnly:=False;
  ItemOptionsPanel.Show;
end;

Procedure TForm1.EndSpeedButtonClick(Sender: TObject);
Begin
  TreeView.MoveEnd(True);
end;

Procedure TForm1.ExpandSpeedButtonClick(Sender: TObject);
Begin
  TreeView.FullExpand;
end;

Procedure TForm1.FormCreate(Sender: TObject);
Begin
  Form1.Caption:=TitleString;
  NodeList:=TStringList.Create;
  TimeList:=TStringList.Create;
  CommandList:=TStringList.Create;
  PositionList:=TStringList.Create;
  Config.LoadConfigFile;
  ViewModeSpeedButtonClick(Nil);
end;

Procedure TForm1.IndentSpinEditChange(Sender: TObject);
Begin
  TreeView.Indent:=IndentSpinEdit.Value;
end;

Procedure TForm1.CommentMemoChange(Sender: TObject);
Var I:Longint;Node:TTreeNode;
    FileName,IDs:String;
Begin
  If NavigatingFlag Then Exit;
  Node:=TreeView.Selected;
  IDs:=IntToStr(Longint(Node.Data));
  FileName:=OSPath(ProjectPath+'/'+BaseProjectName+'-'+IDs+'.txt');
  If CommentMemo.Text<>'' Then
    CommentMemo.Lines.SaveToFile(FileName)
  Else DeleteFile(FileName);
end;

Procedure TForm1.MoveDnSpeedButtonClick(Sender: TObject);
Var Node,Node1:TTreeNode;
Begin
  Node:=TreeView.Selected;
  Node1:=Node.GetNextSibling;
  If Node1<> Nil Then Node.MoveTo(Node1,naInsertBehind);
end;

Procedure TForm1.MoveLevelUpSpeedButtonClick(Sender: TObject);
Var Node,Node1:TTreeNode;
Begin
  Node:=TreeView.Selected;
  Node1:=Node.GetParentNodeOfAbsoluteLevel(0);
  If Node1<>Nil Then Node.MoveTo(Node1,naInsertBehind);
end;

Procedure TForm1.MoveUpSpeedButtonClick(Sender: TObject);
Var Node,Node1:TTreeNode;
Begin
  Node:=TreeView.Selected;
  Node1:=Node.GetPrevSibling;
  If Node1<> Nil Then Node.MoveTo(Node1,naInsert);
end;

Procedure TForm1.NewProjectSpeedButtonClick(Sender: TObject);
Begin
  NodeList.Clear;
  TimeList.Clear;
  CommandList.Clear;
  PositionList.Clear;
  TreeView.Items.Clear;
  BaseProjectName:='';
  CommentMemo.Lines.Clear;
  PositionEdit.Text:='';
  CommandEdit.Text:='';
  TimeEdit.Text:='';
end;

Procedure TForm1.NextSpeedButtonClick(Sender: TObject);
Var Node,NewNode:TTreeNode;
    I,Index:Longint;
Begin
  If KeepCollapsedSpeedButton.Down Then TreeView.FullCollapse;
  Node:=TreeView.Selected;
  For I:=0 To TreeView.Items.Count-1 Do
    If TreeView.Items.Item[I]=Node Then Begin Index:=I; Break; End;
  If Index<TreeView.Items.Count-1 Then Begin
    NewNode:=TreeView.Items.Item[Index+1];
    TreeView.Selected:=NewNode;
  End;
end;

Procedure TForm1.ViewModeSpeedButtonClick(Sender: TObject);
Begin
  PlayPanel.Align:=alClient;
  PlayPanel.Show;
  EditPanel.Hide;
  TreeView.ReadOnly:=True;
  TreeView.DragMode:=dmManual;
  CommentMemo.ReadOnly:=True;
  ItemOptionsPanel.Hide;
end;

Procedure TForm1.PositionEditChange(Sender: TObject);
Var Node:TTreeNode;
    IDs:String;Index:Longint;
Begin
  If NavigatingFlag Then Exit;
  Node:=TreeView.Selected;
  IDs:=IntToStr(Longint(Node.Data));
  Index:=PositionList.IndexOfName(IDs);
  If Index<0 Then Begin
    //не было - добавляем
    PositionList.Add(IDs+'='+PositionEdit.Text);
  End Else Begin
    If PositionEdit.Text='' Then begin
      //очистили - удаляем
      PositionList.Delete(Index);
    End Else Begin
      //изменили - сохраняем
      PositionList[Index]:=(IDs+'='+PositionEdit.Text);
    End;
  End;
end;

Procedure TForm1.PrevSpeedButtonClick(Sender: TObject);
Var Node,NewNode:TTreeNode;
    I,Index:Longint;
Begin
  If KeepCollapsedSpeedButton.Down Then TreeView.FullCollapse;
  Node:=TreeView.Selected;
  For I:=0 To TreeView.Items.Count-1 Do
    If TreeView.Items.Item[I]=Node Then Begin Index:=I; Break; End;
  If Index>0 Then Begin
    NewNode:=TreeView.Items.Item[Index-1];
    TreeView.Selected:=NewNode;
  End;
end;

Procedure TForm1.OpenProjectSpeedButtonClick(Sender: TObject);
Var Node,PNode:TTreeNode;
    I,J,Level:Longint;
    IDS,LevelS,TimeS,CommandS,PositionS,Capt:String;
Begin
  If ProjectOpenDialog.Execute Then Begin
    NewProjectSpeedButtonClick(Sender);
    ProjectPath:=ExtractFilePath(ProjectOpenDialog.FileName);
    BaseProjectName:=ExtractFileName(ProjectOpenDialog.FileName);

    Try
      NodeList.LoadFromFile(OSPath(ProjectPath+'/'+BaseProjectName));
    Except
    End;
{    Try

      TextList.LoadFromFile(BaseProjectName);
    Except
    End;}
    NodeNumerator:=StrToInt(GetValByName(NodeList[0],'NodeNumerator'));
    If NodeList.Count>0 Then
      For I:=1 To NodeList.Count-1 Do Begin
        IDS:=GetValByName(NodeList[I],'id');
        LevelS:=GetValByName(NodeList[I],'level');Level:=StrToInt(LevelS);
        TimeS:=GetValByName(NodeList[I],'time');
        If TimeS<>'' Then TimeList.Add(IDs+'='+TimeS);
        CommandS:=GetValByName(NodeList[I],'command');
        If CommandS<>'' Then CommandList.Add(IDs+'='+CommandS);
        PositionS:=GetValByName(NodeList[I],'position');
        If PositionS<>'' Then PositionList.Add(IDs+'='+PositionS);
        Capt:=GetValByName(NodeList[I],'caption');
        If Level=0 Then Node:=TreeView.Items.AddChild(Nil,Capt);
        If (Level<>0) And (TreeView.Items.Count>0) Then Begin
          For J:=TreeView.Items.Count-1 DownTo 0 Do Begin
            PNode:=TreeView.Items[J];
            If PNode.Level=Level-1 Then Begin
              Node:=TreeView.Items.AddChild(PNode,Capt);

              break;
            End;
          End;
        End;
        Node.Data:=Pointer(StrToInt(IDS));

      End;
    If KeepCollapsedSpeedButton.Down Then TreeView.FullCollapse;
    TreeView.MoveHome(True);
    Form1.Caption:=TitleString+' '+OSPath(ProjectPath+'/'+BaseProjectName);
  End;
end;

Procedure TForm1.PositionEditDblClick(Sender: TObject);
Var S,Head,Tail:String;
Begin
     ClipBoard.Clear;
     TCPSocket:=TTCPBlockSocket.Create;
     TCPSocket.Connect('127.0.0.1','33333');
     TCPSocket.SendString('self::CAD_PeekViewPosition'+#10);
     S:=TCPSocket.RecvString(500);
     TCPSocket.CloseSocket;
     TCPSocket.Free;
     Sleep(500);
     Application.ProcessMessages;
     S:=ClipBoard.AsText;
     Head:=ExtractFilePath(S);
     Tail:=Copy(S,Length(Head)+1,Length(S)-Length(Head)-1);
     If Pos('file://',Head)>0 Then PositionEdit.Text:='file://'+Tail
     Else PositionEdit.Text:=Tail;
end;

procedure TForm1.SettingsSpeedButtonClick(Sender: TObject);
begin
  SettingsForm.Show;
end;

procedure TForm1.SettingsSpeedButtonMouseMove(Sender: TObject;
  Shift: TShiftState; X, Y: Integer);
begin

end;

procedure TForm1.TreeViewSelectionChanged(Sender: TObject);
Var I,ErrCode:Longint;
    FileName,IDs:String;
    Node:TTreeNode;
Begin
  Node:=TreeView.Selected;
  If Node=Nil Then Exit; //cleaned
  NavigatingFlag:=True;
  IDs:=IntToStr(Longint(Node.Data));
  FileName:=ProjectPath+BaseProjectName+'-'+IDs+'.txt';
  If FileExists(FileName) Then
     CommentMemo.Lines.LoadFromFile(FileName)
  Else CommentMemo.Clear;
  If TimeList.IndexOfName(IDs)>=0 Then Begin
    TimeEdit.Text:=TimeList.Values[IDs];
  End Else TimeEdit.Text:='';
  If CommandList.IndexOfName(IDs)>=0 Then Begin
    CommandEdit.Text:=CommandList.Values[IDs];
  End Else CommandEdit.Text:='';
  If PositionList.IndexOfName(IDs)>=0 Then Begin
    PositionEdit.Text:=PositionList.Values[IDs];
  End Else PositionEdit.Text:='';
  If ViewModeSpeedButton.Down Then Begin
    //в режиме проигрывания не просто обновить интерфейс, но и выпольнять внешние команды и смену позиции сцены
    If PositionEdit.Text<>'' Then Begin
      TCPSocket:=TTCPBlockSocket.Create;
      TCPSocket.Connect(Config.ConfigFile.Values['PencilIP'],Config.ConfigFile.Values['PencilPort']);
      TCPSocket.SendString('self::CAD_Fly:10:'+PositionEdit.Text+#10);
      TCPSocket.CloseSocket;
      TCPSocket.Free;
    End;
    If CommandEdit.Text<>'' Then Begin
      GetResponce(CommandEdit.Text,ErrCode);
    End;
  End;
  NavigatingFlag:=False;
end;


Procedure TForm1.SaveProjectSpeedButtonClick(Sender: TObject);
Var I,Index:Longint;
    Node:TTreeNode;
    StrBuf,IDs,TimeS,CommandS,PositionS:String;
Begin
  If BaseProjectName='' then Begin
    If ProjectSaveDialog.Execute Then Begin
      BaseProjectName:=ProjectSaveDialog.FileName;
    End;
  end;
  NodeList.Clear;
  NodeList.Add('NodeNumerator='+IntToStr(NodeNumerator));
  If TreeView.Items.Count>0 Then
    For I:=0 To TreeView.Items.Count-1 Do Begin
      Node:=TreeView.Items[I];
      IDs:=IntToStr(Longint(Node.Data));
      Index:=TimeList.IndexOfName(IDs);
      If Index>=0 Then TimeS:=TimeList.Values[IDs] Else TimeS:='';
      Index:=PositionList.IndexOfName(IDs);
      If Index>=0 Then PositionS:=PositionList.Values[IDs] Else PositionS:='';
      Index:=CommandList.IndexOfName(IDs);
      If Index>=0 Then CommandS:=CommandList.Values[IDs] Else CommandS:='';

      StrBuf:='id='+IDs+' '+
              'level='+IntToStr(Node.Level)+' ';
      If TimeS<>'' Then StrBuf:=StrBuf+'time='+TimeS+' ';
      If CommandS<>'' Then StrBuf:=StrBuf+'command="'+CommandS+'" ';
      If PositionS<>'' Then StrBuf:=StrBuf+'position="'+PositionS+'" ';
      StrBuf:=StrBuf+'caption="'+(Node.Text)+'"';
      NodeList.Add(StrBuf);
    End;
  Try
    NodeList.SaveToFile(OSPath(ProjectPath+'/'+BaseProjectName));
  Except
  End;
  Form1.Caption:=TitleString+' '+OSPath(ProjectPath+'/'+BaseProjectName);
end;

Procedure TForm1.TimeEditChange(Sender: TObject);
Var Node:TTreeNode;
    IDs:String;Index:Longint;
Begin
  If NavigatingFlag Then Exit;
  Node:=TreeView.Selected;
  IDs:=IntToStr(Longint(Node.Data));
  Index:=TimeList.IndexOfName(IDs);
  If Index<0 Then Begin
    //не было - добавляем
    TimeList.Add(IDs+'='+TimeEdit.Text);
  End Else Begin
    If TimeEdit.Text='' Then begin
      //очистили - удаляем
      TimeList.Delete(Index);
    End Else Begin
      //изменили - сохраняем
      TimeList[Index]:=(IDs+'='+TimeEdit.Text);
    End;
  End;
end;


Procedure TForm1.TreeViewDragOver(Sender, Source: TObject; X, Y: Integer;
  State: TDragState; Var Accept: Boolean);
Var DstNode:TTreeNode;
Begin
  If not ((Sender=TreeView)) Then Begin Accept:=False;Exit;End;
  DstNode:=TreeView.GetNodeAt(X,Y);
  If DstNode<>Nil Then Accept:=True Else Accept:=False;

end;

Procedure TForm1.TreeViewEndDrag(Sender, Target: TObject; X, Y: Integer);
Var SrcNode,DstNode:TTreeNode;
Begin
  If not ((Sender=TreeView)) Then Exit;
  SrcNode:=TreeView.Selected;
  DstNode:=TreeView.GetNodeAt(X,Y);
  If DstNode<>Nil Then Begin
   // TreeView.DropTarget
    SrcNode.MoveTo(DstNode,naAddChild);
  End;
end;

Procedure TForm1.UPSpeedButtonClick(Sender: TObject);
Var Node,NewNode:TTreeNode;
Begin
  Node:=TreeView.Selected;
  TreeView.MoveToNextNode(True);
end;


Procedure TForm1.AddChildNodeSpeedButtonClick(Sender: TObject);
Var Node,NewNode:TTreeNode;
Begin
  Inc(NodeNumerator);
  Node:=TreeView.Selected;
  NewNode:=TreeView.Items.AddChild(Node,'!');
  NewNode.Data:=Pointer(NodeNumerator);
  TreeView.Selected:=NewNode;
  NewNode.EditText;
end;

Procedure TForm1.AddSiblingNodeSpeedButtonClick(Sender: TObject);
Var Node,NewNode:TTreeNode;
Begin
  Inc(NodeNumerator);
  Node:=TreeView.Selected;
  NewNode:=TreeView.Items.InsertBehind(Node,'!');
  TreeView.Selected:=NewNode;
  NewNode.Data:=Pointer(NodeNumerator);
  NewNode.EditText;

end;

Procedure TForm1.BeginSpeedButtonClick(Sender: TObject);
Begin
  If KeepCollapsedSpeedButton.Down Then TreeView.FullCollapse;
  TreeView.MoveHome(True);
end;

Procedure TForm1.CollapseSpeedButtonClick(Sender: TObject);
Begin
  If KeepCollapsedSpeedButton.Down Then TreeView.FullCollapse;
  TreeView.FullCollapse;
end;

Procedure TForm1.CommandEditChange(Sender: TObject);
Var Node:TTreeNode;
    IDs:String;Index:Longint;
Begin
  If NavigatingFlag Then Exit;
  Node:=TreeView.Selected;
  IDs:=IntToStr(Longint(Node.Data));
  Index:=CommandList.IndexOfName(IDs);
  If Index<0 Then Begin
    //не было - добавляем
    CommandList.Add(IDs+'='+CommandEdit.Text);
  End Else Begin
    If CommandEdit.Text='' Then Begin
      //очистили - удаляем
      CommandList.Delete(Index);
    End Else Begin
      //изменили - сохраняем
      CommandList[Index]:=(IDs+'='+CommandEdit.Text);
    End;
  End;
end;


end.

