﻿using GDWeave;
using GDWeave.Godot;
using GDWeave.Godot.Variants;
using GDWeave.Modding;
using Serilog;
using Serilog.Configuration;

namespace BetterFirstPerson;

public class PlayerPatch(BetterFirstPersonMod mod) : IScriptMod
{
    public bool ShouldRun(string path) => path == "res://Scenes/Entities/Player/player.gdc";

    public IEnumerable<Token> Modify(string path, IEnumerable<Token> tokens)
    {
        var waiter = new MultiTokenWaiter([
            t => t is IdentifierToken{Name: "skeleton"},
            t => t.Type is TokenType.Period,
            t => t is IdentifierToken{Name: "set_bone_custom_pose" },
            t => t.Type is TokenType.ParenthesisOpen,
            t => t is ConstantToken{Value: IntVariant{Value:1 } },
            t => t.Type is TokenType.Comma,
            t => t.Type is TokenType.BuiltInType,
            t => t.Type is TokenType.ParenthesisOpen,
            t => t.Type is TokenType.ParenthesisClose,
            t => t.Type is TokenType.ParenthesisClose,
            t => t.Type is TokenType.Newline,
        ], allowPartialMatch: true);

        foreach (var token in tokens)
        {
            // was useful since GDWeave.Test didn't work for me :p
            // mod.logger.Information(token.ToString());
            if (waiter.Check(token))
            {
                yield return new Token(TokenType.Newline, 1);

                // if camera_zoom < 0.5 and not freecamming:
                yield return new Token(TokenType.CfIf);
                yield return new IdentifierToken(TokenType.Identifier, 32, "camera_zoom");
                yield return new Token(TokenType.OpLess);
                yield return new ConstantToken(new RealVariant(0.5));
                yield return new Token(TokenType.OpAnd);
                yield return new Token(TokenType.OpNot);
                yield return new IdentifierToken(TokenType.Identifier, 134, "freecamming");
                yield return new Token(TokenType.Colon);
                yield return new Token(TokenType.Newline, 2);

                // if state != STATES.FISHING and state != STATES.FISHING_STRUGGLE:
                yield return new Token(TokenType.CfIf);
                yield return new IdentifierToken(TokenType.Identifier, 47, "state");
                yield return new Token(TokenType.OpNotEqual);
                yield return new ConstantToken(new IntVariant(1)); // 1 - BUSY
                yield return new Token(TokenType.OpAnd);
                yield return new IdentifierToken(TokenType.Identifier, 47, "state");
                yield return new Token(TokenType.OpNotEqual);
                yield return new ConstantToken(new IntVariant(6)); // 6 - FISHING_CAST
                yield return new Token(TokenType.OpAnd);
                yield return new IdentifierToken(TokenType.Identifier, 47, "state");
                yield return new Token(TokenType.OpNotEqual);
                yield return new ConstantToken(new IntVariant(7)); // 7 - FISHING
                yield return new Token(TokenType.OpAnd);
                yield return new IdentifierToken(TokenType.Identifier, 47, "state");
                yield return new Token(TokenType.OpNotEqual);
                yield return new ConstantToken(new IntVariant(9)); // 9 - FISHING_STRUGGLE
                yield return new Token(TokenType.OpAnd);
                yield return new IdentifierToken(TokenType.Identifier, 47, "state");
                yield return new Token(TokenType.OpNotEqual);
                yield return new ConstantToken(new IntVariant(10)); // 10 - SHOVEL_CAST
                yield return new Token(TokenType.OpAnd);
                yield return new IdentifierToken(TokenType.Identifier, 47, "state");
                yield return new Token(TokenType.OpNotEqual);
                yield return new ConstantToken(new IntVariant(11)); // 11 - SHOVEL_STRUGGLE

                // idk what these are :p

                //yield return new Token(TokenType.OpOr);
                //yield return new IdentifierToken(TokenType.Identifier, 47, "state");
                //yield return new Token(TokenType.OpNotEqual);
                //yield return new ConstantToken(new IntVariant(12)); // 12 - NET_CAST
                //yield return new Token(TokenType.OpOr);
                //yield return new IdentifierToken(TokenType.Identifier, 47, "state");
                //yield return new Token(TokenType.OpNotEqual);
                //yield return new ConstantToken(new IntVariant(13)); // 13 - NET_STRUGGLE

                yield return new Token(TokenType.OpAnd);
                yield return new IdentifierToken(TokenType.Identifier, 47, "state");
                yield return new Token(TokenType.OpNotEqual);
                yield return new ConstantToken(new IntVariant(16)); // 16 - SHOVEL_STRUGGLE

                yield return new Token(TokenType.Colon);
                yield return new Token(TokenType.Newline, 3);

                // self.rotation.y = camera.rotation.y
                yield return new Token(TokenType.Self);
                yield return new Token(TokenType.Period);
                yield return new IdentifierToken("rotation");
                yield return new Token(TokenType.Period);
                yield return new IdentifierToken("y");
                yield return new Token(TokenType.OpAssign);
                yield return new IdentifierToken(TokenType.Identifier, 137, "camera");
                yield return new Token(TokenType.Period);
                yield return new IdentifierToken(TokenType.Identifier, 286, "rotation");
                yield return new Token(TokenType.Period);
                yield return new IdentifierToken(TokenType.Identifier, 288, "y");
                yield return new Token(TokenType.Newline, 3);

                // var back_bend = clamp(camera.rotation.x * 54, -50.0, 50.0)
                yield return new Token(TokenType.PrVar);
                yield return new IdentifierToken("back_bend");
                yield return new Token(TokenType.OpAssign);
                yield return new Token(TokenType.BuiltInFunc, 53);
                yield return new Token(TokenType.ParenthesisOpen);
                yield return new IdentifierToken(TokenType.Identifier, 137, "camera");
                yield return new Token(TokenType.Period);
                yield return new IdentifierToken(TokenType.Identifier, 286, "rotation");
                yield return new Token(TokenType.Period);
                yield return new IdentifierToken(TokenType.Identifier, 288, "x");
                yield return new Token(TokenType.OpMul);
                yield return new ConstantToken(new IntVariant(54));
                yield return new Token(TokenType.Comma);
                yield return new ConstantToken(new RealVariant(-50.0));
                yield return new Token(TokenType.Comma);
                yield return new ConstantToken(new RealVariant(50.0));
                yield return new Token(TokenType.ParenthesisClose);
                yield return new Token(TokenType.Newline, 3);

                // $body / player_body / Armature / Skeleton / BoneAttachment / Spatial.rotation_degrees = Vector3(0.0, camera.rotation.y, back_bend * 0.7)
                yield return new Token(TokenType.Dollar);
                yield return new IdentifierToken(TokenType.Identifier, 142, "body");
                yield return new Token(TokenType.OpDiv);
                yield return new IdentifierToken(TokenType.Identifier, 144, "player_body");
                yield return new Token(TokenType.OpDiv);
                yield return new IdentifierToken(TokenType.Identifier, 145, "Armature");
                yield return new Token(TokenType.OpDiv);
                yield return new IdentifierToken(TokenType.Identifier, 146, "Skeleton");
                yield return new Token(TokenType.OpDiv);
                yield return new IdentifierToken(TokenType.Identifier, 597, "BoneAttachment");
                yield return new Token(TokenType.OpDiv);
                yield return new IdentifierToken(TokenType.Identifier, 598, "Spatial");
                yield return new Token(TokenType.Period);
                yield return new IdentifierToken(TokenType.Identifier, 342, "rotation_degrees");
                yield return new Token(TokenType.OpAssign);
                yield return new Token(TokenType.BuiltInType, 7);
                yield return new Token(TokenType.ParenthesisOpen);
                yield return new ConstantToken(new RealVariant(0.0));
                yield return new Token(TokenType.Comma);
                yield return new ConstantToken(new RealVariant(0.0));
                yield return new Token(TokenType.Comma);
                yield return new IdentifierToken("back_bend");
                yield return new Token(TokenType.OpMul);
                yield return new ConstantToken(new RealVariant(0.7));
                yield return new Token(TokenType.ParenthesisClose);
                yield return new Token(TokenType.Newline, 3);

                // animation_data["back_bend"] = deg2rad( - back_bend)
                yield return new IdentifierToken(TokenType.Identifier, 126, "animation_data");
                yield return new Token(TokenType.BracketOpen);
                yield return new ConstantToken(new StringVariant("back_bend"));
                yield return new Token(TokenType.BracketClose);
                yield return new Token(TokenType.OpAssign);
                yield return new Token(TokenType.BuiltInFunc, 43);
                yield return new Token(TokenType.ParenthesisOpen);
                yield return new Token(TokenType.OpSub);
                yield return new IdentifierToken("back_bend");
                yield return new Token(TokenType.ParenthesisClose);
                yield return new Token(TokenType.Newline, 1);

                if (mod.Config.ResetUpDownOnFreecam)
                {
                    // else:
                    yield return new Token(TokenType.CfElse);
                    yield return new Token(TokenType.Colon);

                    yield return new Token(TokenType.Newline, 2);
                    // if not freecamming:
                    yield return new Token(TokenType.CfIf);
                    yield return new Token(TokenType.OpNot);
                    yield return new IdentifierToken(TokenType.Identifier, 134, "freecamming");
                    yield return new Token(TokenType.Colon);
                    yield return new Token(TokenType.Newline, 3);

                    // if animation_data["back_bend"] != 0.0:
                    yield return new Token(TokenType.CfIf);
                    yield return new IdentifierToken(TokenType.Identifier, 126, "animation_data");
                    yield return new Token(TokenType.BracketOpen);
                    yield return new ConstantToken(new StringVariant("back_bend"));
                    yield return new Token(TokenType.BracketClose);
                    yield return new Token(TokenType.OpNotEqual);
                    yield return new ConstantToken(new RealVariant(0.0));
                    yield return new Token(TokenType.Colon);
                    yield return new Token(TokenType.Newline, 4);

                    // animation_data["back_bend"] = 0.0
                    yield return new IdentifierToken(TokenType.Identifier, 126, "animation_data");
                    yield return new Token(TokenType.BracketOpen);
                    yield return new ConstantToken(new StringVariant("back_bend"));
                    yield return new Token(TokenType.BracketClose);
                    yield return new Token(TokenType.OpAssign);
                    yield return new ConstantToken(new RealVariant(0.0));
                    yield return new Token(TokenType.Newline, 1);
                }
            }
            else
            {
                yield return token;
            }
        }
    }

}