%% Chess Game
%
% Use double click to:
% - select any chess piece
% - move selected piece to a field on the chessboard
%
% There is no rules checking algorithm implemented.
%
% You can undo the last move using the '<<' button.
% Use the 'R' button to toggle automatic board rotation.

%   Copyright 1998-2021 HUMUSOFT s.r.o.

%% Initialize the 3D Foundation object
FW = F3D.World('F3DChess');
FW.initialize;

% Set the viewport properties
VP = FW.Viewports.Main;
VP.Title = mfilename;
Path = fileparts(which(mfilename));
setLogo(VP, fullfile(Path, 'data', 'MatlabLogo.png'));

% Assign user buttons
VP.Button6 = F3D.Type.Button('Symbol', 'R', 'ToolTip', 'Enable automatic rotation', 'Callback', @SwitchAutorotate);
VP.Button8 = F3D.Type.Button('Symbol', '«', 'ToolTip', 'Undo last move', 'Callback', 'loadSnapshot(FW, ''Undo'');');

% Show help
VP.message(help(mfilename));

% Load the chess board
FW.load('f3dex_chessboard.f3d')

% Take a first snapshot for undo
FW.SnapshotParams = {'Physics', 'Location', 'Rotation', 'LinearVelocity', 'AngularVelocity', 'Transparency'};
FW.saveSnapshot('Undo');

% Assign callbacks
Board = FW.Root.Children.Floor;
Board.propagate('OnActivate', @SelectPiece, 'children');
Board.OnActivate = @MovePiece;
FW.UserData.Selected = [];
FW.UserData.Autorotate = false;

function SelectPiece(Actor, ~)
    W = Actor.ParentWorld;
    S = W.UserData.Selected;
    if ~isempty(S)
        if S.Name(1) == 'W'
            S.Color = 1;
        else
            S.Color = 0.1;
        end
    end

    if Actor.Name(1) == 'W'
        Actor.Color = [1 0.5 0];
    else
        Actor.Color = [1 0.1 0.1];
    end
    W.UserData.Selected = Actor;
end

function MovePiece(Actor, Pos)
    W = Actor.ParentWorld;
    S = W.UserData.Selected;
    if ~isempty(S)
        saveSnapshot(W, 'Undo');

        Pos = round(Pos + 0.5) - 0.5;
        NewPos = [Pos(1) -Pos(2) 0.25];

        if contains(S.Name, 'Knight')
            Trajectory = [S.Location + [0 0 2]; NewPos  + [0 0 2]; NewPos];
        else
            Trajectory = NewPos;
        end

        x = char(64 + round(NewPos(1) + 4.5));
        y = char(48 + round(NewPos(2) + 4.5));
        if S.Name(1) == 'W'
            label(W.Viewports.Main, ['White ' S.Name(2:end-1) ' to ' x y]);
        else
            label(W.Viewports.Main, ['Black ' S.Name(2:end-1) ' to ' x y]);
        end

        ToBeFired = W.Root.findBy('Location', NewPos, 'first');
        Fight = ~isempty(ToBeFired) && (S ~= ToBeFired);
        if Fight
            ToBeFired.Gravity = false;
            ToBeFired.Physics = true;
            ToBeFired.Friction = 0;
            interpolate(S, 'Location', Trajectory);
            interpolate(ToBeFired, 'Transparency', 0.5, 'sweep', 0.5);
            interpMotion(S, NewPos + [0 0 0.5], S.Rotation + rand(1, 3) - 0.5, 'hill', 0.5)
        else
            interpolate(S, 'Location', Trajectory);
        end

        label(W.Viewports.Main, '');

        if W.UserData.Autorotate
            interpolate(S.ParentActor, 'Rotation', S.ParentActor.Rotation + [0 0 pi]);
        end

    end
end

function SwitchAutorotate(World, ~)
    World.UserData.Autorotate = ~World.UserData.Autorotate;
    if World.UserData.Autorotate
        World.Viewports.Main.Button6.Symbol = '|R|';
        World.Viewports.Main.Button6.ToolTip = 'Disable automatic rotation';
        World.Viewports.Main.Button6.Color = [0 0 1];
    else
        World.Viewports.Main.Button6.Symbol = 'R';
        World.Viewports.Main.Button6.ToolTip = 'Enable automatic rotation';
        World.Viewports.Main.Button6.Color = [0 0 0];
    end
end